diff --git a/Helper-Methods.md b/Helper-Methods.md new file mode 100644 index 0000000..e3f8f45 --- /dev/null +++ b/Helper-Methods.md @@ -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("<>", 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. \ No newline at end of file