Add Helper Methods

robert 2026-01-06 16:45:10 +01:00
parent 7d5b1565e2
commit 6e92799c3b

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.