Skip to content

Commit c52d977

Browse files
g-abiliotatatupi
andauthored
Add Node Processing Status feature (#494)
* wip: adds NodeValidationState info to NodeDelegateModel * makes the nodeObject red in case of invalid state and adds a tooltip for error msg * adds warning state and adapts calculator example * adds validation icon and adapts calculation example * core improvements to develop node processing status * first commit on the creation of a processing status example * fixes nodeprocessingstatus cast * creation of random gen example, and fix of icon color * Connect delegate UI update signal * fix random number node dynamic * clean up test code in multiplication node * revert unnecessary changes in multiplication model * revert unnecessary changes * solve icon size and refactor NodeProcessingStatus code * remove duplicate code * add space to better organize processing status in node display * remove processing value default value * fix bugs in node processing status * add Q_DECLARE_METATYPE to solve linux build problems * declaring metatype in the correct place * uniformizes icon files attributes * removes commented code * improves processing status icon resolution * solves situations where icons should not appear * adds docstring to each nodeprocessingstatus * adds possibility to change the node processing status icon style * moves all status logic to NodeStyle * removes unnecessary code * adds declaration of QPixmap --------- Co-authored-by: Taiguara Tupinambás <[email protected]>
1 parent 280406a commit c52d977

22 files changed

+439
-27
lines changed

examples/calculator/DivisionModel.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,23 +59,23 @@ class DivisionModel : public MathOperationDataModel
5959
if (n2 && (n2->number() == 0.0)) {
6060
state._state = QtNodes::NodeValidationState::State::Error;
6161
state._stateMessage = QStringLiteral("Division by zero error");
62-
setValidatonState(state);
62+
setValidationState(state);
6363
_result.reset();
6464
} else if ( n2 && (n2->number() < 1e-5)) {
6565
state._state = QtNodes::NodeValidationState::State::Warning;
6666
state._stateMessage = QStringLiteral("Very small divident. Result might overflow");
67-
setValidatonState(state);
67+
setValidationState(state);
6868
if (n1) {
6969
_result = std::make_shared<DecimalData>(n1->number() / n2->number());
7070
} else {
7171
_result.reset();
7272
}
7373
} else if (n1 && n2) {
74-
setValidatonState(state);
74+
setValidationState(state);
7575
_result = std::make_shared<DecimalData>(n1->number() / n2->number());
7676
} else {
7777
QtNodes::NodeValidationState state;
78-
setValidatonState(state);
78+
setValidationState(state);
7979
_result.reset();
8080
}
8181

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#pragma once
2+
3+
#include <QtNodes/NodeDelegateModel>
4+
#include <QTimer>
5+
#include <QtCore/QObject>
6+
#include <QtWidgets/QLabel>
7+
#include <QtCore/QRandomGenerator64>
8+
9+
#include "MathOperationDataModel.hpp"
10+
#include "DecimalData.hpp"
11+
12+
/// The model generates a random value in a long processing schema,
13+
/// as it should demonstrate the usage of the NodeProcessingStatus.
14+
/// The random number is generate in the [n1, n2] interval.
15+
class RandomNumberModel : public MathOperationDataModel
16+
{
17+
public:
18+
RandomNumberModel() {
19+
this->setNodeProcessingStatus(QtNodes::NodeProcessingStatus::Empty);
20+
21+
22+
QObject::connect(this, &NodeDelegateModel::computingStarted, this, [this]() {
23+
if (_number1.lock() && _number2.lock()) {
24+
this->setNodeProcessingStatus(
25+
QtNodes::NodeProcessingStatus::Processing);
26+
}
27+
28+
emit requestNodeUpdate();
29+
});
30+
QObject::connect(this, &NodeDelegateModel::computingFinished, this, [this]() {
31+
this->setNodeProcessingStatus(
32+
QtNodes::NodeProcessingStatus::Updated);
33+
34+
emit requestNodeUpdate();
35+
});
36+
}
37+
virtual ~RandomNumberModel() {}
38+
39+
public:
40+
QString caption() const override { return QStringLiteral("Random Number"); }
41+
42+
QString name() const override { return QStringLiteral("Random Number"); }
43+
44+
private:
45+
void compute() override
46+
{
47+
Q_EMIT computingStarted();
48+
PortIndex const outPortIndex = 0;
49+
50+
auto n1 = _number1.lock();
51+
auto n2 = _number2.lock();
52+
53+
QTimer *timer = new QTimer(this);
54+
timer->start(1000);
55+
int secondsRemaining = 3;
56+
connect(timer, &QTimer::timeout, this, [=]() mutable {
57+
if (--secondsRemaining <= 0) {
58+
timer->stop();
59+
if (n1 && n2) {
60+
double a = n1->number();
61+
double b = n2->number();
62+
63+
if (a > b) {
64+
setNodeProcessingStatus(QtNodes::NodeProcessingStatus::Failed);
65+
66+
emit requestNodeUpdate();
67+
return;
68+
}
69+
70+
double upper = std::nextafter(b, std::numeric_limits<double>::max());
71+
double randomValue = QRandomGenerator::global()->generateDouble() * (upper - a) + a;
72+
73+
_result = std::make_shared<DecimalData>(randomValue);
74+
Q_EMIT computingFinished();
75+
} else {
76+
_result.reset();
77+
}
78+
79+
Q_EMIT dataUpdated(outPortIndex);
80+
}
81+
});
82+
}
83+
};

examples/calculator/headless_main.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "AdditionModel.hpp"
22
#include "DivisionModel.hpp"
3+
#include "LongProcessingRandomNumber.hpp"
34
#include "MultiplicationModel.hpp"
45
#include "NumberDisplayDataModel.hpp"
56
#include "NumberSourceDataModel.hpp"
@@ -27,6 +28,8 @@ static std::shared_ptr<NodeDelegateModelRegistry> registerDataModels()
2728

2829
ret->registerModel<DivisionModel>("Operators");
2930

31+
ret->registerModel<RandomNumberModel>("Operators");
32+
3033
return ret;
3134
}
3235

