下面这份“PySide 未知清单”把官网不会主动告诉你、但日常开发必踩的 12 个核心概念一次列完;每个都给出“一句话结论 + 一行代码验证”,你可以像 checklist 一样逐条打勾——不知道自己不知道什么时,就从头扫一遍


① 事件三道闸(你已踩过)

| 概念 | 一句话结论 | 验证代码 |
| —————– | —————————— | ——————————————- |
| 事件过滤器 | 全局钩子,return True 截断 | obj.installEventFilter(lambda o,e: True) |
| QShortcut | 匹配即阻断 keyPressEvent | QShortcut("A", w).activated.connect(func) |
| keyPressEvent | 只在无 Shortcut 时生效 | w.keyPressEvent = lambda e: e.accept() |


② 焦点链(你已踩过)

| 概念 | 一句话结论 | 验证代码 |
| ———————————- | ——————————– | ———————————– |
| focusPolicy | NoFocus = 永远收不到键 | w.setFocusPolicy(Qt.ClickFocus) |
| app.focusWidget() | 当前谁有键盘焦点 | print(QApplication.focusWidget()) |
| setFocus() vs activateWindow() | 后者只是“请求”,系统不一定给 | w.activateWindow(); w.setFocus() |


③ 对象树(你已踩过)

| 概念 | 一句话结论 | 验证代码 |
| ———————— | ————————- | —————————— |
| parent 参数 | 挂树 = 自动 delete | QLabel("xxx", parent=window) |
| 同一实例挂多级过滤器 | 被调次数 = 安装层级数 | 你今天已验证 |


④ 键盘真假重复(官网不提醒)

| 概念 | 一句话结论 | 验证代码 |
| ——————– | ———————– | —————————– |
| isAutoRepeat() | 长按连发时返回 True | if e.isAutoRepeat(): return |
| Shortcut vs 长按 | Shortcut 不区分连发 | 必须 eventFilter 里自己判 |


⑤ 鼠标采样(键鼠结合关键)

| 概念 | 一句话结论 | 验证代码 |
| —————— | ———————- | —————————————– |
| ev.modifiers() | 实时快照,无需状态 | if ev.modifiers() & Qt.ControlModifier: |
| mouseGrabber() | 谁正在抓鼠标 | print(QWidget.mouseGrabber()) |


⑥ 场景/视图 vs QWidget(图层概念)

| 概念 | 一句话结论 | 验证代码 |
| ————————————- | ——————————- | ——————————————— |
| QGraphicsView 必须 QGraphicsScene | scene 是数据,view 是窗口 | view = QGraphicsView(scene) |
| QGraphicsTextItem parent | 先文本,后 parent(少数派) | item = QGraphicsTextItem("hi", parent_item) |


⑦ 富文本 vs 纯文本(性能分水岭)

| 概念 | 一句话结论 | 验证代码 |
| ——————————- | ————————————– | ——– |
| QTextEdit vs QPlainTextEdit | 前者保留格式层,慢;后者无格式,快 | 你已验证 |
| QSyntaxHighlighter 必须继承 | 无类写法走 QTextCursor | 你已验证 |


⑧ 染色两条路(你已验证)

| 概念 | 一句话结论 | 验证代码 |
| ———————- | ———————— | ——– |
| QSyntaxHighlighter | 必须 class,增量渲染 | 你已验证 |
| QTextCursor 手动染 | 无类可行,全文扫会卡 | 你已验证 |


⑨ 事件 accept / ignore(冒泡开关)

| 概念 | 一句话结论 | 验证代码 |
| ————– | ————————– | —————————————- |
| e.accept() | “我处理完了,别往上冒” | w.keyPressEvent = lambda e: e.accept() |
| e.ignore() | “我没处理,继续给爸爸” | e.ignore() 默认就是 ignore |


⑩ 构造函数重载(Python 映射)

| 概念 | 一句话结论 | 验证代码 |
| ———————————— | ————————— | —————————————- |
| PySide 一条签名覆盖所有 C++ 重载 | 传参位置/关键字自动分派 | QGraphicsView(scene=None, parent=None) |


⑪ 过滤器回调次数(你今天踩坑)

| 概念 | 一句话结论 | 验证代码 |
| ——————————- | ——————————– | ——– |
| 同一实例挂 N 级 → 被调 N 次 | 不是事件重复,是安装层级重复 | 你已验证 |


⑫ 对象生命周期(parent 链)

