关于 pyqt6-ui-development-rules
为 PyQt6 桌面 GUI 开发提供规范的技能,面向构建、重构和调试 Qt 应用的开发者。重点涵盖 MVC、线程、主题与跨平台布局。
主要功能
- 01MVC 分层与信号槽通信
- 02QSS 应用级主题配置
- 03QThread 与线程池后台任务
- 04布局管理器构建响应式界面
- 05跨平台 DPI 与无障碍支持
适用场景
- 01搭建新的 PyQt6 桌面应用
- 02重构现有 PyQt 或 PySide 代码
- 03排查界面冻结与无响应
- 04实现自定义控件和复杂布局
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ oimiragieo/ agent-studio --skill pyqt6-ui-development-rules安装后在 Claude Code 中输入/pyqt6-ui-development-rules确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
commands/pyqt6-ui-development-rules.mdhooks/post-execute.cjspre-execute.cjsreferences/research-requirements.mdrules/pyqt6-ui-development-rules.mdschemas/input.schema.jsonoutput.schema.jsonscripts/main.cjstemplates/implementation-template.md详细介绍
PyQt6 UI 开发规则技能
PyQt6 桌面 GUI 开发专家,负责落实 MVC 分离、信号/槽架构、QSS 主题设置、线程并发和跨平台渲染最佳实践。确保桌面应用响应灵敏、易于访问且视觉风格一致。 - 设计采用 MVC 分离的 PyQt6 应用架构 - 实现 UI 与业务逻辑之间的信号/槽通信模式 - 配置应用级 QSS 主题,支持深色/浅色模式 - 使用 QThread、QRunnable 和 QThreadPool 管理后台操作 - 使用 QVBoxLayout、QHBoxLayout、QGridLayout 和 QFormLayout 构建自适应布局 - 实现自定义 QWidget 子类,并正确处理 paintEvent - 设置支持跨平台的 DPI 感知渲染 - 配置无障碍功能(屏幕阅读器支持、键盘导航)概述
此技能为构建可用于生产环境的 PyQt6 桌面应用制定规则。核心原则是:通过信号/槽严格分离 MVC、绝不阻塞 UI 线程、通过 QSS 集中管理主题,以及使用布局管理器驱动自适应设计。这些规则可以避免最常见的 PyQt6 问题:UI 冻结、难以测试的耦合,以及特定平台的渲染错误。
适用场景
- 构建新的 PyQt6 桌面应用
- 将现有 PyQt/PySide 代码重构为 PyQt6
- 调试冻结或无响应的 Qt UI
- 实现自定义控件或复杂布局
- 设置跨平台桌面应用构建
铁律
- 始终使用 Qt 的信号/槽机制进行 UI 与逻辑之间的通信——UI 层与业务逻辑层之间直接调用方法会破坏 MVC 分离,并造成难以测试的耦合。
- 绝不在主 UI 线程上执行耗时操作——阻塞 Qt 事件循环会导致界面无响应,并触发操作系统的“未响应”对话框。
- 始终在 QApplication 级别应用 QSS 样式表,而不是逐个控件应用——控件级内联样式会导致主题不一致,并使样式管理变得杂乱且难以维护。
- 绝不使用绝对像素坐标布置控件——应使用 Qt 布局管理器(QVBoxLayout、QHBoxLayout、QGridLayout),以确保 DPI 感知和跨平台渲染。
- 始终在发布前于所有目标平台上测试 UI——PyQt6 的渲染、字体缩放和控件尺寸在 Windows、macOS 和 Linux 之间存在差异。
反模式
| 反模式 | 失败原因 | 正确做法 |
|---|---|---|
| 从 UI 槽函数直接调用业务逻辑 | UI 与逻辑耦合;无法测试,并破坏 MVC 架构 | 从 UI 发出信号;通过槽函数连接到控制器/服务方法 |
| 在主线程中执行网络或文件 I/O | 阻塞 Qt 事件循环;操作完成前 UI 会冻结 | 使用 QThread、QRunnable,或使用 qasync 的 asyncio 执行后台操作 |
| 硬编码像素尺寸和位置 | 在高 DPI 显示器和不同 OS DPI 缩放设置下会失效 | 使用布局管理器和尺寸策略;使用 logicalDpiX() 实现 DPI 感知的尺寸设置 |
| 为单个控件设置内联样式 | 导致视觉不一致;主题化或维护极其困难 | 在 QApplication 级别定义统一的 QSS 样式表,并使用对象名称/类 |
| 忽略跨平台渲染差异 | Windows/macOS/Linux 上的控件尺寸、字体和边距差异很大 | 在所有目标平台上测试;在渲染存在差异的地方使用平台条件逻辑 |
工作流
步骤 1:应用架构(MVC)
# model.py -- Business logic, no Qt dependencies
class DataModel:
def __init__(self):
self._items = []
def add_item(self, item: str) -> bool:
if item and item not in self._items:
self._items.append(item)
return True
return False
# controller.py -- Mediates between Model and View
from PyQt6.QtCore import QObject, pyqtSignal
class Controller(QObject):
items_changed = pyqtSignal(list)
error_occurred = pyqtSignal(str)
def __init__(self, model: DataModel):
super().__init__()
self._model = model
def add_item(self, item: str) -> None:
if self._model.add_item(item):
self.items_changed.emit(self._model._items.copy())
else:
self.error_occurred.emit(f"Could not add: {item}")
步骤 2:信号/槽连接
# view.py -- UI only, connects via signals/slots
from PyQt6.QtWidgets import QMainWindow, QVBoxLayout, QWidget, QLineEdit, QPushButton, QListWidget
class MainView(QMainWindow):
def __init__(self, controller: Controller):
super().__init__()
self._controller = controller
# Wire signals to slots
self._controller.items_changed.connect(self._on_items_changed)
self._controller.error_occurred.connect(self._on_error)
# UI emits to controller -- never calls model directly
self._add_btn.clicked.connect(lambda: self._controller.add_item(self._input.text()))
def _on_items_changed(self, items: list) -> None:
self._list.clear()
self._list.addItems(items)
步骤 3:后台操作
from PyQt6.QtCore import QThread, pyqtSignal
class WorkerThread(QThread):
progress = pyqtSignal(int)
finished_with_result = pyqtSignal(object)
error = pyqtSignal(str)
def __init__(self, task_fn, parent=None):
super().__init__(parent)
self._task_fn = task_fn
def run(self):
try:
result = self._task_fn(self.progress.emit)
self.finished_with_result.emit(result)
except Exception as e:
self.error.emit(str(e))
步骤 4:QSS 主题设置
# Apply at QApplication level
app = QApplication(sys.argv)
app.setStyleSheet(Path("styles/dark-theme.qss").read_text())
# QSS file
"""
QMainWindow {
background-color: #2b2b2b;
color: #e0e0e0;
}
QPushButton {
background-color: #3c3f41;
border: 1px solid #555;
border-radius: 4px;
padding: 6px 16px;
color: #e0e0e0;
}
QPushButton:hover {
background-color: #4c5052;
}
"""
步骤 5:布局管理
# Use layout managers -- never setGeometry() or move()
layout = QVBoxLayout()
layout.addWidget(self._toolbar)
layout.addWidget(self._content, stretch=1) # stretch fills available space
layout.addWidget(self._status_bar)
# For responsive grids
grid = QGridLayout()
grid.addWidget(label, 0, 0)
grid.addWidget(input_field, 0, 1)
grid.setColumnStretch(1, 1) # input stretches, label stays fixed
互补技能
| 技能 | 关联 |
|---|---|
modern-python | 使用 uv、ruff、ty、pytest 设置项目 |
python-backend-expert | 桌面应用后端的后端服务模式 |
tdd | Qt 控件测试的测试驱动开发 |
accessibility | 适用于桌面应用的无障碍审查模式 |
记忆协议(强制)
开始前:
阅读 .claude/context/memory/learnings.md,了解此前的 PyQt6 模式和平台特定解决方法。
完成后: 将任何平台特定的渲染问题、信号/槽模式或 QThread 注意事项记录到 .claude/context/memory/learnings.md。
假设会发生中断:你的上下文可能会重置。如果没有记录在记忆中,就等于没有发生。
评论
支持 Markdown