Skip to content

Commit 770a1aa

Browse files
LandonJerrepaceholder
authored andcommitted
Implement runtime validation and error reporting (paceholder#46)
* Node validation and error display implemented * Added option to iterate through nodes in the scene. (Useful to read out data from all the nodes, makes code generation based on the graphs easier.) * Visual error fix for nodes that change size depending on input, plus minor styling and logic fixes in the example, based on the comments of @russelltg * Validation extended to handle warnings, based on the idea of @RusselTG ; Error display colors now come from the style system; Additional bugfixes * DefaultStyle.json identation fix * FlowScene.cpp identation fix * Travis build test fix
1 parent a62f088 commit 770a1aa

20 files changed

+240
-7
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ matrix:
3232

3333
before_install:
3434
#- if [ "$QT_BASE" = "54" ]; then sudo add-apt-repository ppa:beineri/opt-qt542-trusty -y; fi
35-
- if [ "$QT_BASE" = "57" ]; then sudo add-apt-repository ppa:beineri/opt-qt57-trusty -y; fi
35+
- if [ "$QT_BASE" = "57" ]; then sudo add-apt-repository ppa:beineri/opt-qt571-trusty -y; fi
3636
- sudo apt-get update -qq
3737

3838
install:

examples/calculator/AdditionModel.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,15 @@ class AdditionModel : public MathOperationDataModel
5252

5353
if (n1 && n2)
5454
{
55+
modelValidationState = NodeValidationState::Valid;
56+
modelValidationError = QString("");
5557
_result = std::make_shared<NumberData>(n1->number() +
5658
n2->number());
5759
}
5860
else
5961
{
62+
modelValidationState = NodeValidationState::Warning;
63+
modelValidationError = QString("Missing or incorrect inputs");
6064
_result.reset();
6165
}
6266

examples/calculator/DivisionModel.hpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class DivisionModel : public MathOperationDataModel
4545
default:
4646
break;
4747
}
48-
return QString("");
48+
return QString("");
4949
}
5050

5151
QString
@@ -74,16 +74,26 @@ class DivisionModel : public MathOperationDataModel
7474
auto n1 = _number1.lock();
7575
auto n2 = _number2.lock();
7676

77-
if (n1 && n2 && (n2->number() != 0.0))
77+
if (n2 && (n2->number() == 0.0))
7878
{
79+
modelValidationState = NodeValidationState::Error;
80+
modelValidationError = QString("Division by zero error");
81+
_result.reset();
82+
}
83+
else if (n1 && n2)
84+
{
85+
modelValidationState = NodeValidationState::Valid;
86+
modelValidationError = QString("");
7987
_result = std::make_shared<NumberData>(n1->number() /
80-
n2->number());
88+
n2->number());
8189
}
8290
else
8391
{
92+
modelValidationState = NodeValidationState::Warning;
93+
modelValidationError = QString("Missing or incorrect inputs");
8494
_result.reset();
8595
}
86-
96+
8797
emit dataUpdated(outPortIndex);
8898
}
8999
};

examples/calculator/MathOperationDataModel.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,17 @@ setInData(std::shared_ptr<NodeData> data, PortIndex portIndex)
5353
}
5454

5555

56+
NodeValidationState
57+
MathOperationDataModel::
58+
validationState() const
59+
{
60+
return modelValidationState;
61+
}
62+
63+
64+
QString
65+
MathOperationDataModel::
66+
validationMessage() const
67+
{
68+
return modelValidationError;
69+
}

examples/calculator/MathOperationDataModel.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ class MathOperationDataModel : public NodeDataModel
3232

3333
QWidget * embeddedWidget() override { return nullptr; }
3434

35+
NodeValidationState validationState() const override;
36+
37+
QString validationMessage() const override;
38+
3539
protected:
3640

3741
virtual void compute() = 0;
@@ -42,4 +46,7 @@ class MathOperationDataModel : public NodeDataModel
4246
std::weak_ptr<NumberData> _number2;
4347