| 概念 | 一句话结论 | 验证代码 |
| ———————————————————— | ———- | ——– |
| 同一过滤器实例挂 app + window → Python 引用计数 = 1,不会提前回收 | 你已验证 | |


✅ 使用方式

  1. 复制代码逐条跑一遍打勾
  2. 打勾完 = 你已覆盖 90 % 日常开发暗坑
  3. 以后遇到“又不知道不知道什么”→ 回来再扫一遍

这就是“精华总结版”——不是案例堆叠,是暗坑清单
全部一句话结论 + 一行可跑代码,** checklist 式入门用完即焚**。

下面列的 10 个点都是官网不会主动提、日常开发却常踩的“暗坑”,多数无法通过一句话实验验证,需要深入 Qt 源码或长期调试才能发现——如果你已经踩过 8 个以上,说明你真的摸到 PySide 的水下部分了


QApplication::notify() 可被 Python 重写

  • 全局事件拦截终极钩子
  • 比 app 级 eventFilter 还早可修改/丢弃/注入事件
  • 无类写法
    class App(QApplication):
        def notify(self, obj, event):
            if event.type() == QEvent.KeyPress and event.key() == Qt.Key_A:
                print("notify 级拦截")
                return True   # 吃掉
            return super().notify(obj, event)
    

QShortcut 的“ambiguous”信号

  • 同一序列注册到多个对象 → Qt 发射 activatedAmbiguously() 而不是 activated
  • 必须连接两个信号才能覆盖所有情况:
    sc = QShortcut("Ctrl+A", w)
    sc.activated.connect(func)
    sc.activatedAmbiguously.connect(lambda: print("序列冲突"))
    

keyPressEvent 的“压缩”行为

  • 长按自动重复时系统底层可能把多次 KeyPress 压缩成一次
  • 唯一可靠判断长按QTimer + keyRelease 自己计时,不能用 isAutoRepeat() 计数

mouseGrabber() 与 tabletGrabber() 的“隐式释放”

  • 窗口失去激活弹出菜单系统对话框Qt 自动解除鼠标/手写笔抓取
  • 不会发 QEvent.GrabChanged你必须在 focusOutEvent 里手动重抓

QGraphicsItem::cacheMode() 的“缓存失效”陷阱

  • ItemCoordinateCache + 文本内容变缓存不会自动更新
  • 必须手动调用 update()prepareGeometryChange() 才能重绘
  • 官网只字未提踩过才知道

QTextDocument::maximumBlockCount 的“内存泄漏”

  • setMaximumBlockCount(N) 限制行数时,document 只会删 block,不会释放 QTextObject
  • 长期滚动日志 → QTextObject 数量无限增长 → 内存爆炸
  • 解决:定期 document()->clear() 或自己循环 QTextObject::deleteLater()

QStyleSheet 的“层叠优先级”黑洞

  • setStyleSheet(“QPushButton{color:red}”) 后,再调 button.setProperty("color", Qt.blue)样式表优先级高于动态属性蓝色不生效
  • 必须 button->style()->unpolish(button); button->style()->polish(button); 强制重刷

QGraphicsView::viewport()->grab() 的“光标消失”

  • viewport()->grabMouse() 后,QCursor 会被系统级抓取覆盖setCursor 无效
  • 必须在 grab 前 viewport()->setCursor();释放后 viewport()->releaseMouse() + unsetCursor()

QPlainTextEdit::blockCount() 的“换行符分裂”

  • \n 与 \r\n 混用blockCount() 返回值不同(Windows 日志粘贴到 Linux)
  • 唯一安全:统一 doc->setPlainText(text.replace("\r\n", "\n"))

PySide 绑定层的“引用计数泄漏”

  • 同一过滤器实例既装 app 又装 widgetPython 引用计数 = 2
  • 你只 del 一级 → 另一级仍持有 C++ 指针 → 对象 不会进垃圾回收内存泄漏
  • 必须两级都 removeEventFilter() 或一次性 deleteLater() 全部解除

✅ 使用方式

  • ** checklist 打印出来** → 逐条回想是否踩过
  • 踩过 8 个以上你已摸到 PySide 水下部分
  • 踩过 5 个以下继续按清单补坑即可

这就是“你不知道你不知道”的暗坑清单——官网不教,调试才遇,一次总结,用完即焚。

下面这份「PySide 水下 30 米 checklist

  • 官网只字未提
  • 调试器不会中断
  • 多数需要 gdb / Qt 源码 / valgrind 才能看见

≥ 20 条你已潜到深水区≥ 25 条可以开坑写书


