Add Helper Methods
parent
7d5b1565e2
commit
6e92799c3b
1 changed files with 307 additions and 0 deletions
307
Helper-Methods.md
Normal file
307
Helper-Methods.md
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
## Helper Functions Documentation
|
||||
|
||||
### State Management Helpers
|
||||
|
||||
#### `mark_as_modified()`
|
||||
```python
|
||||
def mark_as_modified(self):
|
||||
"""Mark the document as having unsaved changes"""
|
||||
if not self.has_unsaved_changes:
|
||||
self.has_unsaved_changes = True
|
||||
self.update_title()
|
||||
self.changes_label.config(text="Unsaved changes")
|
||||
```
|
||||
**Purpose**:
|
||||
- Tracks when metadata has been modified but not saved
|
||||
- Prevents duplicate change notifications
|
||||
|
||||
**Behavior**:
|
||||
1. Sets `has_unsaved_changes` flag to True
|
||||
2. Updates window title with asterisk (*)
|
||||
3. Shows "Unsaved changes" indicator in UI
|
||||
4. Only triggers once per modification session
|
||||
|
||||
**Example Usage**:
|
||||
```python
|
||||
# After adding a new field
|
||||
self.metadata_dict["NewKey"] = "Value"
|
||||
self.mark_as_modified()
|
||||
```
|
||||
|
||||
#### `mark_as_saved()`
|
||||
```python
|
||||
def mark_as_saved(self):
|
||||
"""Mark the document as saved"""
|
||||
self.has_unsaved_changes = False
|
||||
self.update_title()
|
||||
self.changes_label.config(text="")
|
||||
```
|
||||
**Purpose**:
|
||||
- Resets the unsaved changes indicator
|
||||
- Updates UI to reflect saved state
|
||||
|
||||
**Behavior**:
|
||||
1. Clears `has_unsaved_changes` flag
|
||||
2. Removes asterisk from window title
|
||||
3. Hides unsaved changes indicator
|
||||
|
||||
**Example Usage**:
|
||||
```python
|
||||
# After successful save operation
|
||||
self.mark_as_saved()
|
||||
self.set_status("Metadata saved successfully", color="green")
|
||||
```
|
||||
|
||||
### Display Formatting Helpers
|
||||
|
||||
#### `format_value_for_display(value)`
|
||||
```python
|
||||
def format_value_for_display(self, value):
|
||||
"""Try to parse as JSON if possible, otherwise return as-is"""
|
||||
try:
|
||||
# Try to parse as JSON
|
||||
parsed = json.loads(value)
|
||||
# If successful, return pretty-printed JSON
|
||||
return json.dumps(parsed, indent=2, ensure_ascii=False)
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
# Not JSON, return original value
|
||||
return value
|
||||
```
|
||||
**Purpose**:
|
||||
- Intelligently formats metadata values for display
|
||||
- Handles both JSON and plain text values
|
||||
|
||||
**Behavior**:
|
||||
1. Attempts to parse value as JSON
|
||||
2. If successful, returns pretty-printed with 2-space indentation
|
||||
3. Falls back to string representation if parsing fails
|
||||
4. Preserves Unicode characters with `ensure_ascii=False`
|
||||
|
||||
**Example Usage**:
|
||||
```python
|
||||
# JSON-formatted metadata
|
||||
json_value = '{"camera":"Canon","iso":100}'
|
||||
formatted = app.format_value_for_display(json_value)
|
||||
# Returns: {"camera": "Canon", "iso": 100}
|
||||
|
||||
# Plain text metadata
|
||||
text_value = "Simple string value"
|
||||
formatted = app.format_value_for_display(text_value)
|
||||
# Returns: Simple string value
|
||||
```
|
||||
|
||||
#### `truncate_value(value, max_length=80)`
|
||||
```python
|
||||
def truncate_value(self, value, max_length=80):
|
||||
"""Truncate long values for tree display"""
|
||||
value_str = str(value)
|
||||
if len(value_str) > max_length:
|
||||
return value_str[:max_length] + "..."
|
||||
return value_str
|
||||
```
|
||||
**Purpose**:
|
||||
- Shortens long values for compact tree view display
|
||||
- Maintains readability while preventing UI overflow
|
||||
|
||||
**Behavior**:
|
||||
1. Converts value to string
|
||||
2. Checks if length exceeds `max_length` (default 80)
|
||||
3. Truncates with ellipsis (...) if too long
|
||||
4. Returns full value if within length limit
|
||||
|
||||
**Parameters**:
|
||||
- `value`: The metadata value to truncate
|
||||
- `max_length` (optional): Maximum length before truncation (default: 80)
|
||||
|
||||
**Example Usage**:
|
||||
```python
|
||||
# Long metadata value
|
||||
long_value = "This is a very long string that exceeds the maximum display length"
|
||||
truncated = app.truncate_value(long_value)
|
||||
# Returns: "This is a very long string that exceeds the maximum displa..."
|
||||
|
||||
# Short metadata value
|
||||
short_value = "Short"
|
||||
truncated = app.truncate_value(short_value)
|
||||
# Returns: "Short" (unchanged)
|
||||
```
|
||||
|
||||
### UI Management Helpers
|
||||
|
||||
#### `set_status(message, duration=3000, color="")`
|
||||
```python
|
||||
def set_status(self, message, duration=3000, color=""):
|
||||
"""Set status bar message that auto-clears after duration (ms)"""
|
||||
# Cancel any existing timer
|
||||
if self.status_timer:
|
||||
self.root.after_cancel(self.status_timer)
|
||||
|
||||
# Set the message
|
||||
self.status_label.config(text=message, foreground=color)
|
||||
|
||||
# Schedule clearing the message
|
||||
if duration > 0:
|
||||
self.status_timer = self.root.after(duration, lambda: self.status_label.config(text="Ready", foreground=""))
|
||||
```
|
||||
**Purpose**:
|
||||
- Provides temporary feedback to users
|
||||
- Automatically clears messages after specified duration
|
||||
|
||||
**Behavior**:
|
||||
1. Cancels any existing status timer
|
||||
2. Updates status label with new message and color
|
||||
3. Schedules automatic clearing after duration (ms)
|
||||
4. Defaults to 3000ms (3 seconds) timeout
|
||||
|
||||
**Parameters**:
|
||||
- `message`: Text to display in status bar
|
||||
- `duration` (optional): Time before auto-clear (ms), 0 for permanent (default: 3000)
|
||||
- `color` (optional): Text color for the message
|
||||
|
||||
**Example Usage**:
|
||||
```python
|
||||
# Temporary success message
|
||||
app.set_status("File loaded successfully", color="green")
|
||||
|
||||
# Permanent error message (no auto-clear)
|
||||
app.set_status("Error: File not found", duration=0, color="red")
|
||||
|
||||
# Custom duration message
|
||||
app.set_status("Processing...", duration=5000) # 5 seconds
|
||||
```
|
||||
|
||||
#### `update_title()`
|
||||
```python
|
||||
def update_title(self):
|
||||
"""Update window title with unsaved indicator"""
|
||||
base_title = f"{APP_NAME} v{APP_VERSION}"
|
||||
if self.current_file:
|
||||
filename = Path(self.current_file).name
|
||||
if self.has_unsaved_changes:
|
||||
self.root.title(f"{base_title} - {filename} *")
|
||||
else:
|
||||
self.root.title(f"{base_title} - {filename}")
|
||||
else:
|
||||
self.root.title(base_title)
|
||||
```
|
||||
**Purpose**:
|
||||
- Maintains consistent window title format
|
||||
- Indicates unsaved changes with asterisk (*)
|
||||
|
||||
**Behavior**:
|
||||
1. Constructs base title with app name and version
|
||||
2. Appends filename when file is loaded
|
||||
3. Adds asterisk (*) for unsaved changes
|
||||
4. Falls back to base title when no file is loaded
|
||||
|
||||
**Example Usage**:
|
||||
```python
|
||||
# When opening a file
|
||||
app.current_file = "example.png"
|
||||
app.update_title()
|
||||
# Window title: "PNG Metadata Editor v1.0.0 - example.png"
|
||||
|
||||
# After making changes
|
||||
app.has_unsaved_changes = True
|
||||
app.update_title()
|
||||
# Window title: "PNG Metadata Editor v1.0.0 - example.png *"
|
||||
```
|
||||
|
||||
### Event Handling Helpers
|
||||
|
||||
#### `on_selection_change(event)`
|
||||
```python
|
||||
def on_selection_change(self, event):
|
||||
"""Update detail view when selection changes"""
|
||||
selected = self.tree.selection()
|
||||
if selected:
|
||||
item = selected[0]
|
||||
key, _ = self.tree.item(item, "values")
|
||||
|
||||
if key in self.metadata_dict:
|
||||
value = self.metadata_dict[key]
|
||||
formatted_value = self.format_value_for_display(value)
|
||||
|
||||
# Update detail text
|
||||
self.detail_text.config(state=tk.NORMAL)
|
||||
self.detail_text.delete(1.0, tk.END)
|
||||
self.detail_text.insert(1.0, formatted_value)
|
||||
self.detail_text.config(state=tk.DISABLED)
|
||||
```
|
||||
**Purpose**:
|
||||
- Synchronizes detail view with tree selection
|
||||
- Handles both JSON and plain text formatting
|
||||
|
||||
**Behavior**:
|
||||
1. Gets currently selected item from tree
|
||||
2. Extracts key from selection values
|
||||
3. Retrieves full value from metadata_dict
|
||||
4. Formats value for display (JSON pretty-printing)
|
||||
5. Updates detail text widget with formatted value
|
||||
6. Disables editing of detail view
|
||||
|
||||
**Parameters**:
|
||||
- `event`: Tkinter event object (automatically passed)
|
||||
|
||||
**Example Usage**:
|
||||
```python
|
||||
# Bound to treeview selection event
|
||||
self.tree.bind("<<TreeviewSelect>>", self.on_selection_change)
|
||||
```
|
||||
|
||||
### Utility Helpers
|
||||
|
||||
#### `show_about()`
|
||||
```python
|
||||
def show_about(self):
|
||||
"""Show application about dialog"""
|
||||
about_text = f"{APP_NAME} v{APP_VERSION}\n\n" \
|
||||
"A graphical tool for viewing and editing metadata in PNG files.\n\n" \
|
||||
"Author: Robert Tusa\n" \
|
||||
"License: MIT"
|
||||
|
||||
messagebox.showinfo(
|
||||
f"About {APP_NAME}",
|
||||
about_text,
|
||||
parent=self.root
|
||||
)
|
||||
```
|
||||
**Purpose**:
|
||||
- Displays application information dialog
|
||||
- Provides version, description, author, and license info
|
||||
|
||||
**Behavior**:
|
||||
1. Constructs about text with app metadata
|
||||
2. Shows modal dialog using `messagebox.showinfo()`
|
||||
3. Includes standard application information
|
||||
|
||||
**Example Usage**:
|
||||
```python
|
||||
# Called when About button is clicked
|
||||
app.show_about()
|
||||
```
|
||||
|
||||
## Helper Function Best Practices
|
||||
|
||||
1. **State Management**:
|
||||
- Always call `mark_as_modified()` after any metadata change
|
||||
- Call `mark_as_saved()` only after successful save operations
|
||||
|
||||
2. **Display Formatting**:
|
||||
- Use `format_value_for_display()` for all detail view updates
|
||||
- Apply `truncate_value()` only to tree view values
|
||||
|
||||
3. **UI Feedback**:
|
||||
- Use `set_status()` for temporary user feedback
|
||||
- Include appropriate color coding (green=success, red=error)
|
||||
- Keep messages concise and action-oriented
|
||||
|
||||
4. **Event Handling**:
|
||||
- Bind `on_selection_change()` to treeview selection events
|
||||
- Ensure detail view is properly updated on every selection change
|
||||
|
||||
5. **Title Management**:
|
||||
- Call `update_title()` after any state change that affects title
|
||||
- Maintain consistent formatting across all title states
|
||||
|
||||
These helper functions form the backbone of the application's functionality, handling everything from state management to UI updates. Each function has a specific purpose and follows consistent patterns for maintainability.
|
||||
Loading…
Add table
Add a link
Reference in a new issue