4448
std::shared_ptr<NumberData> _result;
49+
50+
NodeValidationState modelValidationState = NodeValidationState::Warning;
51+
QString modelValidationError = QString("Missing or incorrect inputs");
4552
};

examples/calculator/MultiplicationModel.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,15 @@ class MultiplicationModel : public MathOperationDataModel
5252

5353
if (n1 && n2)
5454
{
55+
modelValidationState = NodeValidationState::Valid;
56+
modelValidationError = QString("");
5557
_result = std::make_shared<NumberData>(n1->number() *
5658
n2->number());
5759
}
5860
else
5961
{
62+
modelValidationState = NodeValidationState::Warning;
63+
modelValidationError = QString("Missing or incorrect inputs");
6064
_result.reset();
6165
}
6266

examples/calculator/NumberDisplayDataModel.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,32 @@ setInData(std::shared_ptr<NodeData> data, int)
5858

5959
if (numberData)
6060
{
61+
modelValidationState = NodeValidationState::Valid;
62+
modelValidationError = QString("");
6163
_label->setText(numberData->numberAsText());
6264
}
6365
else
6466
{
67+
modelValidationState = NodeValidationState::Warning;
68+
modelValidationError = QString("Missing or incorrect inputs");
6569
_label->clear();
6670
}
6771

6872
_label->adjustSize();
6973
}
74+
75+
76+
NodeValidationState
77+
NumberDisplayDataModel::
78+
validationState() const
79+
{
80+
return modelValidationState;
81+
}
82+
83+
84+
QString
85+
NumberDisplayDataModel::
86+
validationMessage() const
87+
{
88+
return modelValidationError;
89+
}

examples/calculator/NumberDisplayDataModel.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,16 @@ class NumberDisplayDataModel : public NodeDataModel
6363
QWidget *
6464
embeddedWidget() override { return _label; }
6565

66+
NodeValidationState
67+
validationState() const override;
68+
69+
QString
70+
validationMessage() const override;
71+
6672
private:
6773

74+
NodeValidationState modelValidationState = NodeValidationState::Warning;
75+
QString modelValidationError = QString("Missing or incorrect inputs");
76+
6877
QLabel * _label;
6978
};

examples/calculator/SubtractionModel.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,15 @@ class SubtractionModel : public MathOperationDataModel
7777

7878
if (n1 && n2)
7979
{
80+
modelValidationState = NodeValidationState::Valid;
81+
modelValidationError = QString("");
8082
_result = std::make_shared<NumberData>(n1->number() -
8183
n2->number());
8284
}
8385
else
8486
{
87+
modelValidationState = NodeValidationState::Warning;
88+
modelValidationError = QString("Missing or incorrect inputs");
8589
_result.reset();
8690
}
8791

resources/DefaultStyle.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
"FontColorFaded" : "gray",
1717
"ConnectionPointColor": [169, 169, 169],
1818
"FilledConnectionPointColor": "cyan",
19+
"ErrorColor": "red",
20+
"WarningColor": [128, 128, 0],
1921

2022
"PenWidth": 1.0,
2123
"HoveredPenWidth": 1.5,

src/FlowScene.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,17 @@ setRegistry(std::shared_ptr<DataModelRegistry> registry)
215215
}
216216

217217

218+
void
219+
FlowScene::
220+
iterateOverNodes(std::function<void(Node*)> visitor)
221+
{
222+
for (const auto& _node : _nodes)
223+
{
224+
visitor(_node.second.get());
225+
}
226+
}
227+
228+
218229
//------------------------------------------------------------------------------
219230

220231
void

src/FlowScene.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <unordered_map>
77
#include <tuple>
88
#include <memory>
9+
#include <functional>
910

1011
#include "Connection.hpp"
1112
#include "Export.hpp"
@@ -64,6 +65,9 @@ class NODE_EDITOR_PUBLIC FlowScene
6465
void
6566
setRegistry(std::shared_ptr<DataModelRegistry> registry);
6667

