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.py participate in circular imports. Fix: extract shared types or defer imports.
  • High - Deep nesting (32 files): qgraphics/node_base.py hits indentation depth 9. Fix: early returns/guard clauses.
  • High - Oversized files (5): nodes/base_node.py at 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.