examples/calculator/main.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "AdditionModel.hpp"
1616
#include "DivisionModel.hpp"
17+
#include "LongProcessingRandomNumber.hpp"
1718
#include "MultiplicationModel.hpp"
1819
#include "NumberDisplayDataModel.hpp"
1920
#include "NumberSourceDataModel.hpp"
@@ -40,6 +41,8 @@ static std::shared_ptr<NodeDelegateModelRegistry> registerDataModels()
4041

4142
ret->registerModel<DivisionModel>("Operators");
4243

44+
ret->registerModel<RandomNumberModel>("Operators");
45+
4346
return ret;
4447
}
4548

include/QtNodes/internal/DefaultNodePainter.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class NODE_EDITOR_PUBLIC DefaultNodePainter : public AbstractNodePainter
3232

3333
void drawResizeRect(QPainter *painter, NodeGraphicsObject &ngo) const;
3434

35+
void drawProcessingIndicator(QPainter *painter, NodeGraphicsObject &ngo) const;
36+
3537
void drawValidationIcon(QPainter *painter, NodeGraphicsObject &ngo) const;
3638

3739
private:

include/QtNodes/internal/Definitions.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ enum class NodeRole {
3333
OutPortCount = 9, ///< `unsigned int`
3434
Widget = 10, ///< Optional `QWidget*` or `nullptr`
3535
ValidationState = 11, ///< Enum NodeValidationState of the node
36+
ProcessingStatus = 12 ///< Enum NodeProcessingStatus of the node
3637
};
3738
Q_ENUM_NS(NodeRole)
3839

include/QtNodes/internal/NodeDelegateModel.hpp

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <memory>
44

55
#include <QMetaType>
6+
#include <QPixmap>
67
#include <QtWidgets/QWidget>
78

