pyside6-reviewer

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

PySide6 Code Reviewer

PySide6代码审查专家

Expert code review for modern PySide6/Qt 6.8+ applications.
针对现代PySide6/Qt 6.8+应用的专业代码审查服务。

Review Process

审查流程

  1. Identify Qt version assumptions — Verify code targets Qt 6.8+ (no Qt5 compat)
  2. Check thread safety — All GUI operations on main thread, proper worker patterns
  3. Validate signal/slot usage — Modern connection syntax, proper signatures
  4. Assess Model/View implementation — Role usage, data method patterns, index validity
  5. Review resource management — Parent-child ownership, prevent leaks
  6. Evaluate async patterns — QThread, QtConcurrent, asyncio integration
  7. Check QML integration — Property bindings, type registration, context exposure
  1. 确认Qt版本适配 — 验证代码目标为Qt 6.8+(无Qt5兼容代码)
  2. 检查线程安全 — 所有GUI操作在主线程执行,采用正确的工作线程模式
  3. 验证信号/槽使用 — 现代连接语法,正确的签名匹配
  4. 评估Model/View实现 — 角色使用、数据方法模式、索引有效性
  5. 审查资源管理 — 父子对象所有权,防止内存泄漏
  6. 评估异步模式 — QThread、QtConcurrent、asyncio集成
  7. 检查QML集成 — 属性绑定、类型注册、上下文暴露

Critical Anti-Patterns (Always Flag)

严重反模式(必须标记)

python
undefined
python
undefined

WRONG: GUI operation from worker thread

WRONG: GUI operation from worker thread

class Worker(QThread): def run(self): self.label.setText("Done") # CRASH: Cross-thread GUI access
class Worker(QThread): def run(self): self.label.setText("Done") # CRASH: Cross-thread GUI access

WRONG: Blocking the event loop

WRONG: Blocking the event loop

def on_button_click(self): time.sleep(5) # FREEZES UI requests.get(url) # FREEZES UI
def on_button_click(self): time.sleep(5) # FREEZES UI requests.get(url) # FREEZES UI

WRONG: Old-style signal connection (Qt4/5 legacy)

WRONG: Old-style signal connection (Qt4/5 legacy)

self.connect(button, SIGNAL("clicked()"), self.handler)
self.connect(button, SIGNAL("clicked()"), self.handler)

WRONG: String-based slot connection

WRONG: String-based slot connection

button.clicked.connect("self.handler") # Should be callable
button.clicked.connect("self.handler") # Should be callable

WRONG: No parent = memory leak risk

WRONG: No parent = memory leak risk

label = QLabel("text") # Should have parent or be assigned to layout
label = QLabel("text") # Should have parent or be assigned to layout

WRONG: Deleting QObject while signals pending

WRONG: Deleting QObject while signals pending

obj.deleteLater() # OK del obj # WRONG if signals/slots active
obj.deleteLater() # OK del obj # WRONG if signals/slots active

WRONG: Direct widget manipulation in QThread.run()

WRONG: Direct widget manipulation in QThread.run()

class BadWorker(QThread): def run(self): self.progress_bar.setValue(50) # Thread violation!
undefined
class BadWorker(QThread): def run(self): self.progress_bar.setValue(50) # Thread violation!
undefined

Modern Patterns (Require These)

现代编程模式(推荐使用)

Signal/Slot Connections

信号/槽连接

python
undefined
python
undefined

Qt 6 style - always use this

Qt 6 style - always use this

button.clicked.connect(self.on_click) button.clicked.connect(lambda: self.handler(arg))
button.clicked.connect(self.on_click) button.clicked.connect(lambda: self.handler(arg))

Typed signals with modern syntax

Typed signals with modern syntax

class Worker(QObject): progress = Signal(int) # Single type result = Signal(str, list) # Multiple types error = Signal(Exception) # Exception passing finished = Signal() # No arguments
undefined
class Worker(QObject): progress = Signal(int) # Single type result = Signal(str, list) # Multiple types error = Signal(Exception) # Exception passing finished = Signal() # No arguments
undefined

Thread-Safe Worker Pattern

线程安全的工作线程模式

python
class Worker(QObject):
    finished = Signal()
    progress = Signal(int)
    result = Signal(object)
    error = Signal(str)

    @Slot()
    def run(self):
        try:
            for i in range(100):
                # Do work
                self.progress.emit(i)
            self.result.emit(data)
        except Exception as e:
            self.error.emit(str(e))
        finally:
            self.finished.emit()
python
class Worker(QObject):
    finished = Signal()
    progress = Signal(int)
    result = Signal(object)
    error = Signal(str)

    @Slot()
    def run(self):
        try:
            for i in range(100):
                # Do work
                self.progress.emit(i)
            self.result.emit(data)
        except Exception as e:
            self.error.emit(str(e))
        finally:
            self.finished.emit()

Usage

Usage

thread = QThread() worker = Worker() worker.moveToThread(thread) thread.started.connect(worker.run) worker.finished.connect(thread.quit) worker.finished.connect(worker.deleteLater) thread.finished.connect(thread.deleteLater) thread.start()
undefined
thread = QThread() worker = Worker() worker.moveToThread(thread) thread.started.connect(worker.run) worker.finished.connect(thread.quit) worker.finished.connect(worker.deleteLater) thread.finished.connect(thread.deleteLater) thread.start()
undefined

Async Integration (Qt 6.8+)

异步集成(Qt 6.8+)

