From 988ce45da02502ee9d5986d535a5d4f87bfad964 Mon Sep 17 00:00:00 2001 From: Taiguara Tupinambas Date: Sun, 22 Jun 2025 14:08:06 -0300 Subject: [PATCH 01/22] wip: adds NodeValidationState info to NodeDelegateModel --- examples/calculator/DivisionModel.hpp | 10 ++-- .../QtNodes/internal/DefaultNodePainter.hpp | 2 + include/QtNodes/internal/Definitions.hpp | 27 +++++----- .../QtNodes/internal/NodeDelegateModel.hpp | 19 +++++++ src/DataFlowGraphModel.cpp | 22 ++++++-- src/DefaultNodePainter.cpp | 54 +++++++++++++++++++ src/NodeDelegateModel.cpp | 5 ++ 7 files changed, 119 insertions(+), 20 deletions(-) diff --git a/examples/calculator/DivisionModel.hpp b/examples/calculator/DivisionModel.hpp index 9018450c8..ab0753b6d 100644 --- a/examples/calculator/DivisionModel.hpp +++ b/examples/calculator/DivisionModel.hpp @@ -56,12 +56,14 @@ class DivisionModel : public MathOperationDataModel auto n2 = _number2.lock(); if (n2 && (n2->number() == 0.0)) { - //modelValidationState = NodeValidationState::Error; - //modelValidationError = QStringLiteral("Division by zero error"); + QtNodes::NodeValidationState state; + state._isValid = false; + state._errorMessage = QStringLiteral("Division by zero error"); + setValidatonState(state); _result.reset(); } else if (n1 && n2) { - //modelValidationState = NodeValidationState::Valid; - //modelValidationError = QString(); + QtNodes::NodeValidationState state; + setValidatonState(state); _result = std::make_shared(n1->number() / n2->number()); } else { //modelValidationState = NodeValidationState::Warning; diff --git a/include/QtNodes/internal/DefaultNodePainter.hpp b/include/QtNodes/internal/DefaultNodePainter.hpp index 484969f9a..d9d2be817 100644 --- a/include/QtNodes/internal/DefaultNodePainter.hpp +++ b/include/QtNodes/internal/DefaultNodePainter.hpp @@ -30,5 +30,7 @@ class NODE_EDITOR_PUBLIC DefaultNodePainter : public AbstractNodePainter void drawEntryLabels(QPainter *painter, NodeGraphicsObject &ngo) const; void drawResizeRect(QPainter *painter, NodeGraphicsObject &ngo) const; + + void drawValidationRect(QPainter *painter, NodeGraphicsObject &ngo) const; }; } // namespace QtNodes diff --git a/include/QtNodes/internal/Definitions.hpp b/include/QtNodes/internal/Definitions.hpp index 863fa40b4..efd9bbd3e 100644 --- a/include/QtNodes/internal/Definitions.hpp +++ b/include/QtNodes/internal/Definitions.hpp @@ -18,21 +18,22 @@ NODE_EDITOR_PUBLIC Q_NAMESPACE Q_NAMESPACE_EXPORT(NODE_EDITOR_PUBLIC) #endif - /** +/** * Constants used for fetching QVariant data from GraphModel. */ - enum class NodeRole { - Type = 0, ///< Type of the current node, usually a string. - Position = 1, ///< `QPointF` positon of the node on the scene. - Size = 2, ///< `QSize` for resizable nodes. - CaptionVisible = 3, ///< `bool` for caption visibility. - Caption = 4, ///< `QString` for node caption. - Style = 5, ///< Custom NodeStyle as QJsonDocument - InternalData = 6, ///< Node-stecific user data as QJsonObject - InPortCount = 7, ///< `unsigned int` - OutPortCount = 9, ///< `unsigned int` - Widget = 10, ///< Optional `QWidget*` or `nullptr` - }; +enum class NodeRole { + Type = 0, ///< Type of the current node, usually a string. + Position = 1, ///< `QPointF` positon of the node on the scene. + Size = 2, ///< `QSize` for resizable nodes. + CaptionVisible = 3, ///< `bool` for caption visibility. + Caption = 4, ///< `QString` for node caption. + Style = 5, ///< Custom NodeStyle as QJsonDocument + InternalData = 6, ///< Node-stecific user data as QJsonObject + InPortCount = 7, ///< `unsigned int` + OutPortCount = 9, ///< `unsigned int` + Widget = 10, ///< Optional `QWidget*` or `nullptr` + ValidationState = 11, ///< Enum NodeValidationState of the node +}; Q_ENUM_NS(NodeRole) /** diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index 6301164db..9ee4a8bfb 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -2,6 +2,7 @@ #include +#include #include #include "Definitions.hpp" @@ -12,6 +13,15 @@ namespace QtNodes { +/** + * Describes whether a node configuration is usable. + */ +struct NodeValidationState +{ + bool _isValid{true}; + QString _errorMessage{""}; +}; + class StyleCollection; /** @@ -44,11 +54,16 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel : public QObject, public Serializable /// Name makes this model unique virtual QString name() const = 0; + /// Validation State will default to Valid, but you can manipulate it by overriding in an inherited class + virtual NodeValidationState validationState() const { return _nodeValidationState; } + public: QJsonObject save() const override; void load(QJsonObject const &) override; + void setValidatonState(const NodeValidationState &validationState); + public: virtual unsigned int nPorts(PortType portType) const = 0; @@ -128,6 +143,10 @@ public Q_SLOTS: private: NodeStyle _nodeStyle; + + NodeValidationState _nodeValidationState; }; } // namespace QtNodes + +Q_DECLARE_METATYPE(QtNodes::NodeValidationState) diff --git a/src/DataFlowGraphModel.cpp b/src/DataFlowGraphModel.cpp index 7256e3aeb..fdfdc0f09 100644 --- a/src/DataFlowGraphModel.cpp +++ b/src/DataFlowGraphModel.cpp @@ -233,9 +233,14 @@ QVariant DataFlowGraphModel::nodeData(NodeId nodeId, NodeRole role) const break; case NodeRole::Widget: { - auto w = model->embeddedWidget(); + auto *w = model->embeddedWidget(); result = QVariant::fromValue(w); } break; + + case NodeRole::ValidationState: { + auto validationState = model->validationState(); + result = QVariant::fromValue(validationState); + } break; } return result; @@ -295,6 +300,15 @@ bool DataFlowGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant valu case NodeRole::Widget: break; + + case NodeRole::ValidationState: { + if (value.canConvert()) { + auto state = value.value(); + if (auto node = delegateModel(nodeId); node != nullptr) { + node->setValidatonState(state); + } + } + } break; } return result; @@ -476,7 +490,8 @@ void DataFlowGraphModel::loadNode(QJsonObject const &nodeJson) connect(model.get(), &NodeDelegateModel::portsAboutToBeDeleted, this, - [restoredNodeId, this](PortType const portType, PortIndex const first, PortIndex const last) { + [restoredNodeId, + this](PortType const portType, PortIndex const first, PortIndex const last) { portsAboutToBeDeleted(restoredNodeId, portType, first, last); }); @@ -488,7 +503,8 @@ void DataFlowGraphModel::loadNode(QJsonObject const &nodeJson) connect(model.get(), &NodeDelegateModel::portsAboutToBeInserted, this, - [restoredNodeId, this](PortType const portType, PortIndex const first, PortIndex const last) { + [restoredNodeId, + this](PortType const portType, PortIndex const first, PortIndex const last) { portsAboutToBeInserted(restoredNodeId, portType, first, last); }); diff --git a/src/DefaultNodePainter.cpp b/src/DefaultNodePainter.cpp index 8febe4cb1..04d85b548 100644 --- a/src/DefaultNodePainter.cpp +++ b/src/DefaultNodePainter.cpp @@ -9,6 +9,7 @@ #include "BasicGraphicsScene.hpp" #include "ConnectionGraphicsObject.hpp" #include "ConnectionIdUtils.hpp" +#include "NodeDelegateModel.hpp" #include "NodeGraphicsObject.hpp" #include "NodeState.hpp" #include "StyleCollection.hpp" @@ -32,6 +33,8 @@ void DefaultNodePainter::paint(QPainter *painter, NodeGraphicsObject &ngo) const drawEntryLabels(painter, ngo); drawResizeRect(painter, ngo); + + drawValidationRect(painter, ngo); } void DefaultNodePainter::drawNodeRect(QPainter *painter, NodeGraphicsObject &ngo) const @@ -270,4 +273,55 @@ void DefaultNodePainter::drawResizeRect(QPainter *painter, NodeGraphicsObject &n } } +void DefaultNodePainter::drawValidationRect(QPainter *painter, NodeGraphicsObject &ngo) const +{ + AbstractGraphModel const &model = ngo.graphModel(); + + NodeId const nodeId = ngo.nodeId(); + + QVariant var = model.nodeData(nodeId, NodeRole::ValidationState); + if (var.canConvert()) { + auto state = var.value(); + if (state._isValid) { + return; + } + QString const errorMsg = state._errorMessage; + + QtNodes::AbstractNodeGeometry const &geometry = ngo.nodeScene()->nodeGeometry(); + QSize const size = geometry.size(nodeId); + + QJsonDocument json = QJsonDocument::fromVariant(model.nodeData(nodeId, NodeRole::Style)); + NodeStyle nodeStyle(json.object()); + + QtNodes::NodeStyle style(json.object()); + + auto color = ngo.isSelected() ? nodeStyle.SelectedBoundaryColor + : nodeStyle.NormalBoundaryColor; + QPen pen(color, ngo.nodeState().hovered() ? nodeStyle.HoveredPenWidth : nodeStyle.PenWidth); + painter->setPen(pen); + + painter->setBrush(nodeStyle.ErrorColor); + + float diam = style.ConnectionPointDiameter; + + QFontMetrics metrics(painter->font()); + QRect textRect = metrics.boundingRect(errorMsg); + unsigned int validationHeight = textRect.height() + diam; + + QRectF boundary(-diam, + -diam + size.height() - validationHeight, + size.width() + 2.0 * diam, + validationHeight + 2.0 * diam); + + double const radius = 3.0; + painter->drawRoundedRect(boundary, radius, radius); + + QPointF position((size.width() - textRect.width()) / 2.0, + size.height() - (validationHeight - textRect.height())); + + painter->setPen(style.FontColor); + painter->drawText(position, errorMsg); + } +} + } // namespace QtNodes diff --git a/src/NodeDelegateModel.cpp b/src/NodeDelegateModel.cpp index 94e47ad68..34cf2cb22 100644 --- a/src/NodeDelegateModel.cpp +++ b/src/NodeDelegateModel.cpp @@ -24,6 +24,11 @@ void NodeDelegateModel::load(QJsonObject const &) // } +void NodeDelegateModel::setValidatonState(const NodeValidationState &validationState) +{ + _nodeValidationState = validationState; +} + ConnectionPolicy NodeDelegateModel::portConnectionPolicy(PortType portType, PortIndex) const { auto result = ConnectionPolicy::One; From 1c847507183d64efe54743662360b63efed3a06f Mon Sep 17 00:00:00 2001 From: Taiguara Tupinambas Date: Sun, 22 Jun 2025 15:20:18 -0300 Subject: [PATCH 02/22] makes the nodeObject red in case of invalid state and adds a tooltip for error msg --- examples/calculator/DivisionModel.hpp | 4 +- .../QtNodes/internal/DefaultNodePainter.hpp | 2 - resources/DefaultStyle.json | 2 +- src/DataFlowGraphModel.cpp | 1 + src/DefaultNodePainter.cpp | 84 +++++-------------- src/NodeGraphicsObject.cpp | 22 +++-- 6 files changed, 42 insertions(+), 73 deletions(-) diff --git a/examples/calculator/DivisionModel.hpp b/examples/calculator/DivisionModel.hpp index ab0753b6d..4b5893614 100644 --- a/examples/calculator/DivisionModel.hpp +++ b/examples/calculator/DivisionModel.hpp @@ -66,8 +66,8 @@ class DivisionModel : public MathOperationDataModel setValidatonState(state); _result = std::make_shared(n1->number() / n2->number()); } else { - //modelValidationState = NodeValidationState::Warning; - //modelValidationError = QStringLiteral("Missing or incorrect inputs"); + QtNodes::NodeValidationState state; + setValidatonState(state); _result.reset(); } diff --git a/include/QtNodes/internal/DefaultNodePainter.hpp b/include/QtNodes/internal/DefaultNodePainter.hpp index d9d2be817..484969f9a 100644 --- a/include/QtNodes/internal/DefaultNodePainter.hpp +++ b/include/QtNodes/internal/DefaultNodePainter.hpp @@ -30,7 +30,5 @@ class NODE_EDITOR_PUBLIC DefaultNodePainter : public AbstractNodePainter void drawEntryLabels(QPainter *painter, NodeGraphicsObject &ngo) const; void drawResizeRect(QPainter *painter, NodeGraphicsObject &ngo) const; - - void drawValidationRect(QPainter *painter, NodeGraphicsObject &ngo) const; }; } // namespace QtNodes diff --git a/resources/DefaultStyle.json b/resources/DefaultStyle.json index 4fe334063..2335ce6c2 100644 --- a/resources/DefaultStyle.json +++ b/resources/DefaultStyle.json @@ -17,7 +17,7 @@ "FontColorFaded" : "gray", "ConnectionPointColor": [169, 169, 169], "FilledConnectionPointColor": "cyan", - "ErrorColor": "red", + "ErrorColor": [255, 105, 97], "WarningColor": [128, 128, 0], "PenWidth": 1.0, diff --git a/src/DataFlowGraphModel.cpp b/src/DataFlowGraphModel.cpp index fdfdc0f09..f3710deef 100644 --- a/src/DataFlowGraphModel.cpp +++ b/src/DataFlowGraphModel.cpp @@ -308,6 +308,7 @@ bool DataFlowGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant valu node->setValidatonState(state); } } + Q_EMIT nodeUpdated(nodeId); } break; } diff --git a/src/DefaultNodePainter.cpp b/src/DefaultNodePainter.cpp index 04d85b548..350f249e0 100644 --- a/src/DefaultNodePainter.cpp +++ b/src/DefaultNodePainter.cpp @@ -33,8 +33,6 @@ void DefaultNodePainter::paint(QPainter *painter, NodeGraphicsObject &ngo) const drawEntryLabels(painter, ngo); drawResizeRect(painter, ngo); - - drawValidationRect(painter, ngo); } void DefaultNodePainter::drawNodeRect(QPainter *painter, NodeGraphicsObject &ngo) const @@ -51,7 +49,18 @@ void DefaultNodePainter::drawNodeRect(QPainter *painter, NodeGraphicsObject &ngo NodeStyle nodeStyle(json.object()); - auto color = ngo.isSelected() ? nodeStyle.SelectedBoundaryColor : nodeStyle.NormalBoundaryColor; + QVariant var = model.nodeData(nodeId, NodeRole::ValidationState); + bool invalid = false; + if (var.canConvert()) { + auto state = var.value(); + invalid = !state._isValid; + } + + QColor color = ngo.isSelected() ? nodeStyle.SelectedBoundaryColor + : nodeStyle.NormalBoundaryColor; + if (invalid) { + color = nodeStyle.ErrorColor; + } if (ngo.nodeState().hovered()) { QPen p(color, nodeStyle.HoveredPenWidth); @@ -61,15 +70,17 @@ void DefaultNodePainter::drawNodeRect(QPainter *painter, NodeGraphicsObject &ngo painter->setPen(p); } - QLinearGradient gradient(QPointF(0.0, 0.0), QPointF(2.0, size.height())); - - gradient.setColorAt(0.0, nodeStyle.GradientColor0); - gradient.setColorAt(0.10, nodeStyle.GradientColor1); - gradient.setColorAt(0.90, nodeStyle.GradientColor2); - gradient.setColorAt(1.0, nodeStyle.GradientColor3); - - painter->setBrush(gradient); + if (invalid) { + painter->setBrush(nodeStyle.ErrorColor); + } else { + QLinearGradient gradient(QPointF(0.0, 0.0), QPointF(2.0, size.height())); + gradient.setColorAt(0.0, nodeStyle.GradientColor0); + gradient.setColorAt(0.10, nodeStyle.GradientColor1); + gradient.setColorAt(0.90, nodeStyle.GradientColor2); + gradient.setColorAt(1.0, nodeStyle.GradientColor3); + painter->setBrush(gradient); + } QRectF boundary(0, 0, size.width(), size.height()); double const radius = 3.0; @@ -273,55 +284,4 @@ void DefaultNodePainter::drawResizeRect(QPainter *painter, NodeGraphicsObject &n } } -void DefaultNodePainter::drawValidationRect(QPainter *painter, NodeGraphicsObject &ngo) const -{ - AbstractGraphModel const &model = ngo.graphModel(); - - NodeId const nodeId = ngo.nodeId(); - - QVariant var = model.nodeData(nodeId, NodeRole::ValidationState); - if (var.canConvert()) { - auto state = var.value(); - if (state._isValid) { - return; - } - QString const errorMsg = state._errorMessage; - - QtNodes::AbstractNodeGeometry const &geometry = ngo.nodeScene()->nodeGeometry(); - QSize const size = geometry.size(nodeId); - - QJsonDocument json = QJsonDocument::fromVariant(model.nodeData(nodeId, NodeRole::Style)); - NodeStyle nodeStyle(json.object()); - - QtNodes::NodeStyle style(json.object()); - - auto color = ngo.isSelected() ? nodeStyle.SelectedBoundaryColor - : nodeStyle.NormalBoundaryColor; - QPen pen(color, ngo.nodeState().hovered() ? nodeStyle.HoveredPenWidth : nodeStyle.PenWidth); - painter->setPen(pen); - - painter->setBrush(nodeStyle.ErrorColor); - - float diam = style.ConnectionPointDiameter; - - QFontMetrics metrics(painter->font()); - QRect textRect = metrics.boundingRect(errorMsg); - unsigned int validationHeight = textRect.height() + diam; - - QRectF boundary(-diam, - -diam + size.height() - validationHeight, - size.width() + 2.0 * diam, - validationHeight + 2.0 * diam); - - double const radius = 3.0; - painter->drawRoundedRect(boundary, radius, radius); - - QPointF position((size.width() - textRect.width()) / 2.0, - size.height() - (validationHeight - textRect.height())); - - painter->setPen(style.FontColor); - painter->drawText(position, errorMsg); - } -} - } // namespace QtNodes diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index cf4236baf..36c886376 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -13,6 +13,7 @@ #include "ConnectionGraphicsObject.hpp" #include "ConnectionIdUtils.hpp" #include "NodeConnectionInteraction.hpp" +#include "NodeDelegateModel.hpp" #include "StyleCollection.hpp" #include "UndoCommands.hpp" @@ -37,8 +38,7 @@ NodeGraphicsObject::NodeGraphicsObject(BasicGraphicsScene &scene, NodeId nodeId) NodeStyle nodeStyle(nodeStyleJson); - if(nodeStyle.ShadowEnabled) - { + if (nodeStyle.ShadowEnabled) { auto effect = new QGraphicsDropShadowEffect; effect->setOffset(4, 4); effect->setBlurRadius(20); @@ -79,10 +79,10 @@ BasicGraphicsScene *NodeGraphicsObject::nodeScene() const void NodeGraphicsObject::updateQWidgetEmbedPos() { - if (_proxyWidget) { - AbstractNodeGeometry &geometry = nodeScene()->nodeGeometry(); - _proxyWidget->setPos(geometry.widgetPosition(_nodeId)); - } + if (_proxyWidget) { + AbstractNodeGeometry &geometry = nodeScene()->nodeGeometry(); + _proxyWidget->setPos(geometry.widgetPosition(_nodeId)); + } } void NodeGraphicsObject::embedQWidget() @@ -161,6 +161,16 @@ void NodeGraphicsObject::reactToConnection(ConnectionGraphicsObject const *cgo) void NodeGraphicsObject::paint(QPainter *painter, QStyleOptionGraphicsItem const *option, QWidget *) { + QString tooltip; + QVariant var = _graphModel.nodeData(_nodeId, NodeRole::ValidationState); + if (var.canConvert()) { + auto state = var.value(); + if (!state._isValid) { + tooltip = state._errorMessage; + } + } + setToolTip(tooltip); + painter->setClipRect(option->exposedRect); nodeScene()->nodePainter().paint(painter, *this); From 3007c23e1c35d8d256d1aec5b7107e94fb01aa11 Mon Sep 17 00:00:00 2001 From: Taiguara Tupinambas Date: Sun, 22 Jun 2025 15:34:37 -0300 Subject: [PATCH 03/22] adds warning state and adapts calculator example --- examples/calculator/DivisionModel.hpp | 9 +++++-- .../QtNodes/internal/NodeDelegateModel.hpp | 11 ++++++--- src/DefaultNodePainter.cpp | 24 +++++++++++++------ src/NodeGraphicsObject.cpp | 4 ++-- 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/examples/calculator/DivisionModel.hpp b/examples/calculator/DivisionModel.hpp index 4b5893614..3643433a0 100644 --- a/examples/calculator/DivisionModel.hpp +++ b/examples/calculator/DivisionModel.hpp @@ -57,12 +57,17 @@ class DivisionModel : public MathOperationDataModel if (n2 && (n2->number() == 0.0)) { QtNodes::NodeValidationState state; - state._isValid = false; - state._errorMessage = QStringLiteral("Division by zero error"); + state._state = QtNodes::NodeValidationState::State::Error; + state._stateMessage = QStringLiteral("Division by zero error"); setValidatonState(state); _result.reset(); } else if (n1 && n2) { QtNodes::NodeValidationState state; + + if (n2->number() < 1e-5) { + state._state = QtNodes::NodeValidationState::State::Warning; + state._stateMessage = QStringLiteral("Dividend very small. Calculation might overflow"); + } setValidatonState(state); _result = std::make_shared(n1->number() / n2->number()); } else { diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index 9ee4a8bfb..1917e3599 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -14,12 +14,17 @@ namespace QtNodes { /** - * Describes whether a node configuration is usable. + * Describes whether a node configuration is usable and defines a description message */ struct NodeValidationState { - bool _isValid{true}; - QString _errorMessage{""}; + enum class State : int { + Valid, /// All required inputs are present and correct. + Warning, /// Some inputs are missing or questionable, processing may be unreliable. + Error /// Inputs or settings are invalid, preventing successful computation. + }; + State _state{State::Valid}; + QString _stateMessage{""}; }; class StyleCollection; diff --git a/src/DefaultNodePainter.cpp b/src/DefaultNodePainter.cpp index 350f249e0..5142e161b 100644 --- a/src/DefaultNodePainter.cpp +++ b/src/DefaultNodePainter.cpp @@ -51,15 +51,25 @@ void DefaultNodePainter::drawNodeRect(QPainter *painter, NodeGraphicsObject &ngo QVariant var = model.nodeData(nodeId, NodeRole::ValidationState); bool invalid = false; - if (var.canConvert()) { - auto state = var.value(); - invalid = !state._isValid; - } QColor color = ngo.isSelected() ? nodeStyle.SelectedBoundaryColor : nodeStyle.NormalBoundaryColor; - if (invalid) { - color = nodeStyle.ErrorColor; + + if (var.canConvert()) { + auto state = var.value(); + switch (state._state) { + case NodeValidationState::State::Error: { + invalid = true; + color = nodeStyle.ErrorColor; + } break; + case NodeValidationState::State::Warning: { + invalid = true; + color = nodeStyle.WarningColor; + break; + default: + break; + } + } } if (ngo.nodeState().hovered()) { @@ -71,7 +81,7 @@ void DefaultNodePainter::drawNodeRect(QPainter *painter, NodeGraphicsObject &ngo } if (invalid) { - painter->setBrush(nodeStyle.ErrorColor); + painter->setBrush(color); } else { QLinearGradient gradient(QPointF(0.0, 0.0), QPointF(2.0, size.height())); gradient.setColorAt(0.0, nodeStyle.GradientColor0); diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index 36c886376..8b886f21e 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -165,8 +165,8 @@ void NodeGraphicsObject::paint(QPainter *painter, QStyleOptionGraphicsItem const QVariant var = _graphModel.nodeData(_nodeId, NodeRole::ValidationState); if (var.canConvert()) { auto state = var.value(); - if (!state._isValid) { - tooltip = state._errorMessage; + if (state._state != NodeValidationState::State::Valid) { + tooltip = state._stateMessage; } } setToolTip(tooltip); From 129d414689b915922a1faa893d8aafbf5cd18968 Mon Sep 17 00:00:00 2001 From: Taiguara Tupinambas Date: Sun, 22 Jun 2025 16:22:49 -0300 Subject: [PATCH 04/22] adds validation icon and adapts calculation example --- examples/calculator/DivisionModel.hpp | 17 ++++---- .../QtNodes/internal/DefaultNodePainter.hpp | 6 +++ include/QtNodes/internal/NodeStyle.hpp | 1 + resources/DefaultStyle.json | 5 ++- resources/info-tooltip.svg | 6 +++ resources/resources.qrc | 5 ++- src/DefaultNodePainter.cpp | 40 +++++++++++++++++++ 7 files changed, 69 insertions(+), 11 deletions(-) create mode 100644 resources/info-tooltip.svg diff --git a/examples/calculator/DivisionModel.hpp b/examples/calculator/DivisionModel.hpp index 3643433a0..ea5e6b09a 100644 --- a/examples/calculator/DivisionModel.hpp +++ b/examples/calculator/DivisionModel.hpp @@ -55,19 +55,22 @@ class DivisionModel : public MathOperationDataModel auto n1 = _number1.lock(); auto n2 = _number2.lock(); + QtNodes::NodeValidationState state; if (n2 && (n2->number() == 0.0)) { - QtNodes::NodeValidationState state; state._state = QtNodes::NodeValidationState::State::Error; state._stateMessage = QStringLiteral("Division by zero error"); setValidatonState(state); _result.reset(); - } else if (n1 && n2) { - QtNodes::NodeValidationState state; - - if (n2->number() < 1e-5) { - state._state = QtNodes::NodeValidationState::State::Warning; - state._stateMessage = QStringLiteral("Dividend very small. Calculation might overflow"); + } else if ( n2 && (n2->number() < 1e-5)) { + state._state = QtNodes::NodeValidationState::State::Warning; + state._stateMessage = QStringLiteral("Very small divident. Result might overflow"); + setValidatonState(state); + if (n1) { + _result = std::make_shared(n1->number() / n2->number()); + } else { + _result.reset(); } + } else if (n1 && n2) { setValidatonState(state); _result = std::make_shared(n1->number() / n2->number()); } else { diff --git a/include/QtNodes/internal/DefaultNodePainter.hpp b/include/QtNodes/internal/DefaultNodePainter.hpp index 484969f9a..589800ddd 100644 --- a/include/QtNodes/internal/DefaultNodePainter.hpp +++ b/include/QtNodes/internal/DefaultNodePainter.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include "AbstractNodePainter.hpp" @@ -30,5 +31,10 @@ class NODE_EDITOR_PUBLIC DefaultNodePainter : public AbstractNodePainter void drawEntryLabels(QPainter *painter, NodeGraphicsObject &ngo) const; void drawResizeRect(QPainter *painter, NodeGraphicsObject &ngo) const; + + void drawValidationIcon(QPainter *painter, NodeGraphicsObject &ngo) const; + +private: + QIcon _toolTipIcon{"://info-tooltip.svg"}; }; } // namespace QtNodes diff --git a/include/QtNodes/internal/NodeStyle.hpp b/include/QtNodes/internal/NodeStyle.hpp index 85abc5612..9cb4a250c 100644 --- a/include/QtNodes/internal/NodeStyle.hpp +++ b/include/QtNodes/internal/NodeStyle.hpp @@ -43,6 +43,7 @@ class NODE_EDITOR_PUBLIC NodeStyle : public Style QColor WarningColor; QColor ErrorColor; + QColor ToolTipIconColor; float PenWidth; float HoveredPenWidth; diff --git a/resources/DefaultStyle.json b/resources/DefaultStyle.json index 2335ce6c2..2df69bfd2 100644 --- a/resources/DefaultStyle.json +++ b/resources/DefaultStyle.json @@ -17,8 +17,9 @@ "FontColorFaded" : "gray", "ConnectionPointColor": [169, 169, 169], "FilledConnectionPointColor": "cyan", - "ErrorColor": [255, 105, 97], - "WarningColor": [128, 128, 0], + "ErrorColor": [211, 47, 47], + "WarningColor": [255, 179, 0], + "ToolTipIconColor": "white", "PenWidth": 1.0, "HoveredPenWidth": 1.5, diff --git a/resources/info-tooltip.svg b/resources/info-tooltip.svg new file mode 100644 index 000000000..7ffca6ca6 --- /dev/null +++ b/resources/info-tooltip.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/resources/resources.qrc b/resources/resources.qrc index a0b5ef8ba..08aec37e6 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -1,5 +1,6 @@ - - + + DefaultStyle.json + info-tooltip.svg diff --git a/src/DefaultNodePainter.cpp b/src/DefaultNodePainter.cpp index 5142e161b..b7a5a8e73 100644 --- a/src/DefaultNodePainter.cpp +++ b/src/DefaultNodePainter.cpp @@ -33,6 +33,8 @@ void DefaultNodePainter::paint(QPainter *painter, NodeGraphicsObject &ngo) const drawEntryLabels(painter, ngo); drawResizeRect(painter, ngo); + + drawValidationIcon(painter, ngo); } void DefaultNodePainter::drawNodeRect(QPainter *painter, NodeGraphicsObject &ngo) const @@ -294,4 +296,42 @@ void DefaultNodePainter::drawResizeRect(QPainter *painter, NodeGraphicsObject &n } } +void DefaultNodePainter::drawValidationIcon(QPainter *painter, NodeGraphicsObject &ngo) const +{ + AbstractGraphModel &model = ngo.graphModel(); + NodeId const nodeId = ngo.nodeId(); + AbstractNodeGeometry &geometry = ngo.nodeScene()->nodeGeometry(); + + QVariant var = model.nodeData(nodeId, NodeRole::ValidationState); + if (!var.canConvert()) + return; + + auto state = var.value(); + if (state._state == NodeValidationState::State::Valid) + return; + + QJsonDocument json = QJsonDocument::fromVariant(model.nodeData(nodeId, NodeRole::Style)); + NodeStyle nodeStyle(json.object()); + + QSize size = geometry.size(nodeId); + + QIcon icon(":/info-tooltip.svg"); + QSize iconSize(16, 16); + QPixmap pixmap = icon.pixmap(iconSize); + + QColor color = (state._state == NodeValidationState::State::Error) ? nodeStyle.ErrorColor + : nodeStyle.WarningColor; + + QPainter imgPainter(&pixmap); + imgPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); + imgPainter.fillRect(pixmap.rect(), color); + imgPainter.end(); + + QPointF center(size.width(), 0.0); + center += QPointF(iconSize.width() / 2.0, -iconSize.height() / 2.0); + + painter->drawPixmap(center.toPoint() - QPoint(iconSize.width() / 2, iconSize.height() / 2), + pixmap); +} + } // namespace QtNodes From e0b0c4a51565ac5eca75067d18db24e77c54187d Mon Sep 17 00:00:00 2001 From: g-abilio Date: Mon, 14 Jul 2025 14:03:05 -0300 Subject: [PATCH 05/22] core improvements to develop node processing status --- .../QtNodes/internal/DefaultNodePainter.hpp | 2 + include/QtNodes/internal/Definitions.hpp | 29 +++---- .../QtNodes/internal/NodeDelegateModel.hpp | 32 ++++++- .../QtNodes/internal/NodeGraphicsObject.hpp | 25 ++++++ resources/resources.qrc | 6 ++ resources/status_icons/empty.svg | 22 +++++ resources/status_icons/failed.svg | 78 +++++++++++++++++ resources/status_icons/partial.svg | 26 ++++++ resources/status_icons/pending.svg | 84 +++++++++++++++++++ resources/status_icons/processing.svg | 80 ++++++++++++++++++ resources/status_icons/updated.svg | 15 ++++ src/BasicGraphicsScene.cpp | 2 + src/DataFlowGraphModel.cpp | 15 ++++ src/DefaultNodePainter.cpp | 36 ++++++++ src/NodeDelegateModel.cpp | 5 ++ src/NodeGraphicsObject.cpp | 75 +++++++++++++++++ 16 files changed, 517 insertions(+), 15 deletions(-) create mode 100644 resources/status_icons/empty.svg create mode 100644 resources/status_icons/failed.svg create mode 100644 resources/status_icons/partial.svg create mode 100644 resources/status_icons/pending.svg create mode 100644 resources/status_icons/processing.svg create mode 100644 resources/status_icons/updated.svg diff --git a/include/QtNodes/internal/DefaultNodePainter.hpp b/include/QtNodes/internal/DefaultNodePainter.hpp index 589800ddd..953faa065 100644 --- a/include/QtNodes/internal/DefaultNodePainter.hpp +++ b/include/QtNodes/internal/DefaultNodePainter.hpp @@ -32,6 +32,8 @@ class NODE_EDITOR_PUBLIC DefaultNodePainter : public AbstractNodePainter void drawResizeRect(QPainter *painter, NodeGraphicsObject &ngo) const; + void drawProcessingIndicator(QPainter *painter, NodeGraphicsObject &ngo) const; + void drawValidationIcon(QPainter *painter, NodeGraphicsObject &ngo) const; private: diff --git a/include/QtNodes/internal/Definitions.hpp b/include/QtNodes/internal/Definitions.hpp index efd9bbd3e..592e874e1 100644 --- a/include/QtNodes/internal/Definitions.hpp +++ b/include/QtNodes/internal/Definitions.hpp @@ -18,22 +18,23 @@ NODE_EDITOR_PUBLIC Q_NAMESPACE Q_NAMESPACE_EXPORT(NODE_EDITOR_PUBLIC) #endif -/** + /** * Constants used for fetching QVariant data from GraphModel. */ -enum class NodeRole { - Type = 0, ///< Type of the current node, usually a string. - Position = 1, ///< `QPointF` positon of the node on the scene. - Size = 2, ///< `QSize` for resizable nodes. - CaptionVisible = 3, ///< `bool` for caption visibility. - Caption = 4, ///< `QString` for node caption. - Style = 5, ///< Custom NodeStyle as QJsonDocument - InternalData = 6, ///< Node-stecific user data as QJsonObject - InPortCount = 7, ///< `unsigned int` - OutPortCount = 9, ///< `unsigned int` - Widget = 10, ///< Optional `QWidget*` or `nullptr` - ValidationState = 11, ///< Enum NodeValidationState of the node -}; + enum class NodeRole { + Type = 0, ///< Type of the current node, usually a string. + Position = 1, ///< `QPointF` positon of the node on the scene. + Size = 2, ///< `QSize` for resizable nodes. + CaptionVisible = 3, ///< `bool` for caption visibility. + Caption = 4, ///< `QString` for node caption. + Style = 5, ///< Custom NodeStyle as QJsonDocument + InternalData = 6, ///< Node-stecific user data as QJsonObject + InPortCount = 7, ///< `unsigned int` + OutPortCount = 9, ///< `unsigned int` + Widget = 10, ///< Optional `QWidget*` or `nullptr` + ValidationState = 11, ///< Enum NodeValidationState of the node + ProcessingStatus = 12 ///< Enum NodeProcessingStatus of the node + }; Q_ENUM_NS(NodeRole) /** diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index 1917e3599..e4b13fe13 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -35,7 +35,9 @@ class StyleCollection; * AbstractGraphModel. * This class is the same what has been called NodeDataModel before v3. */ -class NODE_EDITOR_PUBLIC NodeDelegateModel : public QObject, public Serializable +class NODE_EDITOR_PUBLIC NodeDelegateModel + : public QObject + , public Serializable { Q_OBJECT @@ -44,6 +46,24 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel : public QObject, public Serializable virtual ~NodeDelegateModel() = default; + /** + * Describes the node status, depending on its current situation + */ + struct NodeProcessingStatus + { + enum class Status : int { + NoStatus = 0, /// + Updated = 1, /// + Processing = 2, /// + Pending = 3, /// + Empty = 4, /// + Failed = 5, /// + Partial = 6, /// + }; + + Status _status{Status::NoStatus}; + }; + /// It is possible to hide caption in GUI virtual bool captionVisible() const { return true; } @@ -62,6 +82,12 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel : public QObject, public Serializable /// Validation State will default to Valid, but you can manipulate it by overriding in an inherited class virtual NodeValidationState validationState() const { return _nodeValidationState; } + /// Returns the curent processing status + virtual NodeProcessingStatus::Status processingStatus() const + { + return _processingStatus._status; + } + public: QJsonObject save() const override; @@ -81,6 +107,8 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel : public QObject, public Serializable void setNodeStyle(NodeStyle const &style); + void setNodeProcessingStatus(NodeProcessingStatus status); + public: virtual void setInData(std::shared_ptr nodeData, PortIndex const portIndex) = 0; @@ -150,6 +178,8 @@ public Q_SLOTS: NodeStyle _nodeStyle; NodeValidationState _nodeValidationState; + + NodeProcessingStatus _processingStatus; }; } // namespace QtNodes diff --git a/include/QtNodes/internal/NodeGraphicsObject.hpp b/include/QtNodes/internal/NodeGraphicsObject.hpp index 50ce7be50..f24f81d4e 100644 --- a/include/QtNodes/internal/NodeGraphicsObject.hpp +++ b/include/QtNodes/internal/NodeGraphicsObject.hpp @@ -1,5 +1,7 @@ #pragma once +#include +#include #include #include @@ -52,6 +54,16 @@ class NodeGraphicsObject : public QGraphicsObject void updateQWidgetEmbedPos(); + void updateStatusIconSize() const; + + const QIcon processingStatusIcon() const; + + QRect statusIconRect() const; + + QSize statusIconSize() const; + + QColor getStatusColor() { return _status_color; } + protected: void paint(QPainter *painter, QStyleOptionGraphicsItem const *option, @@ -89,5 +101,18 @@ class NodeGraphicsObject : public QGraphicsObject // either nullptr or owned by parent QGraphicsItem QGraphicsProxyWidget *_proxyWidget; + + mutable bool _statusIconActive; + + mutable QSize _statusIconSize; + + mutable QColor _status_color; + + const QIcon _statusUpdated{"://status_icons/updated.svg"}; + const QIcon _statusProcessing{"://status_icons/processing.svg"}; + const QIcon _statusPending{"://status_icons/pending.svg"}; + const QIcon _statusInvalid{"://status_icons/failed.svg"}; + const QIcon _statusEmpty{"://status_icons/empty.svg"}; + const QIcon _statusPartial{"://status_icons/partial.svg"}; }; } // namespace QtNodes diff --git a/resources/resources.qrc b/resources/resources.qrc index 08aec37e6..f9da26fcd 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -2,5 +2,11 @@ DefaultStyle.json info-tooltip.svg + status_icons/empty.svg + status_icons/failed.svg + status_icons/partial.svg + status_icons/pending.svg + status_icons/processing.svg + status_icons/updated.svg diff --git a/resources/status_icons/empty.svg b/resources/status_icons/empty.svg new file mode 100644 index 000000000..e38cd34ae --- /dev/null +++ b/resources/status_icons/empty.svg @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/resources/status_icons/failed.svg b/resources/status_icons/failed.svg new file mode 100644 index 000000000..0b94f245c --- /dev/null +++ b/resources/status_icons/failed.svg @@ -0,0 +1,78 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/resources/status_icons/partial.svg b/resources/status_icons/partial.svg new file mode 100644 index 000000000..ec991dc5d --- /dev/null +++ b/resources/status_icons/partial.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/resources/status_icons/pending.svg b/resources/status_icons/pending.svg new file mode 100644 index 000000000..c850c9132 --- /dev/null +++ b/resources/status_icons/pending.svg @@ -0,0 +1,84 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/resources/status_icons/processing.svg b/resources/status_icons/processing.svg new file mode 100644 index 000000000..74b82bc3a --- /dev/null +++ b/resources/status_icons/processing.svg @@ -0,0 +1,80 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/status_icons/updated.svg b/resources/status_icons/updated.svg new file mode 100644 index 000000000..c2485802e --- /dev/null +++ b/resources/status_icons/updated.svg @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/src/BasicGraphicsScene.cpp b/src/BasicGraphicsScene.cpp index 10e7b7527..9009071a0 100644 --- a/src/BasicGraphicsScene.cpp +++ b/src/BasicGraphicsScene.cpp @@ -3,11 +3,13 @@ #include "AbstractNodeGeometry.hpp" #include "ConnectionGraphicsObject.hpp" #include "ConnectionIdUtils.hpp" +#include "DataFlowGraphModel.hpp" #include "DefaultConnectionPainter.hpp" #include "DefaultHorizontalNodeGeometry.hpp" #include "DefaultNodePainter.hpp" #include "DefaultVerticalNodeGeometry.hpp" #include "GraphicsView.hpp" +#include "NodeDelegateModel.hpp" #include "NodeGraphicsObject.hpp" #include diff --git a/src/DataFlowGraphModel.cpp b/src/DataFlowGraphModel.cpp index f3710deef..0f173cfdc 100644 --- a/src/DataFlowGraphModel.cpp +++ b/src/DataFlowGraphModel.cpp @@ -241,6 +241,11 @@ QVariant DataFlowGraphModel::nodeData(NodeId nodeId, NodeRole role) const auto validationState = model->validationState(); result = QVariant::fromValue(validationState); } break; + + case NodeRole::ProcessingStatus: { + auto processingStatus = model->processingStatus(); + result = QVariant::fromValue(processingStatus); + } break; } return result; @@ -310,6 +315,16 @@ bool DataFlowGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant valu } Q_EMIT nodeUpdated(nodeId); } break; + + case NodeRole::ProcessingStatus: { + if (value.canConvert()) { + auto status = value.value(); + if (auto node = delegateModel(nodeId); node != nullptr) { + node->setNodeProcessingStatus(status); + } + } + Q_EMIT nodeUpdated(nodeId); + } break; } return result; diff --git a/src/DefaultNodePainter.cpp b/src/DefaultNodePainter.cpp index b7a5a8e73..60d2115f6 100644 --- a/src/DefaultNodePainter.cpp +++ b/src/DefaultNodePainter.cpp @@ -9,6 +9,7 @@ #include "BasicGraphicsScene.hpp" #include "ConnectionGraphicsObject.hpp" #include "ConnectionIdUtils.hpp" +#include "DataFlowGraphModel.hpp" #include "NodeDelegateModel.hpp" #include "NodeGraphicsObject.hpp" #include "NodeState.hpp" @@ -32,6 +33,8 @@ void DefaultNodePainter::paint(QPainter *painter, NodeGraphicsObject &ngo) const drawEntryLabels(painter, ngo); + drawProcessingIndicator(painter, ngo); + drawResizeRect(painter, ngo); drawValidationIcon(painter, ngo); @@ -296,6 +299,39 @@ void DefaultNodePainter::drawResizeRect(QPainter *painter, NodeGraphicsObject &n } } +void DefaultNodePainter::drawProcessingIndicator(QPainter *painter, NodeGraphicsObject &ngo) const +{ + AbstractGraphModel &model = ngo.graphModel(); + NodeId const nodeId = ngo.nodeId(); + + auto *dfModel = dynamic_cast(&model); + if (!dfModel) + return; + + auto *delegate = dfModel->delegateModel(nodeId); + if (!delegate) + return; + + AbstractNodeGeometry &geometry = ngo.nodeScene()->nodeGeometry(); + + ngo.updateStatusIconSize(); + QSize size = geometry.size(nodeId); + + QIcon icon = ngo.processingStatusIcon(); + QSize iconSize(16, 16); + QPixmap pixmap = icon.pixmap(iconSize); + + QColor color = ngo.getStatusColor(); + + QPainter imgPainter(&pixmap); + imgPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); + imgPainter.fillRect(pixmap.rect(), color); + imgPainter.end(); + + QRect r(size.width() - 12.0, size.height() - 12.0, 8.0, 8.0); + painter->drawPixmap(r, pixmap); +} + void DefaultNodePainter::drawValidationIcon(QPainter *painter, NodeGraphicsObject &ngo) const { AbstractGraphModel &model = ngo.graphModel(); diff --git a/src/NodeDelegateModel.cpp b/src/NodeDelegateModel.cpp index 34cf2cb22..834976a34 100644 --- a/src/NodeDelegateModel.cpp +++ b/src/NodeDelegateModel.cpp @@ -56,4 +56,9 @@ void NodeDelegateModel::setNodeStyle(NodeStyle const &style) _nodeStyle = style; } +void NodeDelegateModel::setNodeProcessingStatus(NodeProcessingStatus status) +{ + _processingStatus = status; +} + } // namespace QtNodes diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index 8b886f21e..a558cc3b9 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -65,6 +66,15 @@ NodeGraphicsObject::NodeGraphicsObject(BasicGraphicsScene &scene, NodeId nodeId) if (_nodeId == nodeId) setLockedState(); }); + + QVariant var = _graphModel.nodeData(_nodeId, NodeRole::ProcessingStatus); + auto processingStatusValue = var.value() + ._status; + + _statusIconActive = processingStatusValue + != QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::NoStatus; + _statusIconSize.setWidth(_statusIconActive ? 32 : 0); + _statusIconSize.setHeight(_statusIconActive ? 32 : 0); } AbstractGraphModel &NodeGraphicsObject::graphModel() const @@ -379,4 +389,69 @@ void NodeGraphicsObject::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) Q_EMIT nodeScene()->nodeContextMenu(_nodeId, mapToScene(event->pos())); } +void NodeGraphicsObject::updateStatusIconSize() const +{ + QVariant var = _graphModel.nodeData(_nodeId, NodeRole::ProcessingStatus); + auto processingStatusValue = var.value() + ._status; + + bool oldStatus = _statusIconActive; + _statusIconActive = processingStatusValue + != QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::NoStatus; + + if (oldStatus != _statusIconActive) { + _statusIconSize.setWidth(_statusIconActive ? 32 : 0); + _statusIconSize.setHeight(_statusIconActive ? 32 : 0); + } +} + +QRect NodeGraphicsObject::statusIconRect() const +{ + QVariant var = _graphModel.nodeData(_nodeId, NodeRole::ProcessingStatus); + + // auto spacing = static_cast(_spacing); + auto iconPos = + // = portScenePosition(std::max(var.value().nPorts(PortType::Out), + // var.value().nPorts(PortType::In)), + // PortType::Out) + // .toPoint() + // + + QPoint{-statusIconSize().width() / 2, 0}; + + return QRect{iconPos, statusIconSize()}; +} + +const QIcon NodeGraphicsObject::processingStatusIcon() const +{ + QVariant var = _graphModel.nodeData(_nodeId, NodeRole::ProcessingStatus); + + switch (var.value()._status) { + case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::NoStatus: + case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::Updated: + _status_color = QColor("green"); + return _statusUpdated; + case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::Processing: + _status_color = QColor("blue"); + return _statusProcessing; + case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::Pending: + _status_color = QColor("yellow"); + return _statusPending; + case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::Empty: + _status_color = QColor("gray"); + return _statusEmpty; + case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::Failed: + _status_color = QColor("red"); + return _statusInvalid; + case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::Partial: + _status_color = QColor("white"); + return _statusPartial; + } + return _statusInvalid; +} + +QSize NodeGraphicsObject::statusIconSize() const +{ + return _statusIconSize; +} + } // namespace QtNodes From e002f5bbce9da0f9f105e63b137feb85501a2620 Mon Sep 17 00:00:00 2001 From: g-abilio Date: Tue, 15 Jul 2025 16:20:00 -0300 Subject: [PATCH 06/22] first commit on the creation of a processing status example --- .../calculator/MathOperationDataModel.cpp | 15 ++++++-- examples/calculator/MultiplicationModel.hpp | 14 +++++++- .../QtNodes/internal/NodeDelegateModel.hpp | 2 +- src/BasicGraphicsScene.cpp | 34 +++++++++++++++++++ src/DataFlowGraphModel.cpp | 2 +- src/NodeDelegateModel.cpp | 4 +-- 6 files changed, 64 insertions(+), 7 deletions(-) diff --git a/examples/calculator/MathOperationDataModel.cpp b/examples/calculator/MathOperationDataModel.cpp index f6a3a26d0..0151d0476 100644 --- a/examples/calculator/MathOperationDataModel.cpp +++ b/examples/calculator/MathOperationDataModel.cpp @@ -1,6 +1,6 @@ #include "MathOperationDataModel.hpp" - #include "DecimalData.hpp" +#include unsigned int MathOperationDataModel::nPorts(PortType portType) const { @@ -38,5 +38,16 @@ void MathOperationDataModel::setInData(std::shared_ptr data, PortIndex _number2 = numberData; } - compute(); + Q_EMIT computingStarted(); + + QTimer *timer = new QTimer(this); + timer->start(1000); + int secondsRemaining = 10; + connect(timer, &QTimer::timeout, this, [=]() mutable { + if (--secondsRemaining <= 0) { + timer->stop(); + compute(); + Q_EMIT computingFinished(); + } + }); } diff --git a/examples/calculator/MultiplicationModel.hpp b/examples/calculator/MultiplicationModel.hpp index 127ae4011..40f9ffc62 100644 --- a/examples/calculator/MultiplicationModel.hpp +++ b/examples/calculator/MultiplicationModel.hpp @@ -1,7 +1,7 @@ #pragma once #include - +#include #include #include @@ -29,6 +29,18 @@ class MultiplicationModel : public MathOperationDataModel auto n1 = _number1.lock(); auto n2 = _number2.lock(); + // Dentro da sua classe: + QTimer *timer = new QTimer(this); + timer->start(1000); // 1000ms = 1 segundo + int secondsRemaining = 10; + connect(timer, &QTimer::timeout, this, [=]() mutable { + if (--secondsRemaining <= 0) { + timer->stop(); + // Faça algo quando o timer acabar + } + // Atualize sua UI aqui, ex: label->setText(QString::number(secondsRemaining)); + }); + if (n1 && n2) { //modelValidationState = NodeValidationState::Valid; //modelValidationError = QString(); diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index e4b13fe13..0637451c7 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -107,7 +107,7 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel void setNodeStyle(NodeStyle const &style); - void setNodeProcessingStatus(NodeProcessingStatus status); + void setNodeProcessingStatus(NodeProcessingStatus::Status status); public: virtual void setInData(std::shared_ptr nodeData, PortIndex const portIndex) = 0; diff --git a/src/BasicGraphicsScene.cpp b/src/BasicGraphicsScene.cpp index 9009071a0..3571cdcce 100644 --- a/src/BasicGraphicsScene.cpp +++ b/src/BasicGraphicsScene.cpp @@ -278,6 +278,40 @@ void BasicGraphicsScene::onNodeCreated(NodeId const nodeId) { _nodeGraphicsObjects[nodeId] = std::make_unique(*this, nodeId); + //auto processing_status = NodeDelegateModel::NodeProcessingStatus() + + auto *dfModel = dynamic_cast(&_graphModel); + if (dfModel) { + if (auto *delegate = dfModel->delegateModel(nodeId)) { + connect(delegate, &NodeDelegateModel::computingStarted, this, [this, nodeId]() { + if (auto *df = dynamic_cast(&_graphModel)) + if (auto *d = df->delegateModel(nodeId)) + d->setNodeProcessingStatus( + NodeDelegateModel::NodeProcessingStatus::Status::Processing); + if (auto ngo = nodeGraphicsObject(nodeId)) + ngo->update(); + }); + connect(delegate, &NodeDelegateModel::computingFinished, this, [this, nodeId]() { + if (auto *df = dynamic_cast(&_graphModel)) + if (auto *d = df->delegateModel(nodeId)) + d->setNodeProcessingStatus( + NodeDelegateModel::NodeProcessingStatus::Status::NoStatus); + if (auto ngo = nodeGraphicsObject(nodeId)) + ngo->update(); + }); + /* + connect(delegate, &NodeDelegateModel::computingFinished, this, [this, nodeId]() { + if (auto *df = dynamic_cast(&_graphModel)) + if (auto *d = df->delegateModel(nodeId)) + d->setNodeProcessingStatus( + NodeDelegateModel::NodeProcessingStatus::Status::NoStatus); + if (auto ngo = nodeGraphicsObject(nodeId)) + ngo->update(); + }); +*/ + } + } + Q_EMIT modified(this); } diff --git a/src/DataFlowGraphModel.cpp b/src/DataFlowGraphModel.cpp index 0f173cfdc..5c2210be7 100644 --- a/src/DataFlowGraphModel.cpp +++ b/src/DataFlowGraphModel.cpp @@ -320,7 +320,7 @@ bool DataFlowGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant valu if (value.canConvert()) { auto status = value.value(); if (auto node = delegateModel(nodeId); node != nullptr) { - node->setNodeProcessingStatus(status); + node->setNodeProcessingStatus(status._status); } } Q_EMIT nodeUpdated(nodeId); diff --git a/src/NodeDelegateModel.cpp b/src/NodeDelegateModel.cpp index 834976a34..0079d351b 100644 --- a/src/NodeDelegateModel.cpp +++ b/src/NodeDelegateModel.cpp @@ -56,9 +56,9 @@ void NodeDelegateModel::setNodeStyle(NodeStyle const &style) _nodeStyle = style; } -void NodeDelegateModel::setNodeProcessingStatus(NodeProcessingStatus status) +void NodeDelegateModel::setNodeProcessingStatus(NodeProcessingStatus::Status status) { - _processingStatus = status; + _processingStatus._status = status; } } // namespace QtNodes From 985b6388504fc49d823947f11cd0519f3067fddd Mon Sep 17 00:00:00 2001 From: Taiguara Tupinambas Date: Tue, 15 Jul 2025 19:40:10 -0300 Subject: [PATCH 07/22] fixes nodeprocessingstatus cast --- examples/calculator/MultiplicationModel.hpp | 25 ++++++++++----------- src/BasicGraphicsScene.cpp | 2 +- src/NodeGraphicsObject.cpp | 7 +++--- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/examples/calculator/MultiplicationModel.hpp b/examples/calculator/MultiplicationModel.hpp index 40f9ffc62..1fb13aa82 100644 --- a/examples/calculator/MultiplicationModel.hpp +++ b/examples/calculator/MultiplicationModel.hpp @@ -32,25 +32,24 @@ class MultiplicationModel : public MathOperationDataModel // Dentro da sua classe: QTimer *timer = new QTimer(this); timer->start(1000); // 1000ms = 1 segundo - int secondsRemaining = 10; + int secondsRemaining = 3; connect(timer, &QTimer::timeout, this, [=]() mutable { if (--secondsRemaining <= 0) { timer->stop(); + if (n1 && n2) { + //modelValidationState = NodeValidationState::Valid; + //modelValidationError = QString(); + _result = std::make_shared(n1->number() * n2->number()); + } else { + //modelValidationState = NodeValidationState::Warning; + //modelValidationError = QStringLiteral("Missing or incorrect inputs"); + _result.reset(); + } + + Q_EMIT dataUpdated(outPortIndex); // Faça algo quando o timer acabar } // Atualize sua UI aqui, ex: label->setText(QString::number(secondsRemaining)); }); - - if (n1 && n2) { - //modelValidationState = NodeValidationState::Valid; - //modelValidationError = QString(); - _result = std::make_shared(n1->number() * n2->number()); - } else { - //modelValidationState = NodeValidationState::Warning; - //modelValidationError = QStringLiteral("Missing or incorrect inputs"); - _result.reset(); - } - - Q_EMIT dataUpdated(outPortIndex); } }; diff --git a/src/BasicGraphicsScene.cpp b/src/BasicGraphicsScene.cpp index 3571cdcce..8a5a93fe1 100644 --- a/src/BasicGraphicsScene.cpp +++ b/src/BasicGraphicsScene.cpp @@ -295,7 +295,7 @@ void BasicGraphicsScene::onNodeCreated(NodeId const nodeId) if (auto *df = dynamic_cast(&_graphModel)) if (auto *d = df->delegateModel(nodeId)) d->setNodeProcessingStatus( - NodeDelegateModel::NodeProcessingStatus::Status::NoStatus); + NodeDelegateModel::NodeProcessingStatus::Status::Updated); if (auto ngo = nodeGraphicsObject(nodeId)) ngo->update(); }); diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index a558cc3b9..435957e58 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -392,8 +392,8 @@ void NodeGraphicsObject::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) void NodeGraphicsObject::updateStatusIconSize() const { QVariant var = _graphModel.nodeData(_nodeId, NodeRole::ProcessingStatus); - auto processingStatusValue = var.value() - ._status; + auto processingStatusValue + = var.value(); bool oldStatus = _statusIconActive; _statusIconActive = processingStatusValue @@ -425,8 +425,9 @@ const QIcon NodeGraphicsObject::processingStatusIcon() const { QVariant var = _graphModel.nodeData(_nodeId, NodeRole::ProcessingStatus); - switch (var.value()._status) { + switch (var.value()) { case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::NoStatus: + return QIcon(); case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::Updated: _status_color = QColor("green"); return _statusUpdated; From 3f55e35bd856f388a09bd902bc807187d9422e44 Mon Sep 17 00:00:00 2001 From: g-abilio Date: Wed, 16 Jul 2025 11:51:17 -0300 Subject: [PATCH 08/22] creation of random gen example, and fix of icon color --- .../calculator/LongProcessingRandomNumber.hpp | 79 +++++++++++++++++++ .../calculator/MathOperationDataModel.cpp | 4 + .../calculator/MathOperationDataModel.hpp | 2 +- examples/calculator/MultiplicationModel.hpp | 14 +++- examples/calculator/headless_main.cpp | 3 + examples/calculator/main.cpp | 3 + .../QtNodes/internal/NodeGraphicsObject.hpp | 4 - src/BasicGraphicsScene.cpp | 34 -------- src/DefaultNodePainter.cpp | 7 -- src/NodeGraphicsObject.cpp | 6 -- 10 files changed, 103 insertions(+), 53 deletions(-) create mode 100644 examples/calculator/LongProcessingRandomNumber.hpp diff --git a/examples/calculator/LongProcessingRandomNumber.hpp b/examples/calculator/LongProcessingRandomNumber.hpp new file mode 100644 index 000000000..b10411804 --- /dev/null +++ b/examples/calculator/LongProcessingRandomNumber.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "MathOperationDataModel.hpp" +#include "DecimalData.hpp" + +/// The model generates a random value in a long processing schema, +/// as it should demonstrate the usage of the NodeProcessingStatus. +/// The random number is generate in the [n1, n2] interval. +class RandomNumberModel : public MathOperationDataModel +{ +public: + RandomNumberModel() { + QObject::connect(this, &NodeDelegateModel::computingStarted, this, [this]() { + //auto n1 = _number1.lock()->number(); + //auto n2 = _number2.lock()->number(); + + //if (n1 > n2) { + // this->setNodeProcessingStatus( + // NodeDelegateModel::NodeProcessingStatus::Status::Failed); + //} else { + this->setNodeProcessingStatus( + NodeDelegateModel::NodeProcessingStatus::Status::Processing); + //} + }); + QObject::connect(this, &NodeDelegateModel::computingFinished, this, [this]() { + this->setNodeProcessingStatus( + NodeDelegateModel::NodeProcessingStatus::Status::Updated); + }); + } + virtual ~RandomNumberModel() {} + +public: + QString caption() const override { return QStringLiteral("Random Number"); } + + QString name() const override { return QStringLiteral("Random Number"); } + +private: + void compute() override + { + Q_EMIT computingStarted(); + PortIndex const outPortIndex = 0; + + auto n1 = _number1.lock(); + auto n2 = _number2.lock(); + + QTimer *timer = new QTimer(this); + timer->start(1000); + int secondsRemaining = 3; + connect(timer, &QTimer::timeout, this, [=]() mutable { + if (--secondsRemaining <= 0) { + timer->stop(); + if (n1 && n2) { + double a = n1->number(); + double b = n2->number(); + + if (a > b) + std::swap(a, b); + + double upper = std::nextafter(b, std::numeric_limits::max()); + double randomValue = QRandomGenerator::global()->generateDouble() * (upper - a) + a; + + _result = std::make_shared(randomValue); + Q_EMIT computingFinished(); + } else { + _result.reset(); + } + + Q_EMIT dataUpdated(outPortIndex); + } + }); + } +}; diff --git a/examples/calculator/MathOperationDataModel.cpp b/examples/calculator/MathOperationDataModel.cpp index 0151d0476..dc166311b 100644 --- a/examples/calculator/MathOperationDataModel.cpp +++ b/examples/calculator/MathOperationDataModel.cpp @@ -38,6 +38,7 @@ void MathOperationDataModel::setInData(std::shared_ptr data, PortIndex _number2 = numberData; } + /* Q_EMIT computingStarted(); QTimer *timer = new QTimer(this); @@ -50,4 +51,7 @@ void MathOperationDataModel::setInData(std::shared_ptr data, PortIndex Q_EMIT computingFinished(); } }); + */ + + compute(); } diff --git a/examples/calculator/MathOperationDataModel.hpp b/examples/calculator/MathOperationDataModel.hpp index 6d279642b..16898a8c7 100644 --- a/examples/calculator/MathOperationDataModel.hpp +++ b/examples/calculator/MathOperationDataModel.hpp @@ -34,7 +34,7 @@ class MathOperationDataModel : public NodeDelegateModel void setInData(std::shared_ptr data, PortIndex portIndex) override; - QWidget *embeddedWidget() override { return nullptr; } + QWidget *embeddedWidget() override { return nullptr; } protected: virtual void compute() = 0; diff --git a/examples/calculator/MultiplicationModel.hpp b/examples/calculator/MultiplicationModel.hpp index 1fb13aa82..7349e2584 100644 --- a/examples/calculator/MultiplicationModel.hpp +++ b/examples/calculator/MultiplicationModel.hpp @@ -1,12 +1,12 @@ #pragma once #include +#include #include #include #include #include "MathOperationDataModel.hpp" - #include "DecimalData.hpp" /// The model dictates the number of inputs and outputs for the Node. @@ -14,6 +14,18 @@ class MultiplicationModel : public MathOperationDataModel { public: + MultiplicationModel() { + /* + QObject::connect(this, &NodeDelegateModel::computingStarted, this, [this]() { + this->setNodeProcessingStatus( + NodeDelegateModel::NodeProcessingStatus::Status::Processing); + }); + QObject::connect(this, &NodeDelegateModel::computingFinished, this, [this]() { + this->setNodeProcessingStatus( + NodeDelegateModel::NodeProcessingStatus::Status::Updated); + }); +*/ + } virtual ~MultiplicationModel() {} public: diff --git a/examples/calculator/headless_main.cpp b/examples/calculator/headless_main.cpp index e4dafa022..89103a4f2 100644 --- a/examples/calculator/headless_main.cpp +++ b/examples/calculator/headless_main.cpp @@ -1,5 +1,6 @@ #include "AdditionModel.hpp" #include "DivisionModel.hpp" +#include "LongProcessingRandomNumber.hpp" #include "MultiplicationModel.hpp" #include "NumberDisplayDataModel.hpp" #include "NumberSourceDataModel.hpp" @@ -27,6 +28,8 @@ static std::shared_ptr registerDataModels() ret->registerModel("Operators"); + ret->registerModel("Operators"); + return ret; } diff --git a/examples/calculator/main.cpp b/examples/calculator/main.cpp index 980c0f23a..4d2504f02 100644 --- a/examples/calculator/main.cpp +++ b/examples/calculator/main.cpp @@ -14,6 +14,7 @@ #include "AdditionModel.hpp" #include "DivisionModel.hpp" +#include "LongProcessingRandomNumber.hpp" #include "MultiplicationModel.hpp" #include "NumberDisplayDataModel.hpp" #include "NumberSourceDataModel.hpp" @@ -40,6 +41,8 @@ static std::shared_ptr registerDataModels() ret->registerModel("Operators"); + ret->registerModel("Operators"); + return ret; } diff --git a/include/QtNodes/internal/NodeGraphicsObject.hpp b/include/QtNodes/internal/NodeGraphicsObject.hpp index f24f81d4e..27de9e5a3 100644 --- a/include/QtNodes/internal/NodeGraphicsObject.hpp +++ b/include/QtNodes/internal/NodeGraphicsObject.hpp @@ -62,8 +62,6 @@ class NodeGraphicsObject : public QGraphicsObject QSize statusIconSize() const; - QColor getStatusColor() { return _status_color; } - protected: void paint(QPainter *painter, QStyleOptionGraphicsItem const *option, @@ -106,8 +104,6 @@ class NodeGraphicsObject : public QGraphicsObject mutable QSize _statusIconSize; - mutable QColor _status_color; - const QIcon _statusUpdated{"://status_icons/updated.svg"}; const QIcon _statusProcessing{"://status_icons/processing.svg"}; const QIcon _statusPending{"://status_icons/pending.svg"}; diff --git a/src/BasicGraphicsScene.cpp b/src/BasicGraphicsScene.cpp index 8a5a93fe1..9009071a0 100644 --- a/src/BasicGraphicsScene.cpp +++ b/src/BasicGraphicsScene.cpp @@ -278,40 +278,6 @@ void BasicGraphicsScene::onNodeCreated(NodeId const nodeId) { _nodeGraphicsObjects[nodeId] = std::make_unique(*this, nodeId); - //auto processing_status = NodeDelegateModel::NodeProcessingStatus() - - auto *dfModel = dynamic_cast(&_graphModel); - if (dfModel) { - if (auto *delegate = dfModel->delegateModel(nodeId)) { - connect(delegate, &NodeDelegateModel::computingStarted, this, [this, nodeId]() { - if (auto *df = dynamic_cast(&_graphModel)) - if (auto *d = df->delegateModel(nodeId)) - d->setNodeProcessingStatus( - NodeDelegateModel::NodeProcessingStatus::Status::Processing); - if (auto ngo = nodeGraphicsObject(nodeId)) - ngo->update(); - }); - connect(delegate, &NodeDelegateModel::computingFinished, this, [this, nodeId]() { - if (auto *df = dynamic_cast(&_graphModel)) - if (auto *d = df->delegateModel(nodeId)) - d->setNodeProcessingStatus( - NodeDelegateModel::NodeProcessingStatus::Status::Updated); - if (auto ngo = nodeGraphicsObject(nodeId)) - ngo->update(); - }); - /* - connect(delegate, &NodeDelegateModel::computingFinished, this, [this, nodeId]() { - if (auto *df = dynamic_cast(&_graphModel)) - if (auto *d = df->delegateModel(nodeId)) - d->setNodeProcessingStatus( - NodeDelegateModel::NodeProcessingStatus::Status::NoStatus); - if (auto ngo = nodeGraphicsObject(nodeId)) - ngo->update(); - }); -*/ - } - } - Q_EMIT modified(this); } diff --git a/src/DefaultNodePainter.cpp b/src/DefaultNodePainter.cpp index 60d2115f6..8848ac3f7 100644 --- a/src/DefaultNodePainter.cpp +++ b/src/DefaultNodePainter.cpp @@ -321,13 +321,6 @@ void DefaultNodePainter::drawProcessingIndicator(QPainter *painter, NodeGraphics QSize iconSize(16, 16); QPixmap pixmap = icon.pixmap(iconSize); - QColor color = ngo.getStatusColor(); - - QPainter imgPainter(&pixmap); - imgPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); - imgPainter.fillRect(pixmap.rect(), color); - imgPainter.end(); - QRect r(size.width() - 12.0, size.height() - 12.0, 8.0, 8.0); painter->drawPixmap(r, pixmap); } diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index 435957e58..fe50dfddf 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -429,22 +429,16 @@ const QIcon NodeGraphicsObject::processingStatusIcon() const case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::NoStatus: return QIcon(); case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::Updated: - _status_color = QColor("green"); return _statusUpdated; case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::Processing: - _status_color = QColor("blue"); return _statusProcessing; case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::Pending: - _status_color = QColor("yellow"); return _statusPending; case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::Empty: - _status_color = QColor("gray"); return _statusEmpty; case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::Failed: - _status_color = QColor("red"); return _statusInvalid; case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::Partial: - _status_color = QColor("white"); return _statusPartial; } return _statusInvalid; From 50c6bec1df1eb463e4155bc87e0ffcd00b693e64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taiguara=20Tupinamb=C3=A1s?= Date: Thu, 17 Jul 2025 11:46:09 -0300 Subject: [PATCH 09/22] Connect delegate UI update signal --- include/QtNodes/internal/NodeDelegateModel.hpp | 8 ++++++++ src/DataFlowGraphModel.cpp | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index 0637451c7..296a8b642 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -152,6 +152,14 @@ public Q_SLOTS: void embeddedWidgetSizeUpdated(); + /// Request an update of the node's UI. + /** + * Emit this signal whenever some internal state change requires + * the node to be repainted. The containing graph model will + * propagate the update to the scene. + */ + void requestNodeUpdate(); + /// Call this function before deleting the data associated with ports. /** * The function notifies the Graph Model and makes it remove and recompute the diff --git a/src/DataFlowGraphModel.cpp b/src/DataFlowGraphModel.cpp index 5c2210be7..0387b4c40 100644 --- a/src/DataFlowGraphModel.cpp +++ b/src/DataFlowGraphModel.cpp @@ -93,6 +93,10 @@ NodeId DataFlowGraphModel::addNode(QString const nodeType) this, &DataFlowGraphModel::portsInserted); + connect(model.get(), &NodeDelegateModel::requestNodeUpdate, this, [newId, this]() { + Q_EMIT nodeUpdated(newId); + }); + _models[newId] = std::move(model); Q_EMIT nodeCreated(newId); @@ -528,6 +532,9 @@ void DataFlowGraphModel::loadNode(QJsonObject const &nodeJson) &NodeDelegateModel::portsInserted, this, &DataFlowGraphModel::portsInserted); + connect(model.get(), &NodeDelegateModel::requestNodeUpdate, this, [restoredNodeId, this]() { + Q_EMIT nodeUpdated(restoredNodeId); + }); _models[restoredNodeId] = std::move(model); From 1c09ecf35448013dbed37d60728337d5c1f707c3 Mon Sep 17 00:00:00 2001 From: g-abilio Date: Thu, 17 Jul 2025 15:24:50 -0300 Subject: [PATCH 10/22] fix random number node dynamic --- .../calculator/LongProcessingRandomNumber.hpp | 23 +++++++++---------- .../calculator/MathOperationDataModel.cpp | 15 ------------ 2 files changed, 11 insertions(+), 27 deletions(-) diff --git a/examples/calculator/LongProcessingRandomNumber.hpp b/examples/calculator/LongProcessingRandomNumber.hpp index b10411804..87ad3fab0 100644 --- a/examples/calculator/LongProcessingRandomNumber.hpp +++ b/examples/calculator/LongProcessingRandomNumber.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -18,20 +17,17 @@ class RandomNumberModel : public MathOperationDataModel public: RandomNumberModel() { QObject::connect(this, &NodeDelegateModel::computingStarted, this, [this]() { - //auto n1 = _number1.lock()->number(); - //auto n2 = _number2.lock()->number(); - - //if (n1 > n2) { - // this->setNodeProcessingStatus( - // NodeDelegateModel::NodeProcessingStatus::Status::Failed); - //} else { - this->setNodeProcessingStatus( + this->setNodeProcessingStatus( NodeDelegateModel::NodeProcessingStatus::Status::Processing); - //} + + emit requestNodeUpdate(); + }); QObject::connect(this, &NodeDelegateModel::computingFinished, this, [this]() { this->setNodeProcessingStatus( NodeDelegateModel::NodeProcessingStatus::Status::Updated); + + emit requestNodeUpdate(); }); } virtual ~RandomNumberModel() {} @@ -60,8 +56,11 @@ class RandomNumberModel : public MathOperationDataModel double a = n1->number(); double b = n2->number(); - if (a > b) - std::swap(a, b); + if (a > b) { + this->setNodeProcessingStatus(NodeDelegateModel::NodeProcessingStatus::Status::Failed); + emit requestNodeUpdate(); + return; + } double upper = std::nextafter(b, std::numeric_limits::max()); double randomValue = QRandomGenerator::global()->generateDouble() * (upper - a) + a; diff --git a/examples/calculator/MathOperationDataModel.cpp b/examples/calculator/MathOperationDataModel.cpp index dc166311b..52528af0a 100644 --- a/examples/calculator/MathOperationDataModel.cpp +++ b/examples/calculator/MathOperationDataModel.cpp @@ -38,20 +38,5 @@ void MathOperationDataModel::setInData(std::shared_ptr data, PortIndex _number2 = numberData; } - /* - Q_EMIT computingStarted(); - - QTimer *timer = new QTimer(this); - timer->start(1000); - int secondsRemaining = 10; - connect(timer, &QTimer::timeout, this, [=]() mutable { - if (--secondsRemaining <= 0) { - timer->stop(); - compute(); - Q_EMIT computingFinished(); - } - }); - */ - compute(); } From f2c120f1a0a82fbb0b7da8028ce19007dfb57966 Mon Sep 17 00:00:00 2001 From: g-abilio Date: Thu, 17 Jul 2025 15:35:48 -0300 Subject: [PATCH 11/22] clean up test code in multiplication node --- examples/calculator/MultiplicationModel.hpp | 45 +++++---------------- 1 file changed, 11 insertions(+), 34 deletions(-) diff --git a/examples/calculator/MultiplicationModel.hpp b/examples/calculator/MultiplicationModel.hpp index 7349e2584..7d63534ba 100644 --- a/examples/calculator/MultiplicationModel.hpp +++ b/examples/calculator/MultiplicationModel.hpp @@ -14,18 +14,6 @@ class MultiplicationModel : public MathOperationDataModel { public: - MultiplicationModel() { - /* - QObject::connect(this, &NodeDelegateModel::computingStarted, this, [this]() { - this->setNodeProcessingStatus( - NodeDelegateModel::NodeProcessingStatus::Status::Processing); - }); - QObject::connect(this, &NodeDelegateModel::computingFinished, this, [this]() { - this->setNodeProcessingStatus( - NodeDelegateModel::NodeProcessingStatus::Status::Updated); - }); -*/ - } virtual ~MultiplicationModel() {} public: @@ -41,27 +29,16 @@ class MultiplicationModel : public MathOperationDataModel auto n1 = _number1.lock(); auto n2 = _number2.lock(); - // Dentro da sua classe: - QTimer *timer = new QTimer(this); - timer->start(1000); // 1000ms = 1 segundo - int secondsRemaining = 3; - connect(timer, &QTimer::timeout, this, [=]() mutable { - if (--secondsRemaining <= 0) { - timer->stop(); - if (n1 && n2) { - //modelValidationState = NodeValidationState::Valid; - //modelValidationError = QString(); - _result = std::make_shared(n1->number() * n2->number()); - } else { - //modelValidationState = NodeValidationState::Warning; - //modelValidationError = QStringLiteral("Missing or incorrect inputs"); - _result.reset(); - } - - Q_EMIT dataUpdated(outPortIndex); - // Faça algo quando o timer acabar - } - // Atualize sua UI aqui, ex: label->setText(QString::number(secondsRemaining)); - }); + if (n1 && n2) { + //modelValidationState = NodeValidationState::Valid; + //modelValidationError = QString(); + _result = std::make_shared(n1->number() * n2->number()); + } else { + //modelValidationState = NodeValidationState::Warning; + //modelValidationError = QStringLiteral("Missing or incorrect inputs"); + _result.reset(); + } + + Q_EMIT dataUpdated(outPortIndex); } }; From 965eb1598f6912a2eefcbd49ed67c7617839d54d Mon Sep 17 00:00:00 2001 From: Hudson Miranda Date: Tue, 22 Jul 2025 15:42:27 -0300 Subject: [PATCH 12/22] Add per-node background color API --- include/QtNodes/internal/NodeDelegateModel.hpp | 4 ++++ include/QtNodes/internal/NodeStyle.hpp | 6 ++++++ include/QtNodes/internal/UndoCommands.hpp | 2 +- src/NodeDelegateModel.cpp | 5 +++++ src/NodeStyle.cpp | 13 +++++++++++++ 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index 6301164db..87669d12a 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -8,6 +8,7 @@ #include "Export.hpp" #include "NodeData.hpp" #include "NodeStyle.hpp" +#include #include "Serializable.hpp" namespace QtNodes { @@ -61,6 +62,9 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel : public QObject, public Serializable void setNodeStyle(NodeStyle const &style); + /// Convenience helper to change the node background color. + void setBackgroundColor(QColor const &color); + public: virtual void setInData(std::shared_ptr nodeData, PortIndex const portIndex) = 0; diff --git a/include/QtNodes/internal/NodeStyle.hpp b/include/QtNodes/internal/NodeStyle.hpp index 85abc5612..1f25355d6 100644 --- a/include/QtNodes/internal/NodeStyle.hpp +++ b/include/QtNodes/internal/NodeStyle.hpp @@ -26,6 +26,12 @@ class NODE_EDITOR_PUBLIC NodeStyle : public Style QJsonObject toJson() const override; + /// Set uniform background color for the node. + void setBackgroundColor(QColor const &color); + + /// Current uniform background color. + QColor backgroundColor() const; + public: QColor NormalBoundaryColor; QColor SelectedBoundaryColor; diff --git a/include/QtNodes/internal/UndoCommands.hpp b/include/QtNodes/internal/UndoCommands.hpp index b29c36ab5..77e2a4f09 100644 --- a/include/QtNodes/internal/UndoCommands.hpp +++ b/include/QtNodes/internal/UndoCommands.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include diff --git a/src/NodeDelegateModel.cpp b/src/NodeDelegateModel.cpp index 94e47ad68..b267b58c8 100644 --- a/src/NodeDelegateModel.cpp +++ b/src/NodeDelegateModel.cpp @@ -51,4 +51,9 @@ void NodeDelegateModel::setNodeStyle(NodeStyle const &style) _nodeStyle = style; } +void NodeDelegateModel::setBackgroundColor(QColor const &color) +{ + _nodeStyle.setBackgroundColor(color); +} + } // namespace QtNodes diff --git a/src/NodeStyle.cpp b/src/NodeStyle.cpp index 41b872bba..ddb25b0c1 100644 --- a/src/NodeStyle.cpp +++ b/src/NodeStyle.cpp @@ -158,3 +158,16 @@ QJsonObject NodeStyle::toJson() const return root; } + +void NodeStyle::setBackgroundColor(QColor const &color) +{ + GradientColor0 = color; + GradientColor1 = color; + GradientColor2 = color; + GradientColor3 = color; +} + +QColor NodeStyle::backgroundColor() const +{ + return GradientColor0; +} From b23ecc3baf1fca937ded5dd67a74e73e3292eb21 Mon Sep 17 00:00:00 2001 From: Taiguara Tupinambas Date: Wed, 10 Sep 2025 15:32:48 -0300 Subject: [PATCH 13/22] revert unnecessary changes in multiplication model --- examples/calculator/MultiplicationModel.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/calculator/MultiplicationModel.hpp b/examples/calculator/MultiplicationModel.hpp index 7d63534ba..127ae4011 100644 --- a/examples/calculator/MultiplicationModel.hpp +++ b/examples/calculator/MultiplicationModel.hpp @@ -1,12 +1,12 @@ #pragma once #include -#include -#include + #include #include #include "MathOperationDataModel.hpp" + #include "DecimalData.hpp" /// The model dictates the number of inputs and outputs for the Node. From 0a35f766778f75982af5a0b4441133c43765f8d9 Mon Sep 17 00:00:00 2001 From: Taiguara Tupinambas Date: Wed, 10 Sep 2025 15:36:11 -0300 Subject: [PATCH 14/22] revert unnecessary changes --- examples/calculator/MathOperationDataModel.cpp | 2 +- examples/calculator/MathOperationDataModel.hpp | 2 +- include/QtNodes/internal/Definitions.hpp | 1 - include/QtNodes/internal/NodeGraphicsObject.hpp | 1 - src/BasicGraphicsScene.cpp | 1 - 5 files changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/calculator/MathOperationDataModel.cpp b/examples/calculator/MathOperationDataModel.cpp index 52528af0a..f6a3a26d0 100644 --- a/examples/calculator/MathOperationDataModel.cpp +++ b/examples/calculator/MathOperationDataModel.cpp @@ -1,6 +1,6 @@ #include "MathOperationDataModel.hpp" + #include "DecimalData.hpp" -#include unsigned int MathOperationDataModel::nPorts(PortType portType) const { diff --git a/examples/calculator/MathOperationDataModel.hpp b/examples/calculator/MathOperationDataModel.hpp index 16898a8c7..6d279642b 100644 --- a/examples/calculator/MathOperationDataModel.hpp +++ b/examples/calculator/MathOperationDataModel.hpp @@ -34,7 +34,7 @@ class MathOperationDataModel : public NodeDelegateModel void setInData(std::shared_ptr data, PortIndex portIndex) override; - QWidget *embeddedWidget() override { return nullptr; } + QWidget *embeddedWidget() override { return nullptr; } protected: virtual void compute() = 0; diff --git a/include/QtNodes/internal/Definitions.hpp b/include/QtNodes/internal/Definitions.hpp index 6f66a1e4c..8c01475f9 100644 --- a/include/QtNodes/internal/Definitions.hpp +++ b/include/QtNodes/internal/Definitions.hpp @@ -12,7 +12,6 @@ */ namespace QtNodes { - #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) NODE_EDITOR_PUBLIC Q_NAMESPACE #else diff --git a/include/QtNodes/internal/NodeGraphicsObject.hpp b/include/QtNodes/internal/NodeGraphicsObject.hpp index b122f97c4..69eb2ba6b 100644 --- a/include/QtNodes/internal/NodeGraphicsObject.hpp +++ b/include/QtNodes/internal/NodeGraphicsObject.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include diff --git a/src/BasicGraphicsScene.cpp b/src/BasicGraphicsScene.cpp index edd3eddb4..7cec5ec45 100644 --- a/src/BasicGraphicsScene.cpp +++ b/src/BasicGraphicsScene.cpp @@ -3,7 +3,6 @@ #include "AbstractNodeGeometry.hpp" #include "ConnectionGraphicsObject.hpp" #include "ConnectionIdUtils.hpp" -#include "DataFlowGraphModel.hpp" #include "DefaultConnectionPainter.hpp" #include "DefaultHorizontalNodeGeometry.hpp" #include "DefaultNodePainter.hpp" From a9be092b5eb36830023a285241e8a90ac0715aa1 Mon Sep 17 00:00:00 2001 From: g-abilio Date: Thu, 11 Sep 2025 19:27:06 -0300 Subject: [PATCH 15/22] solve icon size and refactor NodeProcessingStatus code --- .../calculator/LongProcessingRandomNumber.hpp | 6 +-- .../QtNodes/internal/NodeDelegateModel.hpp | 40 ++++++++----------- src/DataFlowGraphModel.cpp | 6 +-- src/DefaultNodePainter.cpp | 2 +- src/NodeDelegateModel.cpp | 4 +- src/NodeGraphicsObject.cpp | 28 ++++++------- 6 files changed, 37 insertions(+), 49 deletions(-) diff --git a/examples/calculator/LongProcessingRandomNumber.hpp b/examples/calculator/LongProcessingRandomNumber.hpp index 87ad3fab0..d6399ca03 100644 --- a/examples/calculator/LongProcessingRandomNumber.hpp +++ b/examples/calculator/LongProcessingRandomNumber.hpp @@ -18,14 +18,14 @@ class RandomNumberModel : public MathOperationDataModel RandomNumberModel() { QObject::connect(this, &NodeDelegateModel::computingStarted, this, [this]() { this->setNodeProcessingStatus( - NodeDelegateModel::NodeProcessingStatus::Status::Processing); + QtNodes::NodeProcessingStatus::Processing); emit requestNodeUpdate(); }); QObject::connect(this, &NodeDelegateModel::computingFinished, this, [this]() { this->setNodeProcessingStatus( - NodeDelegateModel::NodeProcessingStatus::Status::Updated); + QtNodes::NodeProcessingStatus::Updated); emit requestNodeUpdate(); }); @@ -57,7 +57,7 @@ class RandomNumberModel : public MathOperationDataModel double b = n2->number(); if (a > b) { - this->setNodeProcessingStatus(NodeDelegateModel::NodeProcessingStatus::Status::Failed); + setNodeProcessingStatus(QtNodes::NodeProcessingStatus::Failed); emit requestNodeUpdate(); return; } diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index 296a8b642..28666c8a7 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -27,6 +27,19 @@ struct NodeValidationState QString _stateMessage{""}; }; +/** + * Describes the node status, depending on its current situation + */ +enum class NodeProcessingStatus : int { + NoStatus = 0, /// + Updated = 1, /// + Processing = 2, /// + Pending = 3, /// + Empty = 4, /// + Failed = 5, /// + Partial = 6, /// +}; + class StyleCollection; /** @@ -46,24 +59,6 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel virtual ~NodeDelegateModel() = default; - /** - * Describes the node status, depending on its current situation - */ - struct NodeProcessingStatus - { - enum class Status : int { - NoStatus = 0, /// - Updated = 1, /// - Processing = 2, /// - Pending = 3, /// - Empty = 4, /// - Failed = 5, /// - Partial = 6, /// - }; - - Status _status{Status::NoStatus}; - }; - /// It is possible to hide caption in GUI virtual bool captionVisible() const { return true; } @@ -83,10 +78,7 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel virtual NodeValidationState validationState() const { return _nodeValidationState; } /// Returns the curent processing status - virtual NodeProcessingStatus::Status processingStatus() const - { - return _processingStatus._status; - } + virtual NodeProcessingStatus processingStatus() const { return _processingStatus; } public: QJsonObject save() const override; @@ -107,7 +99,7 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel void setNodeStyle(NodeStyle const &style); - void setNodeProcessingStatus(NodeProcessingStatus::Status status); + void setNodeProcessingStatus(NodeProcessingStatus status); public: virtual void setInData(std::shared_ptr nodeData, PortIndex const portIndex) = 0; @@ -187,7 +179,7 @@ public Q_SLOTS: NodeValidationState _nodeValidationState; - NodeProcessingStatus _processingStatus; + NodeProcessingStatus _processingStatus = NodeProcessingStatus::Updated; }; } // namespace QtNodes diff --git a/src/DataFlowGraphModel.cpp b/src/DataFlowGraphModel.cpp index 0387b4c40..74600dace 100644 --- a/src/DataFlowGraphModel.cpp +++ b/src/DataFlowGraphModel.cpp @@ -321,10 +321,10 @@ bool DataFlowGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant valu } break; case NodeRole::ProcessingStatus: { - if (value.canConvert()) { - auto status = value.value(); + if (value.canConvert()) { + auto status = value.value(); if (auto node = delegateModel(nodeId); node != nullptr) { - node->setNodeProcessingStatus(status._status); + node->setNodeProcessingStatus(status); } } Q_EMIT nodeUpdated(nodeId); diff --git a/src/DefaultNodePainter.cpp b/src/DefaultNodePainter.cpp index 8848ac3f7..7dfee358a 100644 --- a/src/DefaultNodePainter.cpp +++ b/src/DefaultNodePainter.cpp @@ -321,7 +321,7 @@ void DefaultNodePainter::drawProcessingIndicator(QPainter *painter, NodeGraphics QSize iconSize(16, 16); QPixmap pixmap = icon.pixmap(iconSize); - QRect r(size.width() - 12.0, size.height() - 12.0, 8.0, 8.0); + QRect r(size.width() - 28.0, size.height() - 28.0, 20.0, 20.0); painter->drawPixmap(r, pixmap); } diff --git a/src/NodeDelegateModel.cpp b/src/NodeDelegateModel.cpp index 0079d351b..834976a34 100644 --- a/src/NodeDelegateModel.cpp +++ b/src/NodeDelegateModel.cpp @@ -56,9 +56,9 @@ void NodeDelegateModel::setNodeStyle(NodeStyle const &style) _nodeStyle = style; } -void NodeDelegateModel::setNodeProcessingStatus(NodeProcessingStatus::Status status) +void NodeDelegateModel::setNodeProcessingStatus(NodeProcessingStatus status) { - _processingStatus._status = status; + _processingStatus = status; } } // namespace QtNodes diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index fe50dfddf..2938c3764 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -68,11 +68,9 @@ NodeGraphicsObject::NodeGraphicsObject(BasicGraphicsScene &scene, NodeId nodeId) }); QVariant var = _graphModel.nodeData(_nodeId, NodeRole::ProcessingStatus); - auto processingStatusValue = var.value() - ._status; + auto processingStatusValue = var.value(); - _statusIconActive = processingStatusValue - != QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::NoStatus; + _statusIconActive = processingStatusValue != QtNodes::NodeProcessingStatus::NoStatus; _statusIconSize.setWidth(_statusIconActive ? 32 : 0); _statusIconSize.setHeight(_statusIconActive ? 32 : 0); } @@ -392,12 +390,10 @@ void NodeGraphicsObject::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) void NodeGraphicsObject::updateStatusIconSize() const { QVariant var = _graphModel.nodeData(_nodeId, NodeRole::ProcessingStatus); - auto processingStatusValue - = var.value(); + auto processingStatusValue = var.value(); bool oldStatus = _statusIconActive; - _statusIconActive = processingStatusValue - != QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::NoStatus; + _statusIconActive = processingStatusValue != QtNodes::NodeProcessingStatus::NoStatus; if (oldStatus != _statusIconActive) { _statusIconSize.setWidth(_statusIconActive ? 32 : 0); @@ -425,20 +421,20 @@ const QIcon NodeGraphicsObject::processingStatusIcon() const { QVariant var = _graphModel.nodeData(_nodeId, NodeRole::ProcessingStatus); - switch (var.value()) { - case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::NoStatus: + switch (var.value()) { + case QtNodes::NodeProcessingStatus::NoStatus: return QIcon(); - case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::Updated: + case QtNodes::NodeProcessingStatus::Updated: return _statusUpdated; - case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::Processing: + case QtNodes::NodeProcessingStatus::Processing: return _statusProcessing; - case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::Pending: + case QtNodes::NodeProcessingStatus::Pending: return _statusPending; - case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::Empty: + case QtNodes::NodeProcessingStatus::Empty: return _statusEmpty; - case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::Failed: + case QtNodes::NodeProcessingStatus::Failed: return _statusInvalid; - case QtNodes::NodeDelegateModel::NodeProcessingStatus::Status::Partial: + case QtNodes::NodeProcessingStatus::Partial: return _statusPartial; } return _statusInvalid; From 03e311a0d2fdefae5b84d3484b3d7a5af6cd7b90 Mon Sep 17 00:00:00 2001 From: g-abilio Date: Thu, 11 Sep 2025 20:10:10 -0300 Subject: [PATCH 16/22] remove duplicate code --- include/QtNodes/internal/NodeDelegateModel.hpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index 434410a51..ca841e250 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -72,9 +72,6 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel /// Caption is used in GUI virtual QString caption() const = 0; - /// It is possible to hide caption in GUI - virtual bool captionVisible() const { return true; } - /// Port caption is used in GUI to label individual ports virtual QString portCaption(PortType, PortIndex) const { return QString(); } @@ -93,7 +90,7 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel void setValidationState(const NodeValidationState &validationState); - void setNodeProcessingStatus(NodeProcessingStatus::Status status); + void setNodeProcessingStatus(NodeProcessingStatus status); virtual unsigned int nPorts(PortType portType) const = 0; @@ -105,8 +102,6 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel void setNodeStyle(NodeStyle const &style); - void setNodeProcessingStatus(NodeProcessingStatus status); - public: virtual void setInData(std::shared_ptr nodeData, PortIndex const portIndex) = 0; From a87ceb2162bebfb07a7d68f1385913a05a291a18 Mon Sep 17 00:00:00 2001 From: g-abilio Date: Mon, 15 Sep 2025 14:49:37 -0300 Subject: [PATCH 17/22] add space to better organize processing status in node display --- src/DefaultHorizontalNodeGeometry.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/DefaultHorizontalNodeGeometry.cpp b/src/DefaultHorizontalNodeGeometry.cpp index 8f3e07aad..a2102c641 100644 --- a/src/DefaultHorizontalNodeGeometry.cpp +++ b/src/DefaultHorizontalNodeGeometry.cpp @@ -1,7 +1,7 @@ #include "DefaultHorizontalNodeGeometry.hpp" - #include "AbstractGraphModel.hpp" #include "NodeData.hpp" +#include "NodeDelegateModel.hpp" #include #include @@ -55,6 +55,12 @@ void DefaultHorizontalNodeGeometry::recomputeSize(NodeId const nodeId) const height += _portSpasing; // space above caption height += _portSpasing; // space below caption + QVariant var = _graphModel.nodeData(nodeId, NodeRole::ProcessingStatus); + auto processingStatusValue = var.value(); + + if (processingStatusValue != QtNodes::NodeProcessingStatus::NoStatus) + height += 20; + unsigned int inPortWidth = maxPortsTextAdvance(nodeId, PortType::In); unsigned int outPortWidth = maxPortsTextAdvance(nodeId, PortType::Out); From ae3715baaa2818a8e30c5651d7bc4461b2b45c1c Mon Sep 17 00:00:00 2001 From: g-abilio Date: Fri, 19 Sep 2025 10:54:39 -0300 Subject: [PATCH 18/22] remove processing value default value --- include/QtNodes/internal/NodeDelegateModel.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index ca841e250..bb8a8ddb4 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -176,7 +176,7 @@ public Q_SLOTS: NodeValidationState _nodeValidationState; - NodeProcessingStatus _processingStatus = NodeProcessingStatus::Updated; + NodeProcessingStatus _processingStatus; }; } // namespace QtNodes From eedc666eabfcd46eb803a74b876acf777e360a27 Mon Sep 17 00:00:00 2001 From: g-abilio Date: Fri, 19 Sep 2025 11:37:27 -0300 Subject: [PATCH 19/22] fix bugs in node processing status --- examples/calculator/LongProcessingRandomNumber.hpp | 6 +++++- examples/calculator/NumberDisplayDataModel.cpp | 4 +++- examples/calculator/NumberSourceDataModel.cpp | 4 +++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/examples/calculator/LongProcessingRandomNumber.hpp b/examples/calculator/LongProcessingRandomNumber.hpp index 431ecdd17..8be2882ca 100644 --- a/examples/calculator/LongProcessingRandomNumber.hpp +++ b/examples/calculator/LongProcessingRandomNumber.hpp @@ -16,9 +16,13 @@ class RandomNumberModel : public MathOperationDataModel { public: RandomNumberModel() { + this->setNodeProcessingStatus(QtNodes::NodeProcessingStatus::Empty); + QObject::connect(this, &NodeDelegateModel::computingStarted, this, [this]() { - this->setNodeProcessingStatus( + if (_number1.lock() && _number2.lock()) { + this->setNodeProcessingStatus( QtNodes::NodeProcessingStatus::Processing); + } emit requestNodeUpdate(); }); diff --git a/examples/calculator/NumberDisplayDataModel.cpp b/examples/calculator/NumberDisplayDataModel.cpp index 5086050c8..6f58f5854 100644 --- a/examples/calculator/NumberDisplayDataModel.cpp +++ b/examples/calculator/NumberDisplayDataModel.cpp @@ -4,7 +4,9 @@ NumberDisplayDataModel::NumberDisplayDataModel() : _label{nullptr} -{} +{ + this->setNodeProcessingStatus(QtNodes::NodeProcessingStatus::NoStatus); +} unsigned int NumberDisplayDataModel::nPorts(PortType portType) const { diff --git a/examples/calculator/NumberSourceDataModel.cpp b/examples/calculator/NumberSourceDataModel.cpp index f6a7ca55d..f2564886a 100644 --- a/examples/calculator/NumberSourceDataModel.cpp +++ b/examples/calculator/NumberSourceDataModel.cpp @@ -9,7 +9,9 @@ NumberSourceDataModel::NumberSourceDataModel() : _lineEdit{nullptr} , _number(std::make_shared(0.0)) -{} +{ + this->setNodeProcessingStatus(QtNodes::NodeProcessingStatus::NoStatus); +} QJsonObject NumberSourceDataModel::save() const { From 12986053a482c2f91dad912582503b94b27abefc Mon Sep 17 00:00:00 2001 From: g-abilio Date: Wed, 24 Sep 2025 09:59:09 -0300 Subject: [PATCH 20/22] add Q_DECLARE_METATYPE to solve linux build problems --- include/QtNodes/internal/NodeDelegateModel.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index bb8a8ddb4..523c00493 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -43,6 +43,7 @@ enum class NodeProcessingStatus : int { Failed = 5, /// Partial = 6, /// }; +Q_DECLARE_METATYPE(QtNodes::NodeProcessingStatus); class StyleCollection; From 47d948f3f1641e8c5fd111990abc72609a29b101 Mon Sep 17 00:00:00 2001 From: g-abilio Date: Wed, 24 Sep 2025 10:10:09 -0300 Subject: [PATCH 21/22] declaring metatype in the correct place --- include/QtNodes/internal/NodeDelegateModel.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index 523c00493..476cf6f65 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -43,7 +43,6 @@ enum class NodeProcessingStatus : int { Failed = 5, /// Partial = 6, /// }; -Q_DECLARE_METATYPE(QtNodes::NodeProcessingStatus); class StyleCollection; @@ -183,3 +182,4 @@ public Q_SLOTS: } // namespace QtNodes Q_DECLARE_METATYPE(QtNodes::NodeValidationState) +Q_DECLARE_METATYPE(QtNodes::NodeProcessingStatus) From 4f1b23c05975899b89c43bb4b0509f6b94dcdbbb Mon Sep 17 00:00:00 2001 From: g-abilio Date: Wed, 3 Dec 2025 11:20:34 -0300 Subject: [PATCH 22/22] fix undocommands module inclusion --- include/QtNodes/internal/UndoCommands.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/QtNodes/internal/UndoCommands.hpp b/include/QtNodes/internal/UndoCommands.hpp index 77e2a4f09..870478618 100644 --- a/include/QtNodes/internal/UndoCommands.hpp +++ b/include/QtNodes/internal/UndoCommands.hpp @@ -3,9 +3,9 @@ #include "Definitions.hpp" #include "Export.hpp" +#include #include #include -#include #include