Visualization
This guide covers the graphics system: scenes, views, painters, and geometry.
Scene and View
QtNodes uses Qt's Graphics View Framework:
- BasicGraphicsScene -- A
QGraphicsScenethat creates visual objects for your model - GraphicsView -- A
QGraphicsViewwith node-editing interactions built in
.. code-block:: cpp
// Connect model to scene to view MyGraphModel model; BasicGraphicsScene scene(model); GraphicsView view(&scene); view.show();
For data flow applications, use the specialized scene:
.. code-block:: cpp
DataFlowGraphModel model(registry); DataFlowGraphicsScene scene(model); // Handles delegate creation GraphicsView view(&scene);
Graphics Objects
The scene creates graphics objects automatically:
- NodeGraphicsObject -- Visual representation of each node
- ConnectionGraphicsObject -- Visual representation of each connection
Access them when needed:
.. code-block:: cpp
NodeGraphicsObject* nodeObj = scene.nodeGraphicsObject(nodeId); ConnectionGraphicsObject* connObj = scene.connectionGraphicsObject(connId);
Custom Node Painter
Replace the default node appearance by subclassing AbstractNodePainter:
.. code-block:: cpp
class MyNodePainter : public QtNodes::AbstractNodePainter { public: void paint(QPainter* painter, NodeGraphicsObject& ngo) const override { auto& geometry = ngo.nodeScene()->nodeGeometry(); auto& model = ngo.graphModel(); NodeId nodeId = ngo.nodeId();
QRectF rect = geometry.boundingRect(nodeId);
// Custom drawing
painter->setBrush(QColor(70, 130, 180));
painter->setPen(QPen(Qt::white, 2));
painter->drawRoundedRect(rect, 10, 10);
// Draw caption
QString caption = model.nodeData(nodeId, NodeRole::Caption).toString();
painter->drawText(rect, Qt::AlignCenter, caption);
}
};
// Register with scene scene.setNodePainter(std::make_unique());
.. image:: /_static/screenshots/custom-painter.png :alt: Nodes with custom painting :width: 400px
.. SCREENSHOT NEEDED: custom-painter.png
- Show the custom_painter example running
- Display nodes with the blue-purple gradient style
- Show connections with arrows
- Size: ~400px wide
Custom Connection Painter
Similarly for connections:
.. code-block:: cpp
class MyConnectionPainter : public QtNodes::AbstractConnectionPainter { public: void paint(QPainter* painter, ConnectionGraphicsObject const& cgo) const override { QPointF start = cgo.endPoint(PortType::Out); QPointF end = cgo.endPoint(PortType::In);
// Draw as straight line with arrow
painter->setPen(QPen(Qt::cyan, 3));
painter->drawLine(start, end);
// Draw arrow head at end
drawArrow(painter, end, start);
}
QPainterPath getPainterStroke(ConnectionGraphicsObject const& cgo) const override
{
// Return path for hit testing
QPainterPath path;
path.moveTo(cgo.endPoint(PortType::Out));
path.lineTo(cgo.endPoint(PortType::In));
QPainterPathStroker stroker;
stroker.setWidth(10);
return stroker.createStroke(path);
}
};
scene.setConnectionPainter(std::make_unique());
Custom Node Geometry
Control node layout by subclassing AbstractNodeGeometry:
.. code-block:: cpp
class MyNodeGeometry : public QtNodes::AbstractNodeGeometry { public: QRectF boundingRect(NodeId nodeId) const override; QPointF portPosition(NodeId, PortType, PortIndex) const override; QPointF captionPosition(NodeId) const override; QPointF widgetPosition(NodeId) const override; // ... other layout methods };
scene.setNodeGeometry(std::make_unique());
The library provides two built-in geometries:
DefaultHorizontalNodeGeometry-- Ports on left/right (default)DefaultVerticalNodeGeometry-- Ports on top/bottom
Vertical Layout
For top-to-bottom graphs:
.. code-block:: cpp
scene.setOrientation(Qt::Vertical);
.. image:: /_static/screenshots/vertical-layout.png :alt: Vertical node layout :width: 500px
.. SCREENSHOT NEEDED: vertical-layout.png
- Show vertical_layout example
- Nodes with ports on top and bottom
- Connections flowing downward
- Size: ~300px wide
GraphicsView Features
Zoom control:
.. code-block:: cpp
// Set zoom limits (0 = unlimited) view.setScaleRange(0.25, 4.0);
// Zoom programmatically view.scaleUp(); view.scaleDown(); view.setupScale(1.5);
// Fit content view.zoomFitAll(); // Fit all nodes view.zoomFitSelected(); // Fit selected nodes
// React to zoom changes connect(&view, &GraphicsView::scaleChanged, [](double scale) { qDebug() << "Zoom:" << scale * 100 << "%"; });
Built-in actions:
.. code-block:: cpp
view.clearSelectionAction(); // Clear selection view.deleteSelectionAction(); // Delete selected items
Copy/paste:
.. code-block:: cpp
view.onCopySelectedObjects(); view.onPasteObjects(); view.onDuplicateSelectedObjects(); // Ctrl+D
Scene Signals
Connect to user interactions:
.. code-block:: cpp
// Node interactions connect(&scene, &BasicGraphicsScene::nodeClicked, [](NodeId id) { qDebug() << "Clicked node" << id; });
connect(&scene, &BasicGraphicsScene::nodeDoubleClicked, [](NodeId id) { // Open node properties dialog });
connect(&scene, &BasicGraphicsScene::nodeMoved, [](NodeId id, QPointF pos) { qDebug() << "Node" << id << "moved to" << pos; });
// Hover events (for tooltips, highlights) connect(&scene, &BasicGraphicsScene::nodeHovered, [](NodeId id, QPoint screenPos) { QToolTip::showText(screenPos, "Node info here"); });
connect(&scene, &BasicGraphicsScene::connectionHovered, [](ConnectionId id, QPoint screenPos) { // Show connection info });
// Context menus connect(&scene, &BasicGraphicsScene::nodeContextMenu, [](NodeId id, QPointF scenePos) { // Show custom context menu for this node });
Context Menus
Override createSceneMenu() for custom right-click menus:
.. code-block:: cpp
class MyScene : public DataFlowGraphicsScene { public: QMenu* createSceneMenu(QPointF scenePos) override { auto* menu = new QMenu();
for (auto& category : _registry->categories()) {
auto* submenu = menu->addMenu(category);
for (auto& modelName : _registry->registeredModelsByCategory(category)) {
submenu->addAction(modelName, [=]() {
// Create node at scenePos
});
}
}
return menu;
}
};
Headless Mode
Run without any visualization:
.. code-block:: cpp
// Model works without scene DataFlowGraphModel model(registry);
NodeId source = model.addNode("NumberSource"); NodeId display = model.addNode("Display"); model.addConnection({source, 0, display, 0});
// Data flows, computations happen, no GUI needed
// Or: scene without view BasicGraphicsScene scene(model); // Scene tracks model changes but nothing is rendered
.. seealso::
- :doc:
styling-- Customize colors and styles - :doc:
/examples/index-- Visual examples