I. Python Language Fundamentals
- Follow PEP 8 Strictly: Adhere to the official Python style guide for consistent and readable code.
- Use Type Hints: Annotate function signatures and variable types using the
typing
module to improve code clarity and enable static analysis.
- Prefer f-strings: Use f-strings for string formatting unless complex formatting or Python 2 compatibility (not required here) is needed.
- Use Context Managers: Utilize
with
statements for resource management (files, locks, network connections) to ensure proper cleanup.
- Write Idiomatic Python: Favor Python's built-in features and standard library over overly complex or non-Pythonic constructs.
- Use Virtual Environments: Always work within a dedicated virtual environment for dependency management.
- Organize with Modules and Packages: Structure the codebase logically into modules and packages based on functionality.
- Avoid
__del__
: Do not rely on object finalization (__del__
) for resource cleanup; prefer explicit methods or context managers.
- Standard Library First: Before adding a third-party library, check if the required functionality is available in Python's standard library.
- Ensure Python 3.8+ Compatibility: Develop and test against the specified minimum Python version (or latest stable release).
- Use Properties: Define controlled access to class attributes using the
@property
decorator.
- Prefer Composition over Inheritance: Design classes by favoring composition to build complex behavior from simpler objects.
- Keep Functions Small: Aim for functions and methods that are concise and focused on a single task.
- Avoid Global State: Minimize the use of global variables; prefer passing data explicitly or using dependency injection.
- Handle Exceptions Explicitly: Use specific exception types in
except
blocks rather than catching generic Exception
.
II. Qt6 & PyQt6 Core Usage
- Inherit from
QObject
: Any class requiring signals, slots, or the object-tree memory management system must inherit from QtCore.QObject
.
- Define Signals with
pyqtSignal
: Declare signals using QtCore.pyqtSignal
or PyQt6.QtCore.Signal
with appropriate argument types.
- Use
@pyqtSlot
Decorator: While optional, decorate slot methods with @QtCore.pyqtSlot
specifying argument types for clarity and runtime checking.
- Connect Signals and Slots Correctly: Use the new-style connection syntax (
signal.connect(slot)
) unless connecting signals to methods with overloaded signatures (which may require the old QtCore.QObject.connect
syntax or specifying the signature).
- GUI Operations on Main Thread Only: All creation, manipulation, and deletion of GUI widgets must occur on the main application thread.
- Thread-Safe GUI Updates: Never directly update GUI elements from a background thread. Use signals/slots or
QtCore.QMetaObject.invokeMethod
queued connections for thread-safe communication back to the main thread.
- Understand the Event Loop: Have a solid grasp of the Qt event loop and how events are processed. Blocking the event loop will freeze the GUI.
- Prefer Standard Widgets: Utilize widgets from
QtWidgets
or other standard Qt modules unless complex or highly customized rendering is required.
- Use Layouts: Employ
QtWidgets.QVBoxLayout
, QHBoxLayout
, QGridLayout
, QFormLayout
, etc., for positioning widgets, avoiding fixed sizes unless absolutely necessary.
- Set Object Names: Assign meaningful and unique names to widgets using
setObjectName()
for easy styling via QSS and debugging.
- Utilize
QSettings
: Manage persistent application settings and user preferences using QtCore.QSettings
.
- Understand Object Tree: Leverage the parent-child relationship of
QObject
for automatic memory management; a child object is automatically deleted when its parent is deleted.
- Disconnect Signals: Disconnect signals and slots when they are no longer needed, especially before deleting objects, to prevent dangling references and crashes.
- Use
QTimer
: For periodic tasks or delayed function calls that need to run on the main thread, use QtCore.QTimer
.
- Handle Events by Overriding: Implement custom behavior for specific events by overriding the relevant event methods (e.g.,
paintEvent
, closeEvent
, mousePressEvent
).
- Use
QDialog
for Modality: Employ QtWidgets.QDialog
subclasses for creating modal windows that block interaction with the main window.
- Adopt Model/View: Use Qt's Model/View architecture (
QtWidgets.QListView
, QTableView
, QTreeView
with QAbstractItemModel
subclasses) for displaying complex data sets.
- Implement Custom Models Correctly: Ensure custom models inherit from appropriate
QAbstractItemModel
base classes and correctly implement required methods (data()
, rowCount()
, columnCount()
, headerData()
, index()
, parent()
).
- Use
QDataStream
for Binary: For efficient binary serialization and deserialization, use QtCore.QDataStream
.
- Use
QJsonDocument
for JSON: Handle JSON data parsing and generation using QtCore.QJsonDocument
.
- Use
QPainter
for Custom Drawing: When standard widgets aren't sufficient, use QtGui.QPainter
within a widget's paintEvent
to draw custom graphics.
- Avoid Excessive Widget Creation/Deletion: Reusing widgets and updating their content is generally more performant than constantly creating and destroying them.
III. GUI Design & Widgets
- Design Responsive Layouts: Ensure layouts adapt gracefully to window resizing and orientation changes.
- Configure
QSizePolicy
: Correctly set the sizePolicy
property for widgets to control how they grow and shrink within layouts.
- Provide Visual Feedback: Implement visual cues (e.g., changing button state, showing spinners, progress bars) to indicate that an action is in progress.
- Validate User Input: Provide immediate feedback (e.g., changing border color, showing error icons/text) when user input is invalid.
- Implement Keyboard Shortcuts: Define keyboard shortcuts for frequently used actions, aligning with OS conventions.
- Ensure Logical Focus Order: Configure the tab order (
setTabOrder
) so users can navigate interactive elements logically using the keyboard.
- Provide Clear Tooltips: Write concise and informative tooltips for all interactive UI elements, especially icons.
- Use Standard System Dialogs: Employ
QtWidgets.QFileDialog
, QMessageBox
, QFontDialog
, etc., for common tasks like opening/saving files or showing messages.
- Support High DPI: Ensure the application scales correctly on high-resolution displays.
- Minimize Interaction Steps: Streamline user workflows to reduce the number of clicks or steps required to perform common tasks.
- Implement Undo/Redo: Provide an undo/redo mechanism for significant user actions where applicable.
- Prefer Composition: Build complex custom UI elements by composing multiple standard Qt widgets within a custom widget class.
- Design for Theming: Structure the UI and QSS to make it easy to apply different visual themes.
IV. Architecture (Python & Qt)
- Separate Core Logic from GUI: Strictly separate the application's business logic, data models, and agent code from the PyQt6 UI code. Business logic classes should not depend on PyQt6.
- Use Signals for Communication: Use signals and slots as the primary mechanism for communication between decoupled core logic components and the GUI.
- Centralize State: The application's core state and data models should reside in non-GUI classes, managed independently of the visual representation.
- Implement a Presentation Layer: Introduce a layer (e.g., Presenters, ViewModels) that translates data from core models into a format suitable for the UI and handles UI-initiated actions by interacting with core logic.
- Dependency Injection: Use dependency injection to manage dependencies between components, making them easier to mock and test.
- Design for Testability: Structure components so they can be easily instantiated, configured, and tested programmatically without needing a running GUI.
- Logical Project Structure: Organize the codebase directory structure reflecting the architectural layers (e.g.,
core/
, ui/
, models/
, services/
, agents/
).
V. Concurrency & Threading
- No Blocking on Main Thread: Operations that take a significant amount of time (file I/O, network requests, complex calculations, model inference) must be performed in background threads.
- Use QThread Correctly: If using
QtCore.QThread
, use the "worker object" pattern: create a QObject
subclass for the task and moveToThread()
it to the QThread
instance, rather than subclassing QThread
itself.
- Use Python's
threading
: Python's native threading
module can also be used for background tasks, but thread-safe communication back to the main thread is still critical (signals/slots are the safest way in Qt).
- Signals for Thread Communication: Always use signals (
QtCore.Signal
) to emit progress updates or results from background threads back to the main thread to be received by slots connected to GUI elements.
- Thread-Safe Data Access: Use appropriate synchronization primitives (e.g.,
threading.Lock
, threading.RLock
, threading.Semaphore
) when multiple threads need to access or modify shared data structures.
- Avoid Thread Pools for GUI Updates: While thread pools (
concurrent.futures.ThreadPoolExecutor
) are useful for general tasks, direct GUI updates from within pool threads are prohibited. Use signals.
- Handle Thread Termination: Implement mechanisms for safely stopping background threads when the application exits or the task is canceled.
- Thread-Safe Qt Modules: Be aware that only specific Qt modules are thread-safe (e.g.,
QtCore.QMutex
, QtCore.QSemaphore
). Most GUI classes (QtWidgets
) are not.
VI. Resource Management
- Set
QObject
Parents: Ensure that all QObject
subclasses that are part of an object hierarchy have a parent set, so they are automatically cleaned up when the parent is deleted.
- Use
deleteLater()
: For QObject
subclasses that do not have a parent or whose lifespan is not tied to a parent widget, use object.deleteLater()
to schedule deletion in the event loop after pending events are processed. Avoid del object
.
- Explicitly Close Non-Qt Resources: Always explicitly close files, database connections, network sockets, etc., using
close()
or with
statements.
- Handle
None
Gracefully: Check for None
or use techniques like the null object pattern to avoid AttributeError
when dealing with potentially missing objects.
- Manage Database Connections: Use connection pooling or ensure database connections are opened and closed appropriately, ideally within context managers.
VII. Testing
- Test Business Logic Separately: Write comprehensive unit tests for core application logic that does not rely on the PyQt6 GUI.
- Use
pytest-qt
: Utilize the pytest-qt
plugin for testing PyQt6 specific code, including widgets, signals, and dialogs.
- Write Integration Tests: Implement tests that verify the correct interaction and data flow between different decoupled components (e.g., a Presenter and a core Model).
- Implement GUI Tests: Write tests that simulate user interaction with the GUI (e.g., clicking buttons, typing text) using
pytest-qt
's tools or external GUI testing frameworks.
- Mock External Dependencies: Use mocking libraries (e.g.,
unittest.mock
) to isolate code under test from external services (API calls, database, file system).
- Regression Tests: Create a specific test for every bug fix to ensure it doesn't reappear in the future.
- Maintain Code Coverage: Aim for and monitor a target code coverage percentage for tests.
- Easy Test Setup: Ensure the test environment is simple to set up and run for any developer.
VIII. Internationalization (i18n) & Localization (l10n)
- Wrap All User Strings: All user-visible strings within
QObject
subclasses must be wrapped using self.tr("Your text here")
.
- Use Translation Tools: Use the PyQt6/PySide6 translation tools (
pylupdate6
, lrelease
) to manage translation files (.ts
, .qm
).
- Load Translators: Load the appropriate compiled translation (
.qm
) files using QtCore.QTranslator
based on the application's configured language.
- Support Runtime Language Switching: While a restart is simplest, design the UI update logic to support changing the language preference at runtime and reloading translations.
IX. Styling
- Prefer Qt Style Sheets (QSS): Use QSS as the primary method for customizing the visual appearance of widgets.
- Organize QSS: Store QSS rules in separate files (.qss) rather than embedding them directly in Python code.
- Use Object Names and Classes: Leverage widget
objectName
and dynamic properties to create specific and maintainable QSS rules.
- Avoid Hardcoding Style: Do not hardcode style properties directly in Python code if they can be managed more flexibly via QSS.
- Design for Multiple Themes: Structure QSS files to support easy switching between different visual themes (e.g., dark/light mode).
X. Error Handling & Logging
- Catch Specific Exceptions: Avoid overly broad
try...except
blocks. Catch only the specific exceptions you expect and know how to handle.
- Log Errors: Use a robust logging framework (e.g., Python's
logging
module) to record errors, warnings, and important information with configurable levels.
- Handle API/Network Errors Gracefully: Implement clear error handling for external API calls or network requests, providing informative feedback to the user.
- Validate Data: Sanitize and validate all external data (user input, API responses, file content) to prevent unexpected errors or security issues.
- Report Critical Errors: For unrecoverable errors, provide a mechanism for the user to report the issue, ideally including relevant log snippets.
- Disconnect on Error: If a connected signal/slot interaction can fail critically, consider disconnecting temporarily or implementing robust error handling within the slot.
XI. Deployment & Packaging
- Use Packaging Tools: Utilize standard tools like PyInstaller or cx_Freeze to create standalone executables.
- Include Dependencies: Ensure the packaging process correctly bundles all necessary Python libraries, PyQt6 dependencies, Qt plugins (image formats, platforms), and resource files (translations, QSS, icons).
- Include Translation Files: Make sure compiled
.qm
translation files are correctly located relative to where the application expects them.
- Sign Executables: For distribution on platforms like macOS and Windows, sign the application bundle or executable for trusted installation.
- Provide Clear Instructions: Include documentation on how to install, uninstall, and potentially update the application.
- Manage Qt Plugins: Be aware of required Qt plugins (e.g.,
platforms/
, imageformats/
) and ensure they are correctly included in the bundled application.
- Cross-Platform Testing: Test the packaged application on all target operating systems to identify platform-specific issues.