Inspect PyQt/PySide elements like Chrome DevTools
Source Code | 中文文档 | PyPI
For Python GUI programs developed with PyQt/PySide using Qt Widgets, it is difficult to view control information, locate the codes where they are defined, and perform other operations at runtime. It's not as easy as inspecting HTML elements in Chrome/Firefox browsers. This project aims to solve this problem by providing an element inspector tool for PyQt/PySide programs, similar to Chrome's element inspector.
-
Python 3.7+
-
One of the following Qt for Python frameworks installed: PyQt5/PySide2/PyQt6/Pyside6
Install with pip install PyQtInspect
.
If you want to experience the latest features introduced in the master branch as soon as possible, you can install from git or download the source ZIP package and install after extraction:
Install from Git:
pip install git+https://github.com/JezaChen/PyQtInspect-Open.git
⚠️ Note: Installing directly from the default branch is not reproducible. For deterministic installs, pin to a specific tag or commit.# pin to a specific tag pip install git+https://github.com/JezaChen/[email protected] # or pin to a specific commit pip install git+https://github.com/JezaChen/PyQtInspect-Open.git@<commit-sha>
Install from Source ZIP:
- Download the source ZIP package
- Extract the ZIP package to a local directory
- Navigate to the extracted directory and run:
pip install .
The PyQtInspect
architecture has two parts:
-
Debugger/Server: A GUI app for developers to visually inspect elements, locate code, etc.
-
Debuggee/Client: Runs inside the target Python process, patches the host’s Python Qt framework, responds to the debugger, and sends host information back.
Two startup modes are supported:
-
Detached Mode: Manually start the GUI server first, then start the debuggee to connect to it. When the debuggee exits, the GUI server remains running.
-
Direct Mode (Recommended): Start only the debuggee; it will launch a local GUI server automatically (no need to start the server yourself). When the debuggee exits, the GUI server exits with it.
Note that in Direct Mode, each client (debuggee) creates its own server, i.e., one-to-one relationship. Also in Direct Mode, you cannot manually specify the listening port, close connections, or attach to processes.
Detached Mode supports remote debugging (server and client on different machines). Direct Mode does not, since the client and its auto-launched server run on the same machine.
PyQtInspect also supports running in IDEs like PyCharm and attaching to an existing PyQt/PySide process.
This recommended one-step method launches both the PyQtInspect server and client together. It requires full access to the Python source code of the debugged program.
If you normally run your PyQt5 app via python xxx.py param1 param2
, simply insert -m PyQtInspect --direct --file
between python
and xxx.py
, i.e.:
python -m PyQtInspect --direct --file xxx.py param1 param2
to start debugging with PyQtInspect.
If the app uses PySide2/PyQt6/PySide6, you must also add the --qt-support
option to specify the Qt framework. For example, for PySide2 you should use:
python -m PyQtInspect --direct --qt-support=pyside2 --file xxx.py param1 param2
.
For Direct Mode, the full command is:
python -m PyQtInspect --direct [--multiprocess] [--show-pqi-stack] [--qt-support=[pyqt5|pyside2|pyqt6|pyside6]] --file py_file [file_args]
Parameter meanings:
--direct
: Use Direct Mode--multiprocess
: Enable multi-process debugging (off by default)--show-pqi-stack
: Show call stacks related to PyQtInspect (hidden by default)--qt-support
: Qt framework used by the target app, defaultpyqt5
; choose frompyqt5
,pyside2
,pyqt6
,pyside6
--file
: Path to the target app’s Python entry filefile_args
: Command-line arguments for the debuggee
For example, to debug the PySide2 version of PyQt-Fluent-Widgets
, whose demo runs via
python examples/gallery/demo.py
, you can use:
python -m PyQtInspect --direct --qt-support=pyside2 --file examples/gallery/demo.py
to launch PyQtInspect in Direct Mode.
Note: When using PyCharm or other IDEs that rely on the pydevd debugger, ensure the IDE’s ‘PyQt compatible’ option matches the Qt framework used by your project, otherwise PyQtInspect may not work correctly or could crash the program.
In Detached Mode, make sure to start the GUI server before launching the debugged Python program.
Run pqi-server
in a terminal to launch the server GUI. After launch, set the listening port (default 19394
) and click Serve to start the server.
Prerequisite: You must have the target program’s Python source.
Similar to Direct Mode, if you run a PyQt5 app via python xxx.py param1 param2
, insert -m PyQtInspect --file
between python
and xxx.py
, i.e.:
python -m PyQtInspect --file xxx.py param1 param2
to start debugging with PyQtInspect.
Likewise, for PySide2/PyQt6/Pyside6, also add --qt-support
to specify the framework. For example, for PySide2:
python -m PyQtInspect --qt-support=pyside2 --file xxx.py param1 param2
.
For Detached Mode, the full command is:
python -m PyQtInspect [--port N] [--client hostname] [--multiprocess] [--show-pqi-stack] [--qt-support=[pyqt5|pyside2|pyqt6|pyside6]] --file py_file [file_args]
Parameter meanings:
--port
: Server listening port (default19394
)--client
: Server address (default127.0.0.1
)--multiprocess
: Enable multi-process debugging (off by default)--show-pqi-stack
: Show call stacks related to PyQtInspect (hidden by default)--qt-support
: Qt framework used by the target app, defaultpyqt5
; choose frompyqt5
,pyside2
,pyqt6
,pyside6
--file
: Path to the target app’s Python entry filefile_args
: Command-line arguments for the target app
Again using the PySide2 version of PyQt-Fluent-Widgets
as an example: if the GUI debugger is already running locally (address 127.0.0.1
) on the default port 19394
, you can start the client with
python -m PyQtInspect --qt-support=pyside2 --file examples/gallery/demo.py
.
(Here the server address and port are defaults, so you don’t need to pass --client
or --port
.)
Note: When using PyCharm or other IDEs that rely on the pydevd debugger, make sure the IDE’s ‘PyQt compatible’ option matches your project’s framework, otherwise PyQtInspect may not work correctly or could crash the program.
Debug the PyQtInspect module directly in PyCharm; this won’t interfere with debugging your app.
Also taking PyQt-Fluent-Widgets
as an example, you can create a new Debug configuration like so:
Then simply Run/Debug.
If you don’t have the target app’s source, you can try enabling inspect via process attach.
Click More → Attach To Process to open the attach window, choose the target process, then click Attach.
Note: For most controls, you cannot retrieve their creation call stacks unless they were created after you attached.
Click Select to start picking. Hover over a control to highlight it and preview brief info (class name, object name, size, relative position, styles, etc.).
Left-click to select the control. You can then inspect it in depth: view and jump to its initialization call stack, execute code in its context, view hierarchy info, view the control tree, and inspect properties.
The second tab below the brief info shows detailed properties, organized hierarchically by class inheritance and property type.
The first tab below the brief info shows the call stack at control creation. Double-click to open PyCharm and navigate to the file and line.
If opening PyCharm fails, set the PyCharm path under More → Settings.
P.S. For clients started via Attach to Process, if the control already existed when you attached, creation info won’t be available and the call stack will be empty.
After selecting a control, click Run Code to execute code in the scope of the selected control (self
is the control instance; essentially this runs inside a method of the control object).
At the bottom is the hierarchy breadcrumb. You can view, highlight, and locate ancestor and child controls of the selection, making it easy to navigate the hierarchy. Combined with mouse selecting, this enables more precise selection.
(Enabled by default. To disable, go to More → Mock Right Button Down as Left When Selecting Elements and uncheck.)
Some controls only appear after a left-click. For easier picking, you can simulate left-click with the right mouse button.
P.S. This only applies while mouse selecting is active.
(Enabled by default. To disable, go to More → Press F8 to Finish Selecting and uncheck.)
For controls that are hard to pick with the mouse, press F8 to finish selection. Note that F8 only ends an ongoing selection; when selection isn’t active, pressing F8 will not start it.
Click View → Control Tree to see the control tree of the process that contains the selected control. Click (or hover) a row in the tree to highlight the corresponding control.
-
Patching fails with multiple inheritance involving more than two PyQt classes, such as class
A(B, C)
, whereB
andC
inherit from QObject. This might cause the__init__
method ofC
to not execute, leading to exceptions.The author of PyQt has warned against multiple inheritance with more than two PyQt classes, as it can also cause abnormal behavior in PyQt itself.
-
Cannot select some controls for PyQt6.
-
For some computers, sometimes the
QEnterEvent
will have the type170
(which isQEvent.DynamicPropertyChange
), which may cause crash when accessing thepropertyName
method.
- Added the “Properties” tab
- Added toolbar entries to open/clear logs
- Fixed a series of issues