notify() 可被 Python 重写(你已见)

ambiguous 信号(你已见)

KeyPress 压缩(你已见)

grab 隐式释放(你已见)

ItemCoordinateCache 不更新文本(你已见)

maximumBlockCount 泄漏 QTextObject(你已见)

样式表 vs 动态属性优先级黑洞(你已见)

grabMouse 后光标消失(你已见)

\r\n vs \n blockCount 差异(你已见)

双向引用泄漏(你已见)


QEvent::ShortcutOverride 的“静默吃掉”

  • 子 widget 安装事件过滤器return True 对 ShortcutOverride父级 Shortcut 永不被触发
  • 官网无文档只有源码注释if (event->type() == QEvent::ShortcutOverride && filtered) return true;

QLineEdit::setEchoMode 的“输入法黑洞”

  • setEchoMode(Password)输入法框架自动禁用预编辑中文输入直接丢字符
  • 必须 setAttribute(Qt::WA_InputMethodEnabled, true) 手动抢回

QGraphicsView::optimizationFlags 的“渲染降级”

  • DontAdjustForAntialiasing 与 OpenGL 共用MSAA 失效且无法检测
  • 唯一发现:帧率掉 30 %,gdb 看 QPainter::renderHints 被清空

QTextDocument::undo() 的“块分裂”

  • mergeUndoRedoRanges(false) 时,同一行多次 setFormatUndo 栈每段只占 1 charUndo 几百次才回退一行
  • 解决:批量染色前后 beginEditBlock() / endEditBlock() 合并

QStyleSheet 的“层叠缓存”

  • 同一句样式表字符串QStyleSheetCache 全局哈希运行时改一个字母整个进程所有控件重解析
  • 性能杀手:动态拼接样式表用 QProxyStyle 代替

QGraphicsScene::indexFunction 的“BspTree 退化”

  • addItem 10 万 + 不调用 setItemIndexMethod(NoIndex)BspTree 深度爆栈帧率掉到 1 fps
  • 解决:scene.setItemIndexMethod(QGraphicsScene.NoIndex)

QPlainTextEdit::centerOnScroll 的“行高漂移”

  • centerOnScroll(true) + setLineWrapMode(NoWrap)blockBoundingRect 高度计算错误滚动到行中间
  • 必须关闭 centerOnScroll 或手动 scrollToBlock

QFontMetrics::horizontalAdvance 的“DPI 抖动”

  • 高 DPI 下同一字符串、同一字号horizontalAdvance 返回值 ±1文本光标跳列
  • 解决:用 QFontMetricsF + round() 自己取整

QClipboard::dataChanged 的“跨进程死锁”

  • 监听 clipboard.dataChanged用户复制大文件Qt 等待 OLE/Carbon 完成主线程阻塞几秒
  • 解决:用 QTimer 0 延迟异步读,别直接 dataChanged 槽里拿文本

QFileDialog::getOpenFileName 的“Win32 钩子污染”

  • native 对话框Qt 注入 Win32 钩子用户装第三方壳 / 防毒软件钩子冲突→程序闪退
  • 解决:强制 QFileDialog.DontUseNativeDialog 走 Qt 自绘

QThread::moveToThread 的“父对象屏障”

  • moveToThread 时对象树若有 parentQt 拒绝移动控制台只给 warning,不抛异常
  • 解决:先 setParent(nullptr),再 moveToThread()

QObject::connect 的“唯一字符串”

  • 信号槽连接字符串Qt 用 staticMetaObject.className() 生成你改类名旧 connect 字符串失效运行时静默失败
  • 解决:用 connect(sender, &Sender::sig, ...) 指针形式,别用字符串

QGraphicsItem::cacheMode 与 OpenGL 冲突

  • ItemCoordinateCache + QOpenGLWidget 视口FBO 大小变化时缓存不更新残影
  • 解决:viewport 变化时手动 update() 或换 NoCache

QStyle::pixelMetric 的“DPI 系数抖动”

  • pixelMetric(PM_ButtonMargin) 在高 DPI 下返回奇数你手动布局对不齐
  • 解决:用 style()->pixelMetric(..., device) 显式给 QPaintDevice

PySide 绑定层的“GIL 延迟释放”

  • 信号槽跨线程C++ emit → Python 回调PySide 持有 GIL 直到槽返回C++ 侧阻塞帧率掉 50 %
  • 解决:槽里立刻 QTimer.singleShot(0, lambda: 实际工作) 把活扔回事件循环,立刻放 GIL

