diff --git a/README.md b/README.md index bffb635..c94fbc8 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,34 @@ -# PNG Metadata Editor v1.0.0 +# PNG Metadata Editor v2.3.0 -A graphical tool for viewing and editing metadata in PNG files. +A graphical tool for viewing and editing metadata in PNG files with an intuitive image browser interface. ![PNG Metadata Editor Screenshot](screenshot.png) ## Features +### Core Features - View all metadata fields (tEXt, zTXt, iTXt chunks) in PNG files - Add, edit, and delete metadata fields -- Pretty-print JSON-formatted values - Copy metadata values to clipboard - Visual indication of unsaved changes +- Pretty-print JSON values in editor (auto-flattens on save) + +### Image Browser +- Browse directories with thumbnail previews +- Auto-scroll to selected images +- Trackpad and mousewheel scrolling support +- Smooth navigation between multiple files + +### User Interface +- Automatic dark mode detection (macOS, Windows, Linux) +- Adaptive canvas background based on system theme +- Unicode support throughout UI +- Resizable panes for optimal workflow + +### Technical Improvements +- Smart JSON handling: displays formatted, saves flattened +- Proper file handle management for reliable multi-file loading +- Enhanced scroll region updates for smooth browsing ## Installation @@ -24,21 +42,37 @@ A graphical tool for viewing and editing metadata in PNG files. Install required packages using pip: ```bash -pip install pillow pngmeta +pip install pillow ``` ## Usage +### Basic Workflow + 1. Run the application: ```bash python png-meta-editor.py ``` -2. Open a PNG file using the "Open PNG File" button +2. **Browse Directory**: Click "Browse Directory" to load thumbnails from a folder +3. **Select Image**: Click any thumbnail to load its metadata +4. **View Metadata**: See all fields in the tree view with preview +5. **Edit Fields**: + - Double-click or use "Edit Field" to modify values + - JSON values display formatted but save flattened +6. **Save Changes**: Click "Save Changes" to write metadata back to file -3. View metadata in the tree view on the left -4. Select a field to see its full value in the detail pane -5. Use the buttons to add, edit, or delete fields +### Keyboard Shortcuts + +- **Double-click** on tree item to edit +- **Enter/Escape** in dialogs to confirm/cancel + +### Dark Mode + +The application automatically detects your system theme: +- **macOS**: Reads `AppleInterfaceStyle` setting +- **Windows**: Reads personalization registry settings +- **Linux**: Checks GTK theme preferences ## Building for Distribution @@ -68,7 +102,7 @@ pyinstaller --name="PNG Metadata Editor" \ png-meta-editor.py ``` -#### Mac OS +#### macOS ```bash pyinstaller --name="PNG Metadata Editor" \ --windowed \ @@ -94,15 +128,53 @@ For building, you'll need: - `AppIcon.icns` (Mac icon) - `requirements.txt` (for dependencies) +## Technical Details + +### JSON Handling + +The editor intelligently handles JSON metadata: +- **Display**: Pretty-printed with indentation for readability +- **Storage**: Automatically flattened to single line to preserve original format +- **Example**: + ```json + // What you see in editor: + { + "prompt": "a cat", + "steps": 20 + } + + // What gets saved: + {"prompt":"a cat","steps":20} + ``` + +### File Management + +- Proper file handle release after reading metadata +- Supports loading multiple files sequentially without issues +- Thumbnail caching for smooth browsing experience + ## License This project is licensed under the MIT License - see [LICENSE](LICENSE) for details. ## Author -Robert Tusa +Robert Tusa robert@tusa.at ## Version History -- **1.0.0 (2026-01-05)**: Initial release +### v2.3.0 (2026-01-07) - Major UI Update +- Added image browser with thumbnail previews +- Implemented dark mode detection for all platforms +- Added trackpad/mousewheel scrolling support +- Smart JSON flattening (preserves original format) +- Fixed file loading issues with proper handle management +- Enhanced scroll region updates +- Unicode support in UI elements + +### 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 \ No newline at end of file diff --git a/png-meta-editor.py b/png-meta-editor.py index 431dd30..4cc3bc4 100644 --- a/png-meta-editor.py +++ b/png-meta-editor.py @@ -94,10 +94,10 @@ class PNGMetadataEditor: top_frame = ttk.Frame(self.root, padding="10") top_frame.pack(fill=tk.X) - ttk.Button(top_frame, text="📄 Open PNG File", command=self.open_file).pack(side=tk.LEFT, padx=5) - ttk.Button(top_frame, text="đŸ“Ĩ Browse Directory", command=self.browse_directory).pack(side=tk.LEFT, padx=5) - ttk.Button(top_frame, text="✅ Save Changes", command=self.save_changes).pack(side=tk.LEFT, padx=5) - ttk.Button(top_frame, text="â„šī¸ About", command=self.show_about).pack(side=tk.RIGHT, padx=5) + ttk.Button(top_frame, text="🌌 Open PNG File", command=self.open_file).pack(side=tk.LEFT, padx=5) + ttk.Button(top_frame, text="đŸ—‚ī¸ Browse Directory", command=self.browse_directory).pack(side=tk.LEFT, padx=5) + ttk.Button(top_frame, text="💾 Save Changes", command=self.save_changes).pack(side=tk.LEFT, padx=5) + ttk.Button(top_frame, text="đŸē About", command=self.show_about).pack(side=tk.RIGHT, padx=5) self.file_label = ttk.Label(top_frame, text="No file loaded", foreground="gray") self.file_label.pack(side=tk.LEFT, padx=20) @@ -191,10 +191,10 @@ class PNGMetadataEditor: button_frame = ttk.Frame(editor_frame, padding="10") button_frame.pack(fill=tk.X) - ttk.Button(button_frame, text="🤌 Add Field", command=self.add_field).pack(side=tk.LEFT, padx=5) + ttk.Button(button_frame, text="âœī¸ Add Field", command=self.add_field).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="âœī¸ Edit Field", command=self.edit_entry).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="❌ Delete Field", command=self.delete_field).pack(side=tk.LEFT, padx=5) - ttk.Button(button_frame, text="âœŒī¸ Copy Value", command=self.copy_value).pack(side=tk.LEFT, padx=5) + ttk.Button(button_frame, text="📋 Copy Value", command=self.copy_value).pack(side=tk.LEFT, padx=5) # Status bar at the bottom status_frame = ttk.Frame(self.root, relief=tk.SUNKEN) @@ -421,6 +421,8 @@ class PNGMetadataEditor: if hasattr(img, 'text'): self.metadata_dict = dict(img.text) + img.close() # Release file handle + self.file_label.config(text=Path(filepath).name, foreground="") self.mark_as_saved() self.refresh_tree() @@ -494,6 +496,15 @@ class PNGMetadataEditor: except (json.JSONDecodeError, TypeError): return value + def flatten_json_if_valid(self, value): + """If value is valid JSON, return it flattened (no indent), otherwise return as-is""" + try: + parsed = json.loads(value) + return json.dumps(parsed, ensure_ascii=False, separators=(',', ':')) + except (json.JSONDecodeError, TypeError): + return value + + def truncate_value(self, value, max_length=80): """Truncate long values for tree display""" value_str = str(value) @@ -583,7 +594,9 @@ class PNGMetadataEditor: messagebox.showwarning("Invalid Input", "Key cannot be empty") return - self.metadata_dict[key] = value + # Flatten JSON if valid to keep original format + flattened_value = self.flatten_json_if_valid(value) + self.metadata_dict[key] = flattened_value self.mark_as_modified() self.refresh_tree() self.set_status(f"Added field '{key}'", color="green") @@ -637,7 +650,9 @@ class PNGMetadataEditor: def save_edit(): new_value = value_text.get(1.0, tk.END).strip() - self.metadata_dict[key] = new_value + # Flatten JSON if valid to keep original format + flattened_value = self.flatten_json_if_valid(new_value) + self.metadata_dict[key] = flattened_value self.mark_as_modified() self.refresh_tree() self.set_status(f"Updated field '{key}'", color="green") diff --git a/screenshot.png b/screenshot.png index ac57d77..6582577 100644 Binary files a/screenshot.png and b/screenshot.png differ