PyQtInspect : 推荐一个好用的 PythonQT 界面 Debug 工具

一. 前言

这1-2个月太忙了 ,完全没有时间做整理 ,所剩无几的时间也在维护开源项目在,所以这一篇就简简单单的分析一个工具吧。

这个工具是在写开源的时候找了半天找到的 ,虽然还不完美 ,但是已经有很大的帮助了。

二. PyQtInspect 基础使用

写过 PyQT 项目的兄弟 ,应该都有感觉 ,不像开发前端 ,可以在Chrome 上面进行调整。 在纯代码写 PyQT 项目的时候 ,界面上的组件对应代码哪个部分 ,纯粹靠经验和猜。

早期自己的代码可能问题不大 ,但是时间一久 ,就很麻烦了。

PyQtInspect 是什么 ?

@ PyqtInspect | PyqtInspect, a Chrome DevTool-like inspector for PyQt/PySide.

PyQtInspect 是一个强大的 PyQt/PySide 应用程序调试和检查工具 ,其效果如下 :

效果如上 ,通过 Select 可以选择窗口里面的组件 ,就可以查找到对应的代码位置以及相关信息 ,便于调试。

同时会展示出组件的各种信息 ,属性树 ,便于问题的分析。

使用方式 :

我是 VSCode 里面添加 Task :

js 复制代码
        {
            "label": "Run with PyQtInspect",
            "type": "shell",
            "command": "python",
            "args": [
                "-m",
                "PyQtInspect",
                "--direct",
                "--qt-support=PyQt6",
                "--file",
                "${workspaceFolder}/main.py"
            ],
            "group": {
                "kind": "build",
                "isDefault": false
            },
            "problemMatcher": []
        }
  • python -m PyQtInspect --direct --qt-support=PyQt6 --file D:\code\python\ant-tools/main.py

三. 原理浅学一下

主要流程 :

  • 阶段1: 系统启动 ,核心类 : ArgHandlerWithParam, PQIWindow, PQYWorker
    • 功能: 解析启动参数,创建服务器主窗口,开始监听指定端口等待客户端连接。支持服务器模式和直连模式。
  • 阶段2: 网络连接 ,核心类 : PyDB, Dispatcher, ReaderThread, WriterThread
    • 功能: 客户端连接服务器,建立双向通信通道。每个连接创建独立的调度器和读写线程,实现并发处理。
  • 阶段3: Qt补丁注入,核心类 : ThreadWrapper, QProcessWrapper, HighlightController
    • 功能: 动态检测Qt版本,对QThread、QProcess等核心类进行运行时包装,注入调试跟踪功能和事件监听器。
  • 阶段4: 事件监听,核心类 : EventListener, EnteredWidgetStack, QWidgetInfo
    • 功能: 实时监听鼠标进入控件事件,维护控件栈,提供红色半透明高亮效果,收集并发送控件详细信息。
  • 阶段5: GUI交互,核心类 : HierarchyBar, WidgetPropertiesGetter, CodeWindow
    • 功能: 更新层级导航栏,展示控件属性树,支持远程Python代码执行,提供完整的可视化调试界面。

3.1 注入与Monkey Patching机制

👉 在 pqi_monkey.py 中 ,会通过猴子补丁拦截所有进程创建相关的系统调用,比如 :

  • os.execl, os.execv, os.execve 系列函数
  • os.spawnl, os.spawnv, os.spawnve 系列函数
  • subprocess.Popen 和相关函数
  • Windows 平台的 CreateProcess

👉 同时pqi_monkey_qt_helpers.py还会对函数进行拦截 ,对 Qt 控件的构造函数进行补丁,实现控件创建时的自动注册

  • _new_QWidget_init : 其中会调用原始构造函数,初始化全局事件过滤器

3.2 事件拦截技术

  • 全局事件过滤器pqi_monkey_qt_helpers.py:188-351
    • EventEnum.Enter / EventEnum.Leave /EventEnum.MouseButtonPress
  • 原生事件过滤器 : 针对不同平台的原生事件处理
    • self.WM_NCHITTEST /

3.3 动态代码执行

在目标进程中动态执行 Python 代码,用于实时调试和控件操作 , 核心代码在 pqi_monkey_qt_helpers.py:564-575

通过这部分逻辑 ,可以访问以下资源 :

  1. 全局命名空间: Python 内置函数和模块
  2. 局部命名空间 : 当前控件实例 (self)
  3. Qt 模块: PyQt/PySide 的所有功能
  4. 调试器 API: PyQtInspect 提供的调试接口

四. 待扩展的点

  • 由于我是通过 VSCode 编写的 Python , 这里本来可以通过双击定位到代码行的 ,VSCode 则不行。
  • 整体界面的效果还有很大的优化空间