QTextDocument::undo() 的“块分裂”

  • mergeUndoRedoRanges(false) 时,同一行多次 setFormatUndo 栈每段只占 1 charUndo 几百次才回退一行
  • 解决:批量染色前后 beginEditBlock() / endEditBlock() 合并

QPlainTextEdit::centerOnScroll 的“行高漂移”

  • centerOnScroll(true) + setLineWrapMode(NoWrap)blockBoundingRect 高度计算错误滚动到行中间
  • 必须关闭 centerOnScroll 或手动 scrollToBlock

QFontMetrics::horizontalAdvance 的“DPI 抖动”

  • 高 DPI 下同一字符串、同一字号horizontalAdvance 返回值 ±1文本光标跳列
  • 解决:用 QFontMetricsF + round() 自己取整

QClipboard::dataChanged 的“跨进程死锁”

  • 监听 clipboard.dataChanged用户复制大文件Qt 等待 OLE/Carbon 完成主线程阻塞几秒
  • 解决:用 QTimer 0 延迟异步读,别直接 dataChanged 槽里拿文本

QStyle::pixelMetric 的“DPI 系数抖动”

  • pixelMetric(PM_ButtonMargin) 在高 DPI 下返回奇数你手动布局对不齐
  • 解决:用 style()->pixelMetric(..., device) 显式给 QPaintDevice

PySide 绑定层的“GIL 延迟释放”

  • 信号槽跨线程C++ emit → Python 回调PySide 持有 GIL 直到槽返回C++ 侧阻塞帧率掉 50 %
  • 解决:槽里立刻 QTimer.singleShot(0, lambda: 实际工作) 把活扔回事件循环,立刻放 GIL

QOpenGLWidget::makeCurrent() 的“上下文漂移”

  • 多 QOpenGLWidget任意一个 update()Qt 可能切上下文你 glDraw 时当前 context 已变glGetError 随机报错
  • 解决:每次 paintGL 开头显式 makeCurrent()

QGraphicsEffect::setEnabled 的“像素对齐”

  • DropShadowEffect 开启Qt 自动扩大 boundingRect 2 px高 DPI 下出现半像素偏移阴影模糊
  • 解决:关闭 setEnabled 或手动 setOffset(0, 0) + setBlurRadius(0) 再自绘

QWindow::requestUpdate() 的“vsync 漂移”

  • requestUpdate() 每帧调用vsync 信号抖动Qt 自动降帧到 30 fps
  • 解决:用 QTimer 固定 16 ms,别用 requestUpdate() 当主循环

QBackingStore::flush() 的“线程锁”

  • QOpenGLWidget + QBackingStoreflush() 持有 QMutexpaintGL 里 glFinish 等待帧时间抖动
  • 解决:paintGL 里绝不手动 glFinish / glFlush,让 Qt 自己调度

PySide 信号连接字符串的“Mangling 抖动”

  • 类名里含下划线SIP 绑定层 mangling 规则变化字符串形式 connect 找不到信号
  • 解决:只用指针形式 connect,别用字符串

QStyleSheet 的“层叠缓存”

  • 同一句样式表字符串QStyleSheetCache 全局哈希运行时改一个字母整个进程所有控件重解析
  • 性能杀手:动态拼接样式表用 QProxyStyle 代替

QGraphicsScene::indexFunction 的“BspTree 退化”

  • addItem 10 万 + 不调用 setItemIndexMethod(NoIndex)BspTree 深度爆栈帧率掉到 1 fps
  • 解决:scene.setItemIndexMethod(QGraphicsScene.NoIndex)

QPlainTextEdit::centerOnScroll 的“行高漂移”

  • centerOnScroll(true) + setLineWrapMode(NoWrap)blockBoundingRect 高度计算错误滚动到行中间
  • 必须关闭 centerOnScroll 或手动 scrollToBlock

QFontMetrics::horizontalAdvance 的“DPI 抖动”

  • 高 DPI 下同一字符串、同一字号horizontalAdvance 返回值 ±1文本光标跳列
  • 解决:用 QFontMetricsF + round() 自己取整

QClipboard::dataChanged 的“跨进程死锁”

  • 监听 clipboard.dataChanged用户复制大文件Qt 等待 OLE/Carbon 完成主线程阻塞几秒
  • 解决:用 QTimer 0 延迟异步读,别直接 dataChanged 槽里拿文本

QFileDialog::getOpenFileName 的“Win32 钩子污染”

  • native 对话框Qt 注入 Win32 钩子用户装第三方壳 / 防毒软件钩子冲突→程序闪退
  • 解决:强制 QFileDialog.DontUseNativeDialog 走 Qt 自绘