python
import asyncio
from PySide6.QtAsyncio import QAsyncioEventLoopPolicy
python
import asyncio
from PySide6.QtAsyncio import QAsyncioEventLoopPolicy

Set up asyncio with Qt event loop

Set up asyncio with Qt event loop

asyncio.set_event_loop_policy(QAsyncioEventLoopPolicy())
class AsyncWidget(QWidget): async def fetch_data(self): async with aiohttp.ClientSession() as session: async with session.get(url) as response: return await response.json()
def start_fetch(self):
    asyncio.ensure_future(self.fetch_data())
undefined
asyncio.set_event_loop_policy(QAsyncioEventLoopPolicy())
class AsyncWidget(QWidget): async def fetch_data(self): async with aiohttp.ClientSession() as session: async with session.get(url) as response: return await response.json()
def start_fetch(self):
    asyncio.ensure_future(self.fetch_data())
undefined

Detailed Reference Files

详细参考文档

  • references/signals-slots.md — Signal/slot patterns, connection types, thread-safe emission
  • references/model-view.md — QAbstractItemModel, roles, proxies, delegates
  • references/threading.md — QThread, QtConcurrent, async patterns, thread pools
  • references/widgets.md — Widget lifecycle, layouts, styling, high-DPI
  • references/qml-integration.md — QML/Python bridge, properties, type registration
  • references/performance.md — Paint optimization, model efficiency, lazy loading
  • references/anti-patterns.md — Comprehensive anti-pattern catalog with fixes
  • references/signals-slots.md — 信号/槽模式、连接类型、线程安全发射
  • references/model-view.md — QAbstractItemModel、角色、代理、委托
  • references/threading.md — QThread、QtConcurrent、异步模式、线程池
  • references/widgets.md — 组件生命周期、布局、样式、高DPI适配
  • references/qml-integration.md — QML/Python桥接、属性、类型注册
  • references/performance.md — 绘制优化、模型效率、懒加载
  • references/anti-patterns.md — 完整的反模式目录及修复方案

Review Checklist (Use for PRs)

PR审查检查清单(用于PR审查)

Thread Safety

线程安全

  • All widget/GUI calls on main thread only
  • Workers use signal/slot for UI updates
  • No
    time.sleep()
    or blocking calls in main thread
  • QThread
    subclass only overrides
    run()
    , not constructor work
  • moveToThread()
    used correctly (object has no parent)
  • 所有组件/GUI调用仅在主线程执行
  • 工作线程通过信号/槽更新UI
  • 主线程中无
    time.sleep()
    或阻塞调用
  • QThread
    子类仅重写
    run()
    方法,不在构造函数中执行任务
  • moveToThread()
    使用正确(对象无父级)

Memory Management

内存管理

  • Widgets have parents OR are added to layouts
  • deleteLater()
    used for QObjects, never
    del
  • Lambda connections don't capture stale references
  • Circular signal connections avoided
  • Model data returned by value, not reference
  • 组件有父级 或 已添加到布局中
  • QObjects使用
    deleteLater()
    ,绝不使用
    del
  • Lambda连接未捕获过期引用
  • 避免循环信号连接
  • 模型数据通过值返回,而非引用

Signal/Slot Correctness

信号/槽正确性

  • Modern connection syntax (no strings)
  • Signal signatures match slot signatures
  • @Slot()
    decorator on all slots
  • Qt.QueuedConnection
    for cross-thread signals
  • Signals defined as class attributes
  • 使用现代连接语法(无字符串)
  • 信号签名与槽签名匹配
  • 所有槽函数使用
    @Slot()
    装饰器
  • 跨线程信号使用
    Qt.QueuedConnection
  • 信号定义为类属性

Model/View

Model/View

  • beginInsertRows()
    /
    endInsertRows()
    bracket changes
  • dataChanged
    emitted for updates
  • index.isValid()
    checked before access
  • Custom roles use
    Qt.UserRole + n
  • roleNames()
    override for QML compatibility
  • 数据变更使用
    beginInsertRows()
    /
    endInsertRows()
    包裹
  • 更新时发射
    dataChanged
    信号
  • 访问前检查
    index.isValid()
  • 自定义角色使用
    Qt.UserRole + n
  • 重写
    roleNames()
    以兼容QML

Performance

性能

  • No widget creation in paint events
  • update()
    not
    repaint()
    for redraws
  • Large lists use QAbstractItemModel (not QListWidget)
  • Lazy loading for expensive data
  • blockSignals()
    during batch updates
  • 绘制事件中不创建组件
  • 重绘使用
    update()
    而非
    repaint()
  • 大型列表使用QAbstractItemModel(而非QListWidget)
  • 昂贵数据采用懒加载
  • 批量更新时使用
    blockSignals()

Qt 6.8+ Specifics

Qt 6.8+特性适配

  • Using new enum scoping (
    Qt.AlignmentFlag.AlignCenter
    )
  • QtAsyncio
    for async patterns (not third-party)
  • QML_ELEMENT
    macro equivalents for type registration
  • Property bindings via
    QProperty
    where appropriate
  • New
    QPermission
    API for platform permissions
  • 使用新的枚举作用域(如
    Qt.AlignmentFlag.AlignCenter
  • 采用
    QtAsyncio
    实现异步模式(而非第三方库)
  • 使用
    QML_ELEMENT
    宏等效方式进行类型注册
  • 适当场景下通过
    QProperty
    实现属性绑定
  • 使用新的
    QPermission
    API处理平台权限