| name | pyqt6-patterns |
| description | Best practices and patterns for building robust PyQt6 desktop applications |
PyQt6 Patterns Skill
Guide for building Desktop applications with PyQt6, focusing on architecture, threading, and user experience.
🏗️ Architecture Pattern
Use a model that separates UI and Logic:
- MainWindow: Manages UI, Layout, Signals.
- Worker Thread (QThread): Handles long-running tasks (IO, Network, Heavy computation).
- Core Logic: Pure Python functions, independent of GUI.
Example Structure main.py
from PyQt6.QtWidgets import ...
from PyQt6.QtCore import QThread, pyqtSignal
class WorkerThread(QThread):
progress = pyqtSignal(int, str)
finished = pyqtSignal(object)
error = pyqtSignal(str)
def run(self):
try:
pass
except Exception as e:
self.error.emit(str(e))
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setup_ui()
def start_task(self):
self.thread = WorkerThread(...)
self.thread.progress.connect(self.on_progress)
self.thread.finished.connect(self.on_finished)
self.thread.start()
🧵 Threading (Critical)
Inviolable Rule: NEVER run heavy tasks on the Main Thread.
Why?
- Blocks Main Thread -> UI freezes (Not Responding).
- On macOS: Causes "Beachball of death".
Standard Pattern
Use QThread:
- Create a class inheriting from
QThread.
- Define Signals (
pyqtSignal) to communicate back to Main Thread.
- Override
run() method.
- Initialize and keep reference to thread (
self.thread) in MainWindow.
- Connect signals and call
start().
🎨 UI & Layouts
Layout Hierarchy
Always use Layouts for responsive UI:
QMainWindow
└── CentralWidget (QWidget)
└── QVBoxLayout
├── QGroupBox ("Input")
│ └── QFormLayout
├── QGroupBox ("Settings")
│ └── QVBoxLayout
└── QGroupBox ("Actions")
└── QHBoxLayout
Styles
Use Fusion style for a clean cross-platform look:
app = QApplication(sys.argv)
app.setStyle("Fusion")
⚠️ Error Handling
Pattern: Try-Except-Signal
In Worker Thread, always use try-except and emit error signal:
def run(self):
try:
do_work()
except Exception as e:
self.error.emit(str(e))
```
In UI, listen for signal and show MessageBox:
```python
def on_error(self, message):
self.btn_start.setEnabled(True)
QMessageBox.critical(self, "Error", message)
📝 Widget Common Patterns
File Browsing
path = QFileDialog.getExistingDirectory(self, "Select Folder")
if path:
self.input_dir = path
self.label.setText(path)
Progress Bar
- Unknown duration:
progressBar.setRange(0, 0)
- Known duration:
emit(percent) from thread -> progressBar.setValue(percent)
Logs Display
Use QTextEdit readonly to display realtime logs:
self.log_text = QTextEdit()
self.log_text.setReadOnly(True)
self.log_text.append(message)
✅ Best Practices Checklist
🤖 Agentic Protocol
Skill Metadata
- Version: 1.0.0
- Last Updated: 2026-01-27
1. Activation Log
When activating this skill (generating code), print:
"🎯 [SKILL ACTIVATED] pyqt6-patterns v1.0.0"
"📋 Parameters:"
" - Component: [MainWindow|WorkerThread|Dialog]"
" - Pattern Applied: [Threading|Layout|Signal-Slot]"
2. User Confirmation
Before applying major architectural changes:
"I'm implementing the [Pattern Name] pattern for [Component]. This will structure the code as [Description]. Proceed?"
3. Completion Log
- Success: "✅ [pyqt6-patterns] Implementation ready. Validated imports and signals."
- Warning: "⚠️ [pyqt6-patterns] Note: Ensure [dependency] is installed."