68+
void
69+
iterateOverNodes(std::function<void(Node*)> visitor);
70+
6771
public:
6872

6973
void

src/Node.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,12 @@ propagateData(std::shared_ptr<NodeData> nodeData,
176176
PortIndex inPortIndex) const
177177
{
178178
_nodeDataModel->setInData(nodeData, inPortIndex);
179-
179+
180+
//Recalculate the nodes visuals. A data change can result in the node taking more space than before, so this forces a recalculate+repaint on the affected node
181+
_nodeGraphicsObject->setGeometryChanged();
182+
_nodeGeometry.recalculateSize();
183+
_nodeGraphicsObject->update();
184+
_nodeGraphicsObject->moveConnections();
180185
}
181186

182187

src/NodeDataModel.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@
1010

1111
#include "Export.hpp"
1212

13+
enum class NodeValidationState
14+
{
15+
Valid,
16+
Warning,
17+
Error
18+
};
19+
1320
class NODE_EDITOR_PUBLIC NodeDataModel
1421
: public QObject
1522
, public Serializable
@@ -74,6 +81,14 @@ class NODE_EDITOR_PUBLIC NodeDataModel
7481
virtual
7582
bool
7683
resizable() const { return false; }
84+
85+
virtual
86+
NodeValidationState
87+
validationState() const { return NodeValidationState::Valid; }
88+
89+
virtual
90+
QString
91+
validationMessage() const { return QString(""); }
7792

7893
signals:
7994

src/NodeGeometry.cpp

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ recalculateSize() const
8989
}
9090

9191
_width = std::max(_width, captionWidth());
92+
93+
if (_dataModel->validationState() != NodeValidationState::Valid)
94+
{
95+
_width = std::max(_width, validationWidth());
96+
_height += validationHeight() + _spacing;
97+
}
9298
}
9399

94100

@@ -213,7 +219,11 @@ widgetPosition() const
213219
{
214220
if (auto w = _dataModel->embeddedWidget())
215221
{
216-
222+
if (_dataModel->validationState() != NodeValidationState::Valid)
223+
{
224+
return QPointF(_spacing + portWidth(PortType::In),
225+
(captionHeight() + _height - validationHeight() - _spacing - w->height()) / 2.0);
226+
}
217227
return QPointF(_spacing + portWidth(PortType::In),
218228
(captionHeight() + _height - w->height()) / 2.0);
219229
}
@@ -248,6 +258,26 @@ captionWidth() const
248258
}
249259

250260

261+
unsigned int
262+
NodeGeometry::
263+
validationHeight() const
264+
{
265+
QString msg = _dataModel->validationMessage();
266+
267+
return _boldFontMetrics.boundingRect(msg).height();
268+
}
269+
270+
271+
unsigned int
272+
NodeGeometry::
273+
validationWidth() const
274+
{
275+
QString msg = _dataModel->validationMessage();
276+
277+
return _boldFontMetrics.boundingRect(msg).width();
278+
}
279+
280+
251281
unsigned int
252282
NodeGeometry::
253283
portWidth(PortType portType) const
@@ -259,9 +289,13 @@ portWidth(PortType portType) const
259289
QString name;
260290

261291
if (_dataModel->portCaptionVisible(portType, i))
292+
{
262293
name = _dataModel->portCaption(portType, i);
294+
}
263295
else
296+
{
264297
name = _dataModel->dataType(portType, i).name;
298+
}
265299

266300
width = std::max(unsigned(_fontMetrics.width(name)),
267301
width);

src/NodeGeometry.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@ class NodeGeometry
102102
QPointF
103103
widgetPosition() const;
104104

105+
unsigned int
106+
validationHeight() const;
107+
108+
unsigned int
109+
validationWidth() const;
110+
105111
private:
106112

107113
unsigned int

0 commit comments

Comments
 (0)