Update Documentation
parent
f153fba905
commit
405075c4eb
1 changed files with 364 additions and 196 deletions
560
Documentation.md
560
Documentation.md
|
|
@ -1,52 +1,46 @@
|
||||||
# PNG Metadata Editor - Technical Documentation
|
# PNG Metadata Editor - Technical Documentation v2.3.0
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
1. [Overview](#overview)
|
1. [Overview](#overview)
|
||||||
2. [Build System](#build-system)
|
2. [Build System](#build-system)
|
||||||
3. [Code Structure](#code-structure)
|
3. [Architecture](#architecture)
|
||||||
4. [Class Reference](#class-reference)
|
4. [Code Structure](#code-structure)
|
||||||
5. [Function Documentation](#function-documentation)
|
5. [Class Reference](#class-reference)
|
||||||
6. [Data Flow](#data-flow)
|
6. [Function Documentation](#function-documentation)
|
||||||
7. [Usage Examples](#usage-examples)
|
7. [Data Flow](#data-flow)
|
||||||
8. [Troubleshooting](#troubleshooting)
|
8. [Theme System](#theme-system)
|
||||||
|
9. [Usage Examples](#usage-examples)
|
||||||
|
10. [Troubleshooting](#troubleshooting)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
PNG Metadata Editor is a graphical application built with Python and Tkinter that allows users to view, edit, add, and delete metadata in PNG files. The application leverages the Pillow library for PNG manipulation.
|
PNG Metadata Editor v2.3.0 is a graphical application built with Python and Tkinter that allows users to view, edit, add, and delete metadata in PNG files. The application features an image browser with thumbnail previews, automatic dark mode detection, and intelligent JSON handling.
|
||||||
|
|
||||||
|
### Key Technologies
|
||||||
|
- **Python 3.6+**: Core language
|
||||||
|
- **Tkinter**: GUI framework
|
||||||
|
- **Pillow (PIL)**: PNG image manipulation and metadata extraction
|
||||||
|
- **subprocess**: System theme detection
|
||||||
|
|
||||||
|
### Version 2.3.0 Features
|
||||||
|
- Image browser with thumbnail previews
|
||||||
|
- Automatic dark mode detection (macOS, Windows, Linux)
|
||||||
|
- Smart JSON flattening (preserves original format)
|
||||||
|
- Enhanced file handle management
|
||||||
|
- Trackpad and mousewheel scrolling support
|
||||||
|
- Resizable UI panes
|
||||||
|
|
||||||
## Build System
|
## Build System
|
||||||
|
|
||||||
### build.py Functionality
|
### build.py Functionality
|
||||||
|
|
||||||
The `build.py` script provides a unified way to build the application across platforms:
|
The `build.py` script provides a unified way to build the application across platforms with automatic dependency management, PyInstaller handling, and platform-specific build options.
|
||||||
|
|
||||||
1. **Dependency Management**:
|
|
||||||
- Checks for `requirements.txt` and installs dependencies
|
|
||||||
- Falls back to default dependencies if file not found
|
|
||||||
|
|
||||||
2. **PyInstaller Handling**:
|
|
||||||
- Verifies PyInstaller is installed
|
|
||||||
- Automatically installs it if missing
|
|
||||||
- Provides clear error messages if installation fails
|
|
||||||
|
|
||||||
3. **Platform-Specific Builds**:
|
|
||||||
- Detects operating system (Windows, macOS, Linux)
|
|
||||||
- Applies appropriate build options for each platform
|
|
||||||
- Includes version information from `version.txt` when available
|
|
||||||
|
|
||||||
4. **Build Process**:
|
|
||||||
```python
|
|
||||||
# Main build workflow:
|
|
||||||
1. Check/install PyInstaller
|
|
||||||
2. Install dependencies
|
|
||||||
3. Determine platform options
|
|
||||||
4. Execute PyInstaller build
|
|
||||||
```
|
|
||||||
|
|
||||||
### Build Requirements
|
### Build Requirements
|
||||||
|
|
||||||
- Python 3.6+
|
- Python 3.6+
|
||||||
- PyInstaller (auto-installed if missing)
|
- PyInstaller (auto-installed if missing)
|
||||||
|
- Pillow library
|
||||||
- Platform-specific icon files:
|
- Platform-specific icon files:
|
||||||
- `AppIcon.ico` for Windows
|
- `AppIcon.ico` for Windows
|
||||||
- `AppIcon.icns` for macOS
|
- `AppIcon.icns` for macOS
|
||||||
|
|
@ -57,167 +51,286 @@ The `build.py` script provides a unified way to build the application across pla
|
||||||
- Temporary build files are stored in `build/`
|
- Temporary build files are stored in `build/`
|
||||||
- Version information is embedded from `version.txt`
|
- Version information is embedded from `version.txt`
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Component Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
+-----------------------------------------------+
|
||||||
|
| PNG Metadata Editor v2.3.0 |
|
||||||
|
+-----------------------------------------------+
|
||||||
|
| +-----------+ +------------------------+ |
|
||||||
|
| | Image | | Metadata Editor | |
|
||||||
|
| | Browser | | +------------------+ | |
|
||||||
|
| | | | | Tree View | | |
|
||||||
|
| | Thumbnails| | +------------------+ | |
|
||||||
|
| | + Scroll | | | Detail Pane | | |
|
||||||
|
| +-----------+ | +------------------+ | |
|
||||||
|
| | Edit/Add/Delete | |
|
||||||
|
| +------------------------+ |
|
||||||
|
+-----------------------------------------------+
|
||||||
|
| Status Bar & Controls |
|
||||||
|
+-----------------------------------------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
### Design Patterns
|
||||||
|
|
||||||
|
1. **MVC Pattern**:
|
||||||
|
- Model: `metadata_dict`
|
||||||
|
- View: Tkinter UI components
|
||||||
|
- Controller: Event handlers and methods
|
||||||
|
|
||||||
|
2. **Observer Pattern**: UI updates when metadata changes via `refresh_tree()`
|
||||||
|
|
||||||
|
3. **Strategy Pattern**: Platform-specific theme detection
|
||||||
|
|
||||||
## Code Structure
|
## Code Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
png-meta-editor.py
|
png-meta-editor.py
|
||||||
├── Constants (APP_VERSION, APP_NAME)
|
|
|
||||||
├── PNGMetadataEditor (Main Class)
|
+-- Constants
|
||||||
│ ├── __init__() - Initialization
|
| +-- APP_VERSION = "2.3.0"
|
||||||
│ ├── setup_ui() - UI Construction
|
| +-- APP_NAME = "PNG Metadata Editor"
|
||||||
│ ├── open_file() - File Operations
|
|
|
||||||
│ └── Various helper methods
|
+-- PNGMetadataEditor (Main Class)
|
||||||
└── Supporting Functions
|
+-- __init__() - Initialization
|
||||||
├── format_value_for_display()
|
+-- Theme System
|
||||||
├── truncate_value()
|
| +-- detect_dark_mode()
|
||||||
└── mark_as_modified()
|
| +-- get_theme_colors()
|
||||||
|
+-- UI Construction
|
||||||
|
| +-- setup_ui()
|
||||||
|
| +-- Image Browser (left pane)
|
||||||
|
| +-- Metadata Editor (right pane)
|
||||||
|
+-- Image Browser
|
||||||
|
| +-- browse_directory()
|
||||||
|
| +-- load_directory_thumbnails()
|
||||||
|
| +-- load_file_from_path()
|
||||||
|
| +-- scroll_to_thumbnail()
|
||||||
|
+-- Metadata Operations
|
||||||
|
| +-- open_file()
|
||||||
|
| +-- save_changes()
|
||||||
|
| +-- add_field()
|
||||||
|
| +-- edit_entry()
|
||||||
|
| +-- delete_field()
|
||||||
|
+-- JSON Handling
|
||||||
|
| +-- format_value_for_display()
|
||||||
|
| +-- flatten_json_if_valid()
|
||||||
|
+-- Helper Methods
|
||||||
|
+-- refresh_tree()
|
||||||
|
+-- set_status()
|
||||||
|
+-- mark_as_modified()
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Class Reference
|
## Class Reference
|
||||||
|
|
||||||
### PNGMetadataEditor Class
|
### PNGMetadataEditor Class
|
||||||
|
|
||||||
```python
|
Instance variables:
|
||||||
class PNGMetadataEditor:
|
- `current_file`: Currently loaded PNG file path
|
||||||
def __init__(self, root):
|
- `current_directory`: Current directory for browser
|
||||||
# Initialize application state
|
- `metadata_dict`: Dictionary storing metadata fields
|
||||||
self.current_file = None # Currently loaded PNG file path
|
- `has_unsaved_changes`: Track unsaved modifications
|
||||||
self.metadata_dict = {} # Dictionary storing all metadata fields
|
- `status_timer`: Timer for status messages
|
||||||
self.has_unsaved_changes = False # Track unsaved modifications
|
- `thumbnail_images`: PhotoImage references for thumbnails
|
||||||
self.status_timer = None # Timer for status messages
|
- `thumbnail_frames`: Frame references for highlighting
|
||||||
```
|
- `theme_colors`: Current theme color scheme
|
||||||
|
|
||||||
### Public Methods
|
### Public Methods
|
||||||
|
|
||||||
| Method | Description |
|
| Method | Description |
|
||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| `open_file()` | Loads PNG file and extracts metadata |
|
| `detect_dark_mode()` | Detects OS dark mode setting |
|
||||||
|
| `get_theme_colors()` | Returns color scheme based on theme |
|
||||||
|
| `browse_directory()` | Opens directory browser for thumbnails |
|
||||||
|
| `load_directory_thumbnails(directory)` | Loads PNG thumbnails from directory |
|
||||||
|
| `load_file_from_path(filepath)` | Loads specific PNG file metadata |
|
||||||
|
| `open_file()` | Opens file dialog and loads PNG |
|
||||||
| `save_changes()` | Saves current metadata to file |
|
| `save_changes()` | Saves current metadata to file |
|
||||||
| `add_field()` | Opens dialog to add new metadata field |
|
| `add_field()` | Opens dialog to add new metadata field |
|
||||||
| `edit_entry(event=None)` | Opens dialog to edit selected field |
|
| `edit_entry(event=None)` | Opens dialog to edit selected field |
|
||||||
| `delete_field()` | Removes selected metadata field |
|
| `delete_field()` | Removes selected metadata field |
|
||||||
| `copy_value()` | Copies selected metadata value to clipboard |
|
| `copy_value()` | Copies selected value to clipboard |
|
||||||
| `show_about()` | Displays application about dialog |
|
| `flatten_json_if_valid(value)` | Flattens JSON to preserve format |
|
||||||
|
|
||||||
### Helper Methods
|
|
||||||
|
|
||||||
```python
|
|
||||||
def format_value_for_display(self, value):
|
|
||||||
"""Try to parse as JSON for pretty printing"""
|
|
||||||
try:
|
|
||||||
return json.dumps(json.loads(value), indent=2)
|
|
||||||
except:
|
|
||||||
return str(value)
|
|
||||||
|
|
||||||
def truncate_value(self, value, max_length=80):
|
|
||||||
"""Shorten long values for tree display"""
|
|
||||||
return str(value)[:max_length] + "..." if len(str(value)) > max_length else str(value)
|
|
||||||
|
|
||||||
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")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Function Documentation
|
## Function Documentation
|
||||||
|
|
||||||
### Core Functions
|
### Theme System
|
||||||
|
|
||||||
1. **`open_file()`**
|
**detect_dark_mode()**
|
||||||
- Opens a file dialog to select PNG file
|
- Detects if OS is in dark mode
|
||||||
- Extracts text chunks using PIL's `Image.text` attribute
|
- Platform-specific commands with 1-second timeout
|
||||||
- Populates `metadata_dict` with key-value pairs
|
- Returns: `bool` (True if dark mode, False otherwise)
|
||||||
- Refreshes UI to display new metadata
|
|
||||||
|
|
||||||
2. **`save_changes()`**
|
**get_theme_colors()**
|
||||||
- Creates new `PngInfo` object with current metadata
|
- Returns color scheme dictionary based on detected theme
|
||||||
- Saves image with updated metadata using PIL's `img.save()`
|
- Dark mode: #2b2b2b backgrounds, #ffffff text
|
||||||
- Marks changes as saved and updates UI state
|
- Light mode: #f0f0f0 backgrounds, #000000 text
|
||||||
|
|
||||||
3. **`refresh_tree()`**
|
### Image Browser Functions
|
||||||
- Clears existing tree view items
|
|
||||||
- Repopulates with current metadata from `metadata_dict`
|
|
||||||
- Truncates long values for display in tree
|
|
||||||
|
|
||||||
4. **`on_selection_change(event)`**
|
**load_directory_thumbnails(directory)**
|
||||||
- Updates detail view when selection changes
|
- Loads 150x150 thumbnails for all PNG files
|
||||||
- Formats selected value for display (JSON pretty-printing)
|
- Shows loading indicator with progress
|
||||||
- Handles both JSON and plain text values
|
- Updates scroll region 3 times (0ms, 100ms, 300ms)
|
||||||
|
|
||||||
### UI Management Functions
|
**load_file_from_path(filepath, auto_scroll=True)**
|
||||||
|
- Opens image with PIL
|
||||||
|
- Extracts text chunks to metadata_dict
|
||||||
|
- **Critical**: Closes image file handle with `img.close()`
|
||||||
|
- Refreshes UI and scrolls to thumbnail
|
||||||
|
|
||||||
1. **`set_status(message, duration=3000, color="")`**
|
**scroll_to_thumbnail(filepath)**
|
||||||
- Displays temporary message in status bar
|
- Centers selected thumbnail in viewport
|
||||||
- Automatically clears after specified duration (ms)
|
- Uses `yview_moveto()` for smooth scrolling
|
||||||
- Supports colored messages for different status types
|
|
||||||
|
|
||||||
2. **`update_title()`**
|
### JSON Handling Functions
|
||||||
- Updates window title based on current state
|
|
||||||
- Shows filename when file is loaded
|
|
||||||
- Adds asterisk (*) for unsaved changes
|
|
||||||
|
|
||||||
3. **`show_about()`**
|
**format_value_for_display(value)**
|
||||||
- Displays application information dialog
|
- Pretty-prints JSON with `indent=2` for editor display
|
||||||
- Shows version, description, author, and license
|
- Falls back to original string if not valid JSON
|
||||||
- Uses `messagebox.showinfo()` for standard dialog
|
|
||||||
|
|
||||||
### Data Handling Functions
|
**flatten_json_if_valid(value)**
|
||||||
|
- Flattens JSON to single line: `separators=(',', ':')`
|
||||||
1. **`format_value_for_display(value)`**
|
- Preserves original compact format before saving
|
||||||
- Attempts to parse value as JSON
|
- Falls back to original string if not valid JSON
|
||||||
- Returns pretty-printed JSON if successful
|
|
||||||
- Falls back to string representation otherwise
|
|
||||||
|
|
||||||
2. **`truncate_value(value, max_length=80)`**
|
|
||||||
- Shortens long values for tree display
|
|
||||||
- Adds ellipsis (...) to truncated values
|
|
||||||
- Preserves full value in detail view
|
|
||||||
|
|
||||||
3. **`mark_as_modified()`**
|
|
||||||
- Sets unsaved changes flag
|
|
||||||
- Updates UI indicators (title, label)
|
|
||||||
- Prevents duplicate change notifications
|
|
||||||
|
|
||||||
## Data Flow
|
## Data Flow
|
||||||
|
|
||||||
1. **File Opening**:
|
### 1. File Opening with Image Browser
|
||||||
```mermaid
|
|
||||||
sequenceDiagram
|
|
||||||
User->>App: Click "Open PNG File"
|
|
||||||
App->>FileDialog: Show open dialog
|
|
||||||
User->>App: Select PNG file
|
|
||||||
App->>PIL: Image.open(filepath)
|
|
||||||
PIL->>App: Extract text chunks
|
|
||||||
App->>metadata_dict: Store key-value pairs
|
|
||||||
App->>UI: Refresh tree and detail view
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Editing Process**:
|
```mermaid
|
||||||
```mermaid
|
sequenceDiagram
|
||||||
sequenceDiagram
|
participant User
|
||||||
User->>App: Select field in tree
|
participant App
|
||||||
App->>on_selection_change: Update detail view
|
participant FileDialog
|
||||||
User->>App: Click "Edit Field"
|
participant PIL
|
||||||
App->>Dialog: Show edit dialog
|
participant UI
|
||||||
User->>App: Modify value
|
|
||||||
User->>App: Click "Save"
|
|
||||||
App->>metadata_dict: Update value
|
|
||||||
App->>mark_as_modified: Set unsaved flag
|
|
||||||
App->>UI: Refresh tree view
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Saving**:
|
User->>App: Click "Browse Directory"
|
||||||
```mermaid
|
App->>FileDialog: Show directory dialog
|
||||||
sequenceDiagram
|
User->>App: Select directory
|
||||||
User->>App: Click "Save Changes"
|
App->>App: load_directory_thumbnails()
|
||||||
App->>PIL: Image.open(current_file)
|
App->>UI: Show loading indicator
|
||||||
App->>PngInfo: Create new metadata object
|
loop For each PNG file
|
||||||
App->>PIL: img.save(pnginfo=metadata)
|
App->>PIL: Open and create thumbnail
|
||||||
App->>mark_as_saved: Clear unsaved flag
|
App->>UI: Create thumbnail frame
|
||||||
App->>UI: Update status and title
|
end
|
||||||
```
|
App->>UI: Update scroll region (3x)
|
||||||
|
User->>App: Click thumbnail
|
||||||
|
App->>App: load_file_from_path()
|
||||||
|
App->>PIL: Image.open(filepath)
|
||||||
|
PIL->>App: Extract text chunks
|
||||||
|
App->>PIL: img.close() <- IMPORTANT
|
||||||
|
App->>App: Store in metadata_dict
|
||||||
|
App->>UI: Refresh tree and detail view
|
||||||
|
App->>UI: Scroll to selected thumbnail
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Editing Process with JSON Flattening
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant User
|
||||||
|
participant App
|
||||||
|
participant Dialog
|
||||||
|
participant metadata_dict
|
||||||
|
participant UI
|
||||||
|
|
||||||
|
User->>App: Select field in tree
|
||||||
|
App->>App: on_selection_change()
|
||||||
|
App->>App: format_value_for_display()
|
||||||
|
Note over App: Pretty-print JSON with indent=2
|
||||||
|
App->>UI: Show formatted value
|
||||||
|
User->>App: Click "Edit Field"
|
||||||
|
App->>Dialog: Show edit dialog with formatted JSON
|
||||||
|
User->>Dialog: Modify value
|
||||||
|
User->>Dialog: Click "Save"
|
||||||
|
Dialog->>App: Return edited value
|
||||||
|
App->>App: flatten_json_if_valid()
|
||||||
|
Note over App: Flatten to single line
|
||||||
|
App->>metadata_dict: Update with flattened value
|
||||||
|
App->>App: mark_as_modified()
|
||||||
|
App->>UI: Refresh tree view
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Saving Metadata
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant User
|
||||||
|
participant App
|
||||||
|
participant PIL
|
||||||
|
participant metadata_dict
|
||||||
|
participant File
|
||||||
|
|
||||||
|
User->>App: Click "Save Changes"
|
||||||
|
App->>PIL: Image.open(current_file)
|
||||||
|
App->>PIL: Create PngInfo object
|
||||||
|
loop For each metadata field
|
||||||
|
App->>metadata_dict: Get key-value pair
|
||||||
|
Note over App: Values already flattened
|
||||||
|
App->>PIL: metadata.add_text(key, value)
|
||||||
|
end
|
||||||
|
App->>PIL: img.save(pnginfo=metadata)
|
||||||
|
PIL->>File: Write PNG with metadata
|
||||||
|
App->>App: mark_as_saved()
|
||||||
|
App->>App: Update UI (remove asterisk)
|
||||||
|
App->>App: Show success status
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Theme Detection Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant App
|
||||||
|
participant detect_dark_mode
|
||||||
|
participant subprocess
|
||||||
|
participant winreg
|
||||||
|
participant get_theme_colors
|
||||||
|
participant UI
|
||||||
|
|
||||||
|
App->>detect_dark_mode: Check OS theme
|
||||||
|
alt macOS
|
||||||
|
detect_dark_mode->>subprocess: defaults read AppleInterfaceStyle
|
||||||
|
subprocess-->>detect_dark_mode: "Dark" or error
|
||||||
|
else Windows
|
||||||
|
detect_dark_mode->>winreg: Read AppsUseLightTheme
|
||||||
|
winreg-->>detect_dark_mode: 0=Dark, 1=Light
|
||||||
|
else Linux
|
||||||
|
detect_dark_mode->>subprocess: gsettings get gtk-theme
|
||||||
|
subprocess-->>detect_dark_mode: theme name
|
||||||
|
end
|
||||||
|
detect_dark_mode-->>App: Return boolean (True=Dark)
|
||||||
|
App->>get_theme_colors: Get color scheme
|
||||||
|
get_theme_colors-->>App: Return color dict
|
||||||
|
App->>UI: Apply colors to canvas/frames
|
||||||
|
```
|
||||||
|
|
||||||
|
## Theme System
|
||||||
|
|
||||||
|
### Color Schemes
|
||||||
|
|
||||||
|
**Dark Mode** (when OS theme is dark):
|
||||||
|
- canvas_bg: #2b2b2b (dark gray)
|
||||||
|
- frame_bg: #2b2b2b
|
||||||
|
- text_fg: #ffffff (white)
|
||||||
|
- loading_fg: #6bb6ff (light blue)
|
||||||
|
|
||||||
|
**Light Mode** (when OS theme is light):
|
||||||
|
- canvas_bg: #f0f0f0 (light gray)
|
||||||
|
- frame_bg: #f0f0f0
|
||||||
|
- text_fg: #000000 (black)
|
||||||
|
- loading_fg: #4a90d9 (blue)
|
||||||
|
|
||||||
|
### Platform-Specific Detection
|
||||||
|
|
||||||
|
| Platform | Method | Command/Registry |
|
||||||
|
|----------|--------|------------------|
|
||||||
|
| macOS | `defaults` | `defaults read -g AppleInterfaceStyle` |
|
||||||
|
| Windows | Registry | `HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize\AppsUseLightTheme` |
|
||||||
|
| Linux | `gsettings` | `gsettings get org.gnome.desktop.interface gtk-theme` |
|
||||||
|
|
||||||
## Usage Examples
|
## Usage Examples
|
||||||
|
|
||||||
|
|
@ -227,28 +340,31 @@ def mark_as_modified(self):
|
||||||
root = tk.Tk()
|
root = tk.Tk()
|
||||||
app = PNGMetadataEditor(root)
|
app = PNGMetadataEditor(root)
|
||||||
|
|
||||||
# Open a file programmatically (simulated)
|
# Browse directory (simulated)
|
||||||
app.current_file = "example.png"
|
app.current_directory = "/path/to/images"
|
||||||
img = Image.open("example.png")
|
app.load_directory_thumbnails("/path/to/images")
|
||||||
app.metadata_dict = dict(img.text)
|
|
||||||
app.refresh_tree()
|
|
||||||
|
|
||||||
# Edit a field programmatically
|
# Load specific file
|
||||||
app.edit_entry() # Would normally be triggered by UI event
|
app.load_file_from_path(Path("/path/to/image.png"))
|
||||||
|
|
||||||
|
# Edit metadata
|
||||||
|
app.metadata_dict["prompt"] = '{"text":"a cat","steps":20}'
|
||||||
|
app.mark_as_modified()
|
||||||
|
app.refresh_tree()
|
||||||
|
|
||||||
# Save changes
|
# Save changes
|
||||||
app.save_changes()
|
app.save_changes()
|
||||||
```
|
```
|
||||||
|
|
||||||
### Programmatic Access
|
### Programmatic Metadata Access
|
||||||
```python
|
```python
|
||||||
# Get all metadata as dictionary
|
# Get all metadata
|
||||||
metadata = app.metadata_dict
|
metadata = app.metadata_dict
|
||||||
|
|
||||||
# Add new field programmatically
|
# Add field with JSON (will be flattened on save)
|
||||||
app.metadata_dict["NewField"] = "Value"
|
json_value = json.dumps({"key": "value"}, indent=2)
|
||||||
|
app.metadata_dict["NewField"] = json_value
|
||||||
app.mark_as_modified()
|
app.mark_as_modified()
|
||||||
app.refresh_tree()
|
|
||||||
|
|
||||||
# Check for unsaved changes
|
# Check for unsaved changes
|
||||||
if app.has_unsaved_changes:
|
if app.has_unsaved_changes:
|
||||||
|
|
@ -257,50 +373,102 @@ if app.has_unsaved_changes:
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Fixed in v2.3.0
|
||||||
|
|
||||||
|
**Can't Load Second File**
|
||||||
|
- Status: FIXED
|
||||||
|
- Cause: Image file handle not closed
|
||||||
|
- Solution: Added `img.close()` after reading metadata
|
||||||
|
|
||||||
|
**JSON Format Changes After Edit**
|
||||||
|
- Status: FIXED
|
||||||
|
- Cause: Pretty-printed JSON was saved as-is
|
||||||
|
- Solution: `flatten_json_if_valid()` restores compact format
|
||||||
|
|
||||||
|
**Scroll Stops Before End of Thumbnails**
|
||||||
|
- Status: FIXED
|
||||||
|
- Cause: Scroll region not updated after thumbnail loading
|
||||||
|
- Solution: Triple scroll region update (0ms, 100ms, 300ms)
|
||||||
|
|
||||||
### Common Issues
|
### Common Issues
|
||||||
1. **File Not Opening**:
|
|
||||||
- Ensure file is a valid PNG
|
|
||||||
- Check file permissions
|
|
||||||
|
|
||||||
2. **Metadata Not Displaying**:
|
**File Not Opening**
|
||||||
- Verify PNG contains text chunks
|
- Ensure file is a valid PNG
|
||||||
- Check for special characters in keys/values
|
- Check file permissions
|
||||||
|
- Verify file isn't locked by another process
|
||||||
|
|
||||||
3. **Save Failures**:
|
**Metadata Not Displaying**
|
||||||
- Ensure file isn't read-only
|
- Verify PNG contains text chunks (tEXt, zTXt, iTXt)
|
||||||
- Verify write permissions to directory
|
- Check for special characters in keys/values
|
||||||
|
|
||||||
### Debugging Tips
|
**Save Failures**
|
||||||
- Check status bar for error messages
|
- Ensure file isn't read-only
|
||||||
- Review console output for exceptions
|
- Verify write permissions to directory
|
||||||
- Verify metadata_dict contents in debugger
|
- Check available disk space
|
||||||
|
|
||||||
|
**Dark Mode Not Working**
|
||||||
|
- Verify theme detection command access
|
||||||
|
- Check terminal for subprocess errors
|
||||||
|
- Ensure permissions for `defaults` or `gsettings`
|
||||||
|
|
||||||
## Development Notes
|
## Development Notes
|
||||||
|
|
||||||
### Build System Improvements
|
### v2.3.0 Architecture Decisions
|
||||||
1. **Automatic Dependency Installation**:
|
|
||||||
- Script checks for `requirements.txt`
|
|
||||||
- Installs dependencies automatically
|
|
||||||
|
|
||||||
2. **PyInstaller Management**:
|
1. **JSON Flattening Strategy**:
|
||||||
- Verifies PyInstaller installation
|
- Display formatted JSON for readability
|
||||||
- Auto-installs if missing
|
- Store flattened JSON to preserve original format
|
||||||
|
- Automatic conversion in save workflows
|
||||||
|
|
||||||
3. **Platform Detection**:
|
2. **File Handle Management**:
|
||||||
- Automatically applies correct build options
|
- Always close PIL Image objects after reading
|
||||||
- Handles version information consistently
|
- Prevents file locking issues
|
||||||
|
- Enables reliable multi-file loading
|
||||||
|
|
||||||
|
3. **Scroll Region Updates**:
|
||||||
|
- Triple update strategy (0ms, 100ms, 300ms)
|
||||||
|
- Handles timing issues with tkinter geometry
|
||||||
|
- Ensures scroll region encompasses all thumbnails
|
||||||
|
|
||||||
|
4. **Theme Detection**:
|
||||||
|
- Platform-specific detection methods
|
||||||
|
- Fallback to light mode if detection fails
|
||||||
|
- Applied once at startup (not reactive)
|
||||||
|
|
||||||
### Future Improvements
|
### Future Improvements
|
||||||
|
|
||||||
- Batch processing for multiple files
|
- Batch processing for multiple files
|
||||||
- Metadata validation rules
|
- Metadata validation rules
|
||||||
- Export/import to JSON
|
- Export/import to JSON
|
||||||
- Advanced search functionality
|
- Advanced search functionality
|
||||||
|
- Undo/redo support
|
||||||
|
- Reactive theme changes (watch for OS theme changes)
|
||||||
|
- Async thumbnail loading
|
||||||
|
- Virtual scrolling for large directories
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
When contributing to this project:
|
When contributing to this project:
|
||||||
1. Fork the repository
|
|
||||||
2. Create a feature branch
|
|
||||||
3. Submit a pull request with tests
|
|
||||||
|
|
||||||
For major changes, please open an issue first to discuss what you would like to change.
|
1. **Code Style**: Follow PEP 8, use type hints, document methods
|
||||||
|
2. **Testing**: Test on all platforms, verify dark mode, test large directories
|
||||||
|
3. **Pull Requests**: Fork, create feature branch, submit with clear description
|
||||||
|
4. **Issues**: Include version, reproduction steps, sample PNG if relevant
|
||||||
|
|
||||||
|
For major changes, please open an issue first to discuss.
|
||||||
|
|
||||||
|
## Version History
|
||||||
|
|
||||||
|
- **v2.3.0 (2026-01-07)**: Major UI update
|
||||||
|
- Added image browser with thumbnail previews
|
||||||
|
- Implemented dark mode detection for all platforms
|
||||||
|
- Smart JSON flattening (preserves original format)
|
||||||
|
- Fixed file loading with proper handle management
|
||||||
|
- Enhanced scroll region updates
|
||||||
|
- Unicode support in UI
|
||||||
|
|
||||||
|
- **v1.0.0 (2026-01-05)**: Initial release
|
||||||
|
- Basic metadata viewing and editing
|
||||||
|
- Tree view with detail pane
|
||||||
|
- Add, edit, delete operations
|
||||||
|
- JSON pretty-printing
|
||||||
Loading…
Add table
Add a link
Reference in a new issue