一. 前言

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

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

二. PyQtInspect 基础使用

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

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

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

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

image.png

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

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

我是 VSCode 里面添加 Task :

        {
            "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:codepythonant-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 则不行。
  • 整体界面的效果还有很大的优化空间

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

总计

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

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

最后的最后 ️️️

  • 欢迎关注 ,超200篇优质文章,未来持续高质量输出
  • 系列文章集合,高并发,源码应有尽有

附录

类名作用
FuncWrapper函数包装器,为信号连接注入跟踪功能
StartedSignalWrapperQThread.started信号包装器,注入跟踪逻辑
ThreadWrapperQThread包装器,注入调试跟踪和信号
RunnableWrapperQRunnable包装器,为线程池任务注入跟踪
QProcessWrapperQProcess包装器,拦截进程启动并修改参数
HighlightController控件高亮显示管理器,实现红色半透明覆盖
EnteredWidgetStack鼠标进入控件的堆栈跟踪管理器
EventListenerQt事件器,处理鼠标和键盘事件
服务器端核心
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键盘钩子处理器,处理全局快捷键事件
KeyboardHookWinWindows键盘钩子实现,底层键盘事件捕获
StackFrameInfo堆栈帧信息类,存储单个堆栈帧数据

一些比较重要的代码

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()
class PyDB:
    def connect_to_server(self):
        self.writer = WriterThread(sock)
        self.reader = ReaderThread(sock)  
        # 建立双向通信通道
  • 核心类: Dispatcher, DispatchReader, WriterThread, ReaderThread
  • 消息工厂: NetCommandFactory
本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]