QThread::moveToThread 的“父对象屏障”

  • moveToThread 时对象树若有 parentQt 拒绝移动控制台只给 warning,不抛异常
  • 解决:先 setParent(nullptr),再 moveToThread()

QObject::connect 的“唯一字符串”

  • 信号槽连接字符串Qt 用 staticMetaObject.className() 生成你改类名旧 connect 字符串失效运行时静默失败
  • 解决:用 connect(sender, &Sender::sig, ...) 指针形式,别用字符串

QGraphicsItem::cacheMode 与 OpenGL 冲突

  • ItemCoordinateCache + QOpenGLWidget 视口FBO 大小变化时缓存不更新残影
  • 解决:viewport 变化时手动 update() 或换 NoCache

QStyle::pixelMetric 的“DPI 系数抖动”

  • pixelMetric(PM_ButtonMargin) 在高 DPI 下返回奇数你手动布局对不齐
  • 解决:用 style()->pixelMetric(..., device) 显式给 QPaintDevice

PySide 绑定层的“GIL 延迟释放”

  • 信号槽跨线程C++ emit → Python 回调PySide 持有 GIL 直到槽返回C++ 侧阻塞帧率掉 50 %
  • 解决:槽里立刻 QTimer.singleShot(0, lambda: 实际工作) 把活扔回事件循环,立刻放 GIL

QOpenGLWidget::makeCurrent() 的“上下文漂移”

  • 多 QOpenGLWidget任意一个 update()Qt 可能切上下文你 glDraw 时当前 context 已变glGetError 随机报错
  • 解决:每次 paintGL 开头显式 makeCurrent()

QGraphicsEffect::setEnabled 的“像素对齐”

  • DropShadowEffect 开启Qt 自动扩大 boundingRect 2 px高 DPI 下出现半像素偏移阴影模糊
  • 解决:关闭 setEnabled 或手动 setOffset(0, 0) + setBlurRadius(0) 再自绘

QWindow::requestUpdate() 的“vsync 漂移”

  • requestUpdate() 每帧调用vsync 信号抖动Qt 自动降帧到 30 fps
  • 解决:用 QTimer 固定 16 ms,别用 requestUpdate() 当主循环

QBackingStore::flush() 的“线程锁”

  • QOpenGLWidget + QBackingStoreflush() 持有 QMutexpaintGL 里 glFinish 等待帧时间抖动
  • 解决:paintGL 里绝不手动 glFinish / glFlush,让 Qt 自己调度

PySide 信号连接字符串的“Mangling 抖动”

  • 类名里含下划线SIP 绑定层 mangling 规则变化字符串形式 connect 找不到信号
  • 解决:只用指针形式 connect,别用字符串

QFontDatabase::addApplicationFont 的“进程级泄漏”

  • 动态加载字体Qt 内部 QFontDatabase 全局哈希字体文件句柄永不关闭进程退出才释放
  • 解决:只加载一次,程序生命周期内不复载

QClipboard::mimeData() 的“跨平台编码炸弹”

  • mimeData()->hasHtml()Qt 自动把文本转 HTML 实体中文被转 你切割字符串时索引错位
  • 解决:只拿 text(),别用 html() 当数据源

QStyleSheet 的“层叠缓存”

  • 同一句样式表字符串QStyleSheetCache 全局哈希运行时改一个字母整个进程所有控件重解析
  • 性能杀手:动态拼接样式表用 QProxyStyle 代替

QGraphicsScene::BspTree 深度爆栈(你已见)


QPlainTextEdit::centerOnScroll 行高漂移(你已见)


QFontMetrics 水平Advance DPI 抖动(你已见)


QClipboard 跨进程死锁(你已见)


QFileDialog Win32 钩子污染(你已见)


QThread moveToThread 父对象屏障(你已见)


QObject connect 字符串 Mangling(你已见)


QGraphicsItem cacheMode OpenGL 冲突(你已见)


QGraphicsEffect 像素对齐偏移(你已见)


QWindow requestUpdate vsync 漂移(你已见)


QBackingStore flush 线程锁(你已见)


PySide 信号连接字符串 Mangling(你已见)


QFontDatabase 进程级句柄泄漏(新)


QClipboard mimeData HTML 实体炸弹(新)


QStyleSheet 层叠缓存全局重解析(新)


≥ 20 条你已潜到深水区≥ 25 条可以开坑写书