Proposal: Migrate from tkinter to PySide6
Overview
Change ID: migrate-to-pyside6
Problem: Despite multiple attempts to fix tkinter rendering issues on macOS (ttk style configuration, tk.Frame replacement), the LoginWindow remains invisible. tkinter has fundamental macOS compatibility issues that cannot be reliably resolved.
Impact: Critical - Application is unusable on macOS. Migration to a modern, reliable GUI framework is necessary.
Root Cause Analysis
Tkinter Limitations
After three failed fix attempts:
- Entry widget styling (added bg/fg colors)
- ttk.Frame style configuration (added TFrame/TLabel styles)
- tk.Frame replacement (replaced all ttk.Frame with tk.Frame)
Conclusion: tkinter has deep macOS rendering issues that cannot be solved with configuration changes. The framework is aging and has poor macOS support.
Why tkinter Fails on macOS
- Outdated architecture: tkinter wraps Tk/Tcl from the 1990s
- Poor theme support: ttk themes don't work consistently on macOS
- macOS-specific bugs: Many unresolved issues in the bug tracker
- No native rendering: Uses X11-style rendering that conflicts with macOS
Proposed Solution: Migrate to PySide6
Why PySide6
Technical Benefits:
- Native rendering: True native widgets on each platform
- Official support: Maintained by Qt Company (not third-party)
- Modern: Qt 6 with latest features and performance improvements
- Cross-platform: Excellent macOS, Windows, Linux support
- Better Windows support: Native Windows 11 styling, high-DPI, touch support
Licensing:
- PySide6: LGPL (can use in commercial apps without opening source code)
- PyQt5: GPL (requires commercial license or open source)
Long-term:
- Qt 6 is the current major version, actively developed
- Qt 5 (PyQt5) is in maintenance mode
- Better future-proofing
Migration Scope
Files to Replace:
-
image_generator.py- Complete rewrite using Qt
New Structure:
# Qt-based implementation
from PySide6.QtWidgets import (
QApplication, QMainWindow, QDialog, QWidget,
QVBoxLayout, QHBoxLayout, QLabel, QLineEdit,
QPushButton, QCheckBox, QTextEdit, QComboBox
)
from PySide6.QtCore import Qt, QThread, Signal
from PySide6.QtGui import QPixmap, QFont
class LoginDialog(QDialog):
"""Qt-based login window"""
# Clean, modern dialog with native rendering
class ImageGeneratorWindow(QMainWindow):
"""Qt-based main application window"""
# Modern UI with better layout management
Functionality Preserved:
- ✓ Database authentication
- ✓ Remember username/password
- ✓ Image generation with Gemini API
- ✓ Reference image upload
- ✓ Image preview and download
- ✓ Prompt management (favorites)
- ✓ All existing features
UI Improvements:
- Better layout management (Qt layouts vs pack/grid)
- Native look and feel on all platforms
- Smooth threading (QThread vs threading.Thread)
- Better image handling (QPixmap)
- Modern file dialogs
- Better styling options (QSS - Qt Style Sheets)
Migration Strategy
Phase 1: LoginDialog (Priority: High)
- Rewrite LoginWindow as QDialog
- Implement authentication logic
- Test on macOS to verify visibility
- Estimated: 2-3 hours
Phase 2: ImageGeneratorWindow (Priority: High)
- Rewrite ImageGeneratorApp as QMainWindow
- Implement all existing features
- Improve layout and responsiveness
- Estimated: 4-6 hours
Phase 3: Testing & Polish (Priority: Medium)
- Cross-platform testing
- UI polish and refinements
- Performance optimization
- Estimated: 1-2 hours
Total Estimated Effort: 8-11 hours
Code Structure Comparison
Current (tkinter):
# Procedural, pack-based layout
main_frame = tk.Frame(self.root, bg='white', padx=40, pady=40)
main_frame.pack(fill="both", expand=True)
username_label = tk.Label(main_frame, text="用户名", ...)
username_label.pack(anchor="w")
Proposed (PySide6):
# Layout-based, cleaner structure
layout = QVBoxLayout()
username_label = QLabel("用户名")
username_label.setStyleSheet("color: #666666; font-size: 10pt;")
layout.addWidget(username_label)
self.setLayout(layout)
Benefits:
- Declarative layouts vs imperative pack/grid
- Better separation of concerns
- Easier to maintain and modify
- Responsive resizing out of the box
Alternative Considered: PyQt5
Rejected because:
- GPL license (restrictive for commercial use)
- Based on Qt 5 (older version)
- Not officially maintained by Qt
- Worse Windows 11 integration
Dependencies
New Dependency:
-
PySide6>=6.6.0(latest stable)
Removed Dependency:
- None (PIL, google-genai, pymysql all compatible with Qt)
Installation:
pip install PySide6
Risks & Mitigation
Risks:
-
Learning curve: Team needs to learn Qt
- Mitigation: Qt has excellent documentation, similar concepts to other GUIs
-
Breaking changes: Complete UI rewrite
- Mitigation: Preserve all functionality, test thoroughly
-
Binary size: Qt apps are larger
- Mitigation: Acceptable for desktop app, better UX worth it
-
Migration time: ~8-11 hours of work
- Mitigation: Necessary to make app usable on macOS
Benefits
Immediate:
- ✓ LoginWindow visible and functional on macOS
- ✓ Native look on all platforms
- ✓ Better reliability
Long-term:
- ✓ Modern, maintained framework
- ✓ Better performance
- ✓ More UI possibilities (animations, effects)
- ✓ Easier to add features
- ✓ Better cross-platform consistency
Testing Strategy
Manual Testing:
- macOS: Verify LoginDialog visible and functional
- macOS: Verify main window renders correctly
- Windows: Test UI appearance and functionality
- Linux: Test if available
Functional Testing:
- Login flow works
- Image generation works
- File operations work
- Database interaction works
- Config save/load works
Rollback Plan
If migration fails:
- Keep current tkinter code in git history
- Can revert if needed
- Low risk: Qt is proven technology
Recommendation
Proceed with PySide6 migration: This is the only reliable solution to fix the macOS visibility issue. tkinter has proven incompatible with macOS despite multiple fix attempts. PySide6 provides a modern, well-supported foundation for cross-platform desktop applications.