Skip to content

Commit fdaf948

Browse files
committed
feat: add examples and tests for advanced features
New examples: - node_validation: demonstrates NodeValidationState and NodeProcessingStatus with status icons - custom_painter: shows custom AbstractNodePainter and AbstractConnectionPainter implementations New tests: - TestNodeValidation: validation state and processing status - TestCustomPainters: custom painter registration - TestCopyPaste: copy/paste and duplicate operations - TestZoomFeatures: zoom fit all/selected, scale range - TestLoopDetection: cycle detection in data flow graphs
1 parent afcbf28 commit fdaf948

21 files changed

Lines changed: 1923 additions & 0 deletions

examples/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@ add_subdirectory(dynamic_ports)
1616

1717
add_subdirectory(lock_nodes_and_connections)
1818

19+
add_subdirectory(node_validation)
20+
21+
add_subdirectory(custom_painter)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
file(GLOB_RECURSE CPPS ./*.cpp )
2+
file(GLOB_RECURSE HPPS ./*.hpp )
3+
4+
add_executable(custom_painter ${CPPS} ${HPPS})
5+
6+
target_link_libraries(custom_painter QtNodes)
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#include "CustomConnectionPainter.hpp"
2+
3+
#include <QtNodes/ConnectionIdUtils>
4+
#include <QtNodes/internal/ConnectionGraphicsObject.hpp>
5+
6+
#include <cmath>
7+
8+
using QtNodes::ConnectionId;
9+
10+
void CustomConnectionPainter::paint(QPainter *painter, ConnectionGraphicsObject const &cgo) const
11+
{
12+
painter->setRenderHint(QPainter::Antialiasing);
13+
14+
QPainterPath path = createPath(cgo);
15+
16+
// Draw the connection with a custom style
17+
QPen pen;
18+
19+
if (cgo.isSelected()) {
20+
pen.setColor(QColor(255, 215, 0)); // Gold when selected
21+
pen.setWidth(4);
22+
pen.setStyle(Qt::SolidLine);
23+
} else if (cgo.connectionId().inNodeId == QtNodes::InvalidNodeId
24+
|| cgo.connectionId().outNodeId == QtNodes::InvalidNodeId) {
25+
// Draft connection (being drawn)
26+
pen.setColor(QColor(150, 150, 150));
27+
pen.setWidth(2);
28+
pen.setStyle(Qt::DashLine);
29+
} else {
30+
// Normal connection - gradient-like effect using dashed line
31+
pen.setColor(QColor(0, 191, 255)); // Deep sky blue
32+
pen.setWidth(3);
33+
pen.setStyle(Qt::SolidLine);
34+
}
35+
36+
painter->setPen(pen);
37+
painter->setBrush(Qt::NoBrush);
38+
painter->drawPath(path);
39+
40+
// Draw arrow at the end point (input port)
41+
if (cgo.connectionId().inNodeId != QtNodes::InvalidNodeId) {
42+
QPointF endPoint = cgo.endPoint(QtNodes::PortType::In);
43+
QPointF startPoint = cgo.endPoint(QtNodes::PortType::Out);
44+
45+
// Get a point slightly before the end for arrow direction
46+
qreal t = 0.95;
47+
QPointF controlPoint1 = startPoint + QPointF(50, 0);
48+
QPointF controlPoint2 = endPoint - QPointF(50, 0);
49+
50+
// Calculate point on bezier curve at t
51+
qreal mt = 1.0 - t;
52+
QPointF beforeEnd = mt * mt * mt * startPoint + 3.0 * mt * mt * t * controlPoint1
53+
+ 3.0 * mt * t * t * controlPoint2 + t * t * t * endPoint;
54+
55+
drawArrow(painter, endPoint, beforeEnd);
56+
}
57+
}
58+
59+
QPainterPath CustomConnectionPainter::getPainterStroke(ConnectionGraphicsObject const &cgo) const
60+
{
61+
QPainterPath path = createPath(cgo);
62+
63+
QPainterPathStroker stroker;
64+
stroker.setWidth(10.0);
65+
return stroker.createStroke(path);
66+
}
67+
68+
QPainterPath CustomConnectionPainter::createPath(ConnectionGraphicsObject const &cgo) const
69+
{
70+
QPointF const &startPoint = cgo.endPoint(QtNodes::PortType::Out);
71+
QPointF const &endPoint = cgo.endPoint(QtNodes::PortType::In);
72+
73+
// Create a bezier curve
74+
QPainterPath path;
75+
path.moveTo(startPoint);
76+
77+
// Control points for smooth curve
78+
qreal dx = std::abs(endPoint.x() - startPoint.x());
79+
qreal controlOffset = std::max(dx * 0.5, 30.0);
80+
81+
QPointF controlPoint1(startPoint.x() + controlOffset, startPoint.y());
82+
QPointF controlPoint2(endPoint.x() - controlOffset, endPoint.y());
83+
84+
path.cubicTo(controlPoint1, controlPoint2, endPoint);
85+
86+
return path;
87+
}
88+
89+
void CustomConnectionPainter::drawArrow(QPainter *painter, QPointF const &tip, QPointF const &from) const
90+
{
91+
// Calculate arrow direction
92+
QPointF direction = tip - from;
93+
qreal length = std::sqrt(direction.x() * direction.x() + direction.y() * direction.y());
94+
95+
if (length < 1.0)
96+
return;
97+
98+
direction /= length;
99+
100+
// Arrow parameters
101+
qreal arrowSize = 10.0;
102+
qreal arrowAngle = M_PI / 6.0; // 30 degrees
103+
104+
// Calculate arrow points
105+
QPointF arrowP1 = tip
106+
- arrowSize
107+
* QPointF(direction.x() * std::cos(arrowAngle)
108+
- direction.y() * std::sin(arrowAngle),
109+
direction.x() * std::sin(arrowAngle)
110+
+ direction.y() * std::cos(arrowAngle));
111+
112+
QPointF arrowP2 = tip
113+
- arrowSize
114+
* QPointF(direction.x() * std::cos(-arrowAngle)
115+
- direction.y() * std::sin(-arrowAngle),
116+
direction.x() * std::sin(-arrowAngle)
117+
+ direction.y() * std::cos(-arrowAngle));
118+
119+
// Draw filled arrow
120+
QPainterPath arrowPath;
121+
arrowPath.moveTo(tip);
122+
arrowPath.lineTo(arrowP1);
123+
arrowPath.lineTo(arrowP2);
124+
arrowPath.closeSubpath();
125+
126+
painter->fillPath(arrowPath, painter->pen().color());
127+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#pragma once
2+
3+
#include <QtNodes/AbstractConnectionPainter>
4+
5+
#include <QPainter>
6+
#include <QPainterPath>
7+
8+
namespace QtNodes {
9+
class ConnectionGraphicsObject;
10+
}
11+
12+
using QtNodes::AbstractConnectionPainter;
13+
using QtNodes::ConnectionGraphicsObject;
14+
15+
/// Custom connection painter that draws dashed lines with arrows
16+
class CustomConnectionPainter : public AbstractConnectionPainter
17+
{
18+
public:
19+
void paint(QPainter *painter, ConnectionGraphicsObject const &cgo) const override;
20+
QPainterPath getPainterStroke(ConnectionGraphicsObject const &cgo) const override;
21+
22+
private:
23+
QPainterPath createPath(ConnectionGraphicsObject const &cgo) const;
24+
void drawArrow(QPainter *painter, QPointF const &tip, QPointF const &from) const;
25+
};
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#include "CustomNodePainter.hpp"
2+
3+
#include <QtNodes/AbstractGraphModel>
4+
#include <QtNodes/BasicGraphicsScene>
5+
#include <QtNodes/StyleCollection>
6+
#include <QtNodes/internal/NodeGraphicsObject.hpp>
7+
8+
#include <QPainterPath>
9+
10+
using QtNodes::AbstractGraphModel;
11+
using QtNodes::AbstractNodeGeometry;
12+
using QtNodes::BasicGraphicsScene;
13+
using QtNodes::NodeRole;
14+
using QtNodes::PortType;
15+
using QtNodes::StyleCollection;
16+
17+
void CustomNodePainter::paint(QPainter *painter, NodeGraphicsObject &ngo) const
18+
{
19+
painter->setRenderHint(QPainter::Antialiasing);
20+
21+
drawBackground(painter, ngo);
22+
drawCaption(painter, ngo);
23+
drawPorts(painter, ngo);
24+
}
25+
26+
void CustomNodePainter::drawBackground(QPainter *painter, NodeGraphicsObject &ngo) const
27+
{
28+
AbstractNodeGeometry const &geometry = ngo.nodeScene()->nodeGeometry();
29+
AbstractGraphModel const &model = ngo.graphModel();
30+
QtNodes::NodeId const nodeId = ngo.nodeId();
31+
32+
QRectF const boundingRect = geometry.boundingRect(nodeId);
33+
34+
// Custom rounded rectangle with gradient
35+
double const radius = 15.0;
36+
37+
QPainterPath path;
38+
path.addRoundedRect(boundingRect, radius, radius);
39+
40+
// Create a custom gradient - blue to purple
41+
QLinearGradient gradient(boundingRect.topLeft(), boundingRect.bottomRight());
42+
43+
if (ngo.isSelected()) {
44+
gradient.setColorAt(0.0, QColor(100, 149, 237)); // Cornflower blue
45+
gradient.setColorAt(1.0, QColor(186, 85, 211)); // Medium orchid
46+
} else {
47+
gradient.setColorAt(0.0, QColor(70, 130, 180)); // Steel blue
48+
gradient.setColorAt(1.0, QColor(138, 43, 226)); // Blue violet
49+
}
50+
51+
painter->fillPath(path, gradient);
52+
53+
// Draw border
54+
QPen pen(ngo.isSelected() ? QColor(255, 215, 0) : QColor(255, 255, 255), 2.0);
55+
painter->setPen(pen);
56+
painter->drawPath(path);
57+
}
58+
59+
void CustomNodePainter::drawCaption(QPainter *painter, NodeGraphicsObject &ngo) const
60+
{
61+
AbstractNodeGeometry const &geometry = ngo.nodeScene()->nodeGeometry();
62+
AbstractGraphModel const &model = ngo.graphModel();
63+
QtNodes::NodeId const nodeId = ngo.nodeId();
64+
65+
QString const caption = model.nodeData(nodeId, NodeRole::Caption).toString();
66+
QRectF const boundingRect = geometry.boundingRect(nodeId);
67+
68+
// Draw caption centered at top
69+
QFont font = painter->font();
70+
font.setBold(true);
71+
font.setPointSize(12);
72+
painter->setFont(font);
73+
74+
painter->setPen(Qt::white);
75+
76+
QRectF captionRect = boundingRect;
77+
captionRect.setHeight(30);
78+
79+
painter->drawText(captionRect, Qt::AlignCenter, caption);
80+
}
81+
82+
void CustomNodePainter::drawPorts(QPainter *painter, NodeGraphicsObject &ngo) const
83+
{
84+
AbstractNodeGeometry const &geometry = ngo.nodeScene()->nodeGeometry();
85+
AbstractGraphModel const &model = ngo.graphModel();
86+
QtNodes::NodeId const nodeId = ngo.nodeId();
87+
88+
// Draw input ports (left side) - green circles
89+
unsigned int const inPortCount = model.nodeData<unsigned int>(nodeId, NodeRole::InPortCount);
90+
for (unsigned int i = 0; i < inPortCount; ++i) {
91+
QPointF pos = geometry.portPosition(nodeId, PortType::In, i);
92+
painter->setBrush(QColor(50, 205, 50)); // Lime green
93+
painter->setPen(QPen(Qt::white, 1.5));
94+
painter->drawEllipse(pos, 6.0, 6.0);
95+
}
96+
97+
// Draw output ports (right side) - orange circles
98+
unsigned int const outPortCount = model.nodeData<unsigned int>(nodeId, NodeRole::OutPortCount);
99+
for (unsigned int i = 0; i < outPortCount; ++i) {
100+
QPointF pos = geometry.portPosition(nodeId, PortType::Out, i);
101+
painter->setBrush(QColor(255, 165, 0)); // Orange
102+
painter->setPen(QPen(Qt::white, 1.5));
103+
painter->drawEllipse(pos, 6.0, 6.0);
104+
}
105+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#pragma once
2+
3+
#include <QtNodes/AbstractNodePainter>
4+
5+
#include <QPainter>
6+
7+
namespace QtNodes {
8+
class NodeGraphicsObject;
9+
class AbstractNodeGeometry;
10+
class AbstractGraphModel;
11+
} // namespace QtNodes
12+
13+
using QtNodes::AbstractNodePainter;
14+
using QtNodes::NodeGraphicsObject;
15+
16+
/// Custom node painter that draws nodes with rounded corners and a gradient
17+
class CustomNodePainter : public AbstractNodePainter
18+
{
19+
public:
20+
void paint(QPainter *painter, NodeGraphicsObject &ngo) const override;
21+
22+
private:
23+
void drawBackground(QPainter *painter, NodeGraphicsObject &ngo) const;
24+
void drawCaption(QPainter *painter, NodeGraphicsObject &ngo) const;
25+
void drawPorts(QPainter *painter, NodeGraphicsObject &ngo) const;
26+
};

0 commit comments

Comments
 (0)