The Problem
Building node-based UIs (DCC tools, data pipelines, shader graphs) from scratch on Qt means reimplementing the same hard parts every time: scene management, pipe routing, node selection, undo/redo, and property panels. NodeGraphQt packages those pieces into a reusable framework so you build the node logic instead of the graph plumbing.
What This Does
NodeGraphQt is a node graph UI framework for Python/Qt. The core lives in NodeGraphQt/base/graph.py (graph management, 135 functions) and NodeGraphQt/widgets/viewer.py (the QGraphicsView wrapper). Rendering is split across NodeGraphQt/qgraphics/ — node_base.py, port.py, pipe.py — while NodeGraphQt/base/commands.py implements undo/redo with 13 command classes. The examples/ folder has a basic_example.py entry point and custom node/widget samples.
How It Is Wired
Execution starts at main in examples/basic_example.py:18, which reaches 253 functions. The graph object routes most traffic: NodeGraphQt/base/graph.py is called from 26 files and calls into 21, making it the primary hub. High-blast-radius functions include scene (38 call sites), name (35), boundingRect (31), and set_property (28).
The import graph shows 14 modules in circular dependencies, including __init__.py, base/graph.py, and nodes/base_node.py. Breaking a cycle means touching several files at once. constants.py is imported by 32 modules — keep it stable.
The only traced filesystem effect is main -> set_context_menu_from_file, which reads a JSON menu file. No database or network I/O was found in the resolved call graph.
File map: base/graph.py owns graph lifecycle and context menus; base/commands.py owns undo/redo; widgets/viewer.py owns the viewport and context menu building; qgraphics/* owns rendering and interaction; custom_widgets/properties_bin/* owns the property panel.
How To Use It
Setup: Install with pip install NodeGraphQt (per README), or clone and run pip install -r requirements.txt from requirements.txt. No lockfile is present, so builds are not fully reproducible.
Running it: python examples/basic_example.py — creates a NodeGraphWidget, registers a custom node class, and displays the viewer. No environment variables or config files are required.
Real-World Use
A DCC integration script that registers tool-specific nodes:
from NodeGraphQt import NodeGraphWidget, BaseNode
class MyNode(BaseNode):
__identifier__ = "com.example"
NODE_NAME = "My Node"
def run(self):
# tool-specific logic
pass
graph = NodeGraphWidget()
graph.register_node(MyNode)
node = graph.create_node("com.example.MyNode")
graph.set_node_selected(node, True)
Code Health & Issues
Static analysis (78 modules, 148 import edges) found 54 findings: 34 high, 20 medium. Key issues:
- High - Import cycles (14 modules):
__init__.py,nodes/base_node.py,base/node.pyparticipate in circular imports. Fix: extract shared types or defer imports. - High - Deep nesting (32 files):
qgraphics/node_base.pyhits indentation depth 9. Fix: early returns/guard clauses. - High - Oversized files (5):
nodes/base_node.pyat 758 lines. Fix: split by responsibility. - High - Duplicated code (24 files): 425 repeated 6-line blocks. Fix: extract shared helpers.
SDLC observations: no test suite exists (56 source files, zero tests); CI uses unpinned third-party actions (pypa/gh-action-pypi-publish@release/v1, ts-graphviz/setup-graphviz@v1); sphinx_doc_build.yml pushes directly to the pages branch; no Dependabot/Renovate configured. These are structural facts from the file tree and workflow files.
The Bottom Line
Solid, well-documented framework for Qt-based node graphs, with working examples and a real PyPI release. The lack of tests and the import cycles make it risky to modify internals without a safety net. Use it if you need a node UI and can tolerate adding your own test coverage.