89
#include "Definitions.hpp"
@@ -19,9 +20,9 @@ namespace QtNodes {
1920
struct NodeValidationState
2021
{
2122
enum class State : int {
22-
Valid = 0, ///< All required inputs are present and correct.
23-
Warning = 1, ///< Some inputs are missing or questionable, processing may be unreliable.
24-
Error = 2, ///< Inputs or settings are invalid, preventing successful computation.
23+
Valid = 0, ///< All required inputs are present and correct.
24+
Warning = 1, ///< Some inputs are missing or questionable, processing may be unreliable.
25+
Error = 2, ///< Inputs or settings are invalid, preventing successful computation.
2526
};
2627
bool isValid() { return _state == State::Valid; };
2728
QString const message() { return _stateMessage; }
@@ -31,6 +32,19 @@ struct NodeValidationState
3132
QString _stateMessage{""};
3233
};
3334

35+
/**
36+
* Describes the node status, depending on its current situation
37+
*/
38+
enum class NodeProcessingStatus : int {
39+
NoStatus = 0, ///< No processing status is shown in the Node UI.
40+
Updated = 1, ///< Node is up to date; its outputs reflect the current inputs and parameters.
41+
Processing = 2, ///< Node is currently running a computation.
42+
Pending = 3, ///< Node is out of date and waiting to be recomputed (e.g. manual/queued run).
43+
Empty = 4, ///< Node has no valid input data; nothing to compute.
44+
Failed = 5, ///< The last computation ended with an error.
45+
Partial = 6, ///< Computation finished incompletely; only partial results are available.
46+
};
47+
3448
class StyleCollection;
3549

3650
/**
@@ -39,7 +53,9 @@ class StyleCollection;
3953
* AbstractGraphModel.
4054
* This class is the same what has been called NodeDataModel before v3.
4155
*/
42-
class NODE_EDITOR_PUBLIC NodeDelegateModel : public QObject, public Serializable
56+
class NODE_EDITOR_PUBLIC NodeDelegateModel
57+
: public QObject
58+
, public Serializable
4359
{
4460
Q_OBJECT
4561

@@ -48,15 +64,15 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel : public QObject, public Serializable
4864

4965
virtual ~NodeDelegateModel() = default;
5066

67+
/// It is possible to hide caption in GUI
68+
virtual bool captionVisible() const { return true; }
69+
5170
/// Name makes this model unique
5271
virtual QString name() const = 0;
5372

5473
/// Caption is used in GUI
5574
virtual QString caption() const = 0;
5675

57-
/// It is possible to hide caption in GUI
58-
virtual bool captionVisible() const { return true; }
59-
6076
/// Port caption is used in GUI to label individual ports
6177
virtual QString portCaption(PortType, PortIndex) const { return QString(); }
6278

@@ -66,12 +82,16 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel : public QObject, public Serializable
6682
/// Validation State will default to Valid, but you can manipulate it by overriding in an inherited class
6783
virtual NodeValidationState validationState() const { return _nodeValidationState; }
6884

69-
public:
85+
/// Returns the curent processing status
86+
virtual NodeProcessingStatus processingStatus() const { return _processingStatus; }
87+
7088
QJsonObject save() const override;
7189

7290
void load(QJsonObject const &) override;
7391

74-
void setValidatonState(const NodeValidationState &validationState);
92+
void setValidationState(const NodeValidationState &validationState);
93+
94+
void setNodeProcessingStatus(NodeProcessingStatus status);
7595

7696
virtual unsigned int nPorts(PortType portType) const = 0;
7797

@@ -80,8 +100,16 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel : public QObject, public Serializable
80100
virtual ConnectionPolicy portConnectionPolicy(PortType, PortIndex) const;
81101

82102
NodeStyle const &nodeStyle() const;
103+
83104
void setNodeStyle(NodeStyle const &style);
84105

106+
QPixmap processingStatusIcon() const;
107+
108+
void setStatusIcon(NodeProcessingStatus status, const QPixmap &pixmap);
109+
110+
void setStatusIconStyle(ProcessingIconStyle const &style);
111+
112+
public:
85113
virtual void setInData(std::shared_ptr<NodeData> nodeData, PortIndex const portIndex) = 0;
86114

87115
virtual std::shared_ptr<NodeData> outData(PortIndex const port) = 0;
@@ -114,10 +142,20 @@ public Q_SLOTS:
114142
void dataInvalidated(PortIndex const index);
115143

116144
void computingStarted();
145+
117146
void computingFinished();
118147

119148
void embeddedWidgetSizeUpdated();
120149

150+
/// Request an update of the node's UI.
151+
/**
152+
* Emit this signal whenever some internal state change requires
153+
* the node to be repainted. The containing graph model will
154+
* propagate the update to the scene.
155+
*/
156+
void requestNodeUpdate();
157+
158+
/// Call this function before deleting the data associated with ports.
121159
/**
122160
* @brief Call this function before deleting the data associated with ports.
123161
* The function notifies the Graph Model and makes it remove and recompute the
@@ -144,8 +182,11 @@ public Q_SLOTS:
144182
NodeStyle _nodeStyle;
145183

146184
NodeValidationState _nodeValidationState;
185+
186+
NodeProcessingStatus _processingStatus{NodeProcessingStatus::NoStatus};
147187
};
148188

149189
} // namespace QtNodes
150190

151191
Q_DECLARE_METATYPE(QtNodes::NodeValidationState)
192+
Q_DECLARE_METATYPE(QtNodes::NodeProcessingStatus)

include/QtNodes/internal/NodeGraphicsObject.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include <QIcon>
34
#include <QtCore/QUuid>
45
#include <QtWidgets/QGraphicsObject>
56

include/QtNodes/internal/NodeStyle.hpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,32 @@
11
#pragma once
22

3+
#include <QIcon>
34
#include <QtGui/QColor>
45

56
#include "Export.hpp"
67
#include "Style.hpp"
78

89
namespace QtNodes {
910

11+
/**
12+
* Describes the position of the processing icon on the node ui
13+
*/
14+
enum class ProcessingIconPos {
15+
BottomLeft = 0, /// icon on the bottom left position
16+
BottomRight = 1, /// icon on the bottom right position
17+
};
18+
19+
/**
20+
* Defines the processing icon style;
21+
*/
22+
struct ProcessingIconStyle
23+
{
24+
ProcessingIconPos _pos{ProcessingIconPos::BottomRight};
25+
double _size{20.0};
26+
double _margin{8.0};
27+
int _resolution{64};
28+
};
29+
1030
class NODE_EDITOR_PUBLIC NodeStyle : public Style
1131
{
1232
public:
@@ -51,5 +71,14 @@ class NODE_EDITOR_PUBLIC NodeStyle : public Style
5171
float ConnectionPointDiameter;
5272

5373
float Opacity;
74+
75+
QIcon statusUpdated{QStringLiteral("://status_icons/updated.svg")};
76+
QIcon statusProcessing{QStringLiteral("://status_icons/processing.svg")};
77+
QIcon statusPending{QStringLiteral("://status_icons/pending.svg")};
78+
QIcon statusInvalid{QStringLiteral("://status_icons/failed.svg")};
79+
QIcon statusEmpty{QStringLiteral("://status_icons/empty.svg")};
80+
QIcon statusPartial{QStringLiteral("://status_icons/partial.svg")};
81+
82+
ProcessingIconStyle processingIconStyle{};
5483
};
5584
} // namespace QtNodes

resources/resources.qrc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,11 @@
22
<qresource prefix="/">
33
<file>DefaultStyle.json</file>
44
<file>info-tooltip.svg</file>
5+
<file>status_icons/empty.svg</file>
6+
<file>status_icons/failed.svg</file>
7+
<file>status_icons/partial.svg</file>
8+
<file>status_icons/pending.svg</file>
9+
<file>status_icons/processing.svg</file>
10+
<file>status_icons/updated.svg</file>
511
</qresource>
612
</RCC>

0 commit comments

Comments
 (0)