后续时间空出来了 ,就会主要针对这两点在大佬的基础上进行一定的改造。

总计

这一块没什么知识积累 ,看起来很吃力 ,为了后面去二次改造这个,还是磨了一下源码 ,也不知道对不对。

我个人现在只找到这一种方式 ,也不知道有没有更好的工具 ,欢迎大佬推荐。

最后的最后 ❤️❤️❤️👇👇👇

附录

类名 作用
FuncWrapper 函数包装器,为信号连接注入跟踪功能
StartedSignalWrapper QThread.started信号包装器,注入跟踪逻辑
ThreadWrapper QThread包装器,注入调试跟踪和信号监听
RunnableWrapper QRunnable包装器,为线程池任务注入跟踪
QProcessWrapper QProcess包装器,拦截进程启动并修改参数
HighlightController 控件高亮显示管理器,实现红色半透明覆盖
EnteredWidgetStack 鼠标进入控件的堆栈跟踪管理器
EventListener Qt事件监听器,处理鼠标和键盘事件
服务器端核心
PQIWindow 服务器主窗口,GUI管理和线程交互中心
DirectModePQIWindow 直连模式窗口,自动监听指定端口
PQYWorker 服务器工作线程,监听连接并创建调度器
DummyWorker 占位符类,空对象模式避免空指针检查
Dispatcher 单客户端通信处理器,管理完整通信生命周期
DispatchReader 调度器专用读取线程,处理URL解码和转发
客户端核心
PyDB 客户端核心调试器,管理远程调试会话
TrackedLock 线程感知锁,跟踪线程锁状态防止死锁
通信层
CommunicationRole 通信角色常量类,定义客户端服务器角色
PyDBDaemonThread 守护线程基类,管理所有后台线程
ReaderThread 通信读取线程基类,处理Socket数据接收
WriterThread 通信写入线程,处理消息队列和发送
NetCommandFactory 网络命令工厂,创建各种协议消息
数据结构
QWidgetInfo 控件信息数据类,存储完整Qt控件信息
QWidgetChildrenInfo 控件子元素信息数据类,存储层级关系
WidgetPropertiesGetter 控件属性获取器,支持50+种Qt控件类型
配置和工具
SetupHolder 全局配置持有器,定义所有配置键常量
DataCenter 数据中心类,管理GUI数据状态和缓存
DataHolder 数据持有器,存储GUI运行时数据
KeyboardHookHandler 键盘钩子处理器,处理全局快捷键事件
KeyboardHookWin Windows键盘钩子实现,底层键盘事件捕获
StackFrameInfo 堆栈帧信息类,存储单个堆栈帧数据

一些比较重要的代码

服务器端监听

python 复制代码
class PQYWorker(QtCore.QObject):
    def run(self):
        self._socket = socket(AF_INET, SOCK_STREAM)
        self._socket.bind(('', self.port))
        self._socket.listen(1)
        
        while self._isServing:
            newSock, _addr = self._socket.accept()
            dispatcher = Dispatcher(None, newSock, dispatcherId)
            dispatcher.start()

客户端连接

python 复制代码
class PyDB:
    def connect_to_server(self):
        self.writer = WriterThread(sock)
        self.reader = ReaderThread(sock)  
        # 建立双向通信通道

通信通道建立

  • 核心类 : Dispatcher, DispatchReader, WriterThread, ReaderThread
  • 消息工厂 : NetCommandFactory
相关推荐
flashlight_hi2 小时前
LeetCode 分类刷题:1901. 寻找峰值 II
python·算法·leetcode
fwerfv3453453 小时前
使用PyTorch构建你的第一个神经网络
jvm·数据库·python
蒋星熠3 小时前
反爬虫机制深度解析:从基础防御到高级对抗的完整技术实战
人工智能·pytorch·爬虫·python·深度学习·机器学习·计算机视觉
Predestination王瀞潞3 小时前
Python oct() 函数
开发语言·python
B站_计算机毕业设计之家4 小时前
python汽车数据分析可视化系统 爬虫 懂车帝 毕业设计 Django框架 vue框架 大数据✅
爬虫·python·数据分析·django·汽车·推荐算法·懂车帝
fl1768314 小时前
基于python+tkinter实现的自动组卷评卷考试系统python源码+课程设计+项目说明
开发语言·python·课程设计
王琦03184 小时前
Python 综合大作业
python
Dxy12393102164 小时前
Python自动连接已保存WiFi
开发语言·python
依旧很淡定5 小时前
Selenium(Python)创建Chrome浏览器实例
chrome·python·selenium