Skip to content

Commit 8b054bc

Browse files
authored
fix: control loops before connecting depending on AbstractGraphModel::loopsEnabled() (#484)
* Extend data flow test classe to propagate through multiple nodes * Control loops before connecting depending on AbstractGraphModel::loopsEnabled() (#198)
1 parent b7fb9f5 commit 8b054bc

8 files changed

Lines changed: 353 additions & 276 deletions

include/QtNodes/internal/AbstractGraphModel.hpp

Lines changed: 102 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
#pragma once
22

33
#include "Export.hpp"
4-
5-
#include <unordered_map>
6-
#include <unordered_set>
4+
#include "ConnectionIdHash.hpp"
5+
#include "Definitions.hpp"
76

87
#include <QtCore/QJsonObject>
98
#include <QtCore/QObject>
109
#include <QtCore/QVariant>
1110

12-
#include "ConnectionIdHash.hpp"
13-
#include "Definitions.hpp"
11+
#include <unordered_set>
12+
1413

1514
namespace QtNodes {
1615

@@ -33,23 +32,23 @@ class NODE_EDITOR_PUBLIC AbstractGraphModel : public QObject
3332

3433
/// @brief Returns the full set of unique Node Ids.
3534
/**
36-
* Model creator is responsible for generating unique `unsigned int`
37-
* Ids for all the nodes in the graph. From an Id it should be
38-
* possible to trace back to the model's internal representation of
39-
* the node.
40-
*/
35+
* Model creator is responsible for generating unique `unsigned int`
36+
* Ids for all the nodes in the graph. From an Id it should be
37+
* possible to trace back to the model's internal representation of
38+
* the node.
39+
*/
4140
virtual std::unordered_set<NodeId> allNodeIds() const = 0;
4241

4342
/**
44-
* A collection of all input and output connections for the given `nodeId`.
45-
*/
43+
* A collection of all input and output connections for the given `nodeId`.
44+
*/
4645
virtual std::unordered_set<ConnectionId> allConnectionIds(NodeId const nodeId) const = 0;
4746

4847
/// @brief Returns all connected Node Ids for given port.
4948
/**
50-
* The returned set of nodes and port indices correspond to the type
51-
* opposite to the given `portType`.
52-
*/
49+
* The returned set of nodes and port indices correspond to the type
50+
* opposite to the given `portType`.
51+
*/
5352
virtual std::unordered_set<ConnectionId> connections(NodeId nodeId,
5453
PortType portType,
5554
PortIndex index) const
@@ -60,51 +59,52 @@ class NODE_EDITOR_PUBLIC AbstractGraphModel : public QObject
6059

6160
/// Creates a new node instance in the derived class.
6261
/**
63-
* The model is responsible for generating a unique `NodeId`.
64-
* @param[in] nodeType is free to be used and interpreted by the
65-
* model on its own, it helps to distinguish between possible node
66-
* types and create a correct instance inside.
67-
*/
62+
* The model is responsible for generating a unique `NodeId`.
63+
* @param[in] nodeType is free to be used and interpreted by the
64+
* model on its own, it helps to distinguish between possible node
65+
* types and create a correct instance inside.
66+
*/
6867
virtual NodeId addNode(QString const nodeType = QString()) = 0;
6968

7069
/// Model decides if a conection with a given connection Id possible.
7170
/**
72-
* The default implementation compares corresponding data types.
73-
*
74-
* It is possible to override the function and connect non-equal
75-
* data types.
76-
*/
71+
* The default implementation compares corresponding data types.
72+
*
73+
* It is possible to override the function and connect non-equal
74+
* data types.
75+
*/
7776
virtual bool connectionPossible(ConnectionId const connectionId) const = 0;
7877

7978
/// Defines if detaching the connection is possible.
8079
virtual bool detachPossible(ConnectionId const) const { return true; }
8180

82-
/// Creates a new connection between two nodes.
8381
/**
84-
* Default implementation emits signal
85-
* `connectionCreated(connectionId)`
86-
*
87-
* In the derived classes user must emite the signal to notify the
88-
* scene about the changes.
89-
*/
82+
* @brief Creates a new connection between two nodes.
83+
*
84+
* Default implementation emits signal
85+
* `connectionCreated(connectionId)`
86+
*
87+
* In the derived classes user must emite the signal to notify the
88+
* scene about the changes.
89+
*/
9090
virtual void addConnection(ConnectionId const connectionId) = 0;
9191

9292
/**
93-
* @returns `true` if there is data in the model associated with the
94-
* given `nodeId`.
95-
*/
93+
* @returns `true` if there is data in the model associated with the
94+
* given `nodeId`.
95+
*/
9696
virtual bool nodeExists(NodeId const nodeId) const = 0;
9797

9898
/// @brief Returns node-related data for requested NodeRole.
9999
/**
100-
* @returns Node Caption, Node Caption Visibility, Node Position etc.
101-
*/
100+
* @returns Node Caption, Node Caption Visibility, Node Position etc.
101+
*/
102102
virtual QVariant nodeData(NodeId nodeId, NodeRole role) const = 0;
103103

104104
/**
105-
* A utility function that unwraps the `QVariant` value returned from the
106-
* standard `QVariant AbstractGraphModel::nodeData(NodeId, NodeRole)` function.
107-
*/
105+
* A utility function that unwraps the `QVariant` value returned from the
106+
* standard `QVariant AbstractGraphModel::nodeData(NodeId, NodeRole)` function.
107+
*/
108108
template<typename T>
109109
T nodeData(NodeId nodeId, NodeRole role) const
110110
{
@@ -117,26 +117,28 @@ class NODE_EDITOR_PUBLIC AbstractGraphModel : public QObject
117117
return NodeFlag::NoFlags;
118118
}
119119

120-
/// @brief Sets node properties.
121120
/**
122-
* Sets: Node Caption, Node Caption Visibility,
123-
* Shyle, State, Node Position etc.
124-
* @see NodeRole.
125-
*/
121+
* @brief Sets node properties.
122+
*
123+
* Sets: Node Caption, Node Caption Visibility,
124+
* Shyle, State, Node Position etc.
125+
* @see NodeRole.
126+
*/
126127
virtual bool setNodeData(NodeId nodeId, NodeRole role, QVariant value) = 0;
127128

128-
/// @brief Returns port-related data for requested NodeRole.
129129
/**
130-
* @returns Port Data Type, Port Data, Connection Policy, Port
131-
* Caption.
132-
*/
130+
* @brief Returns port-related data for requested NodeRole.
131+
*
132+
* @returns Port Data Type, Port Data, Connection Policy, Port
133+
* Caption.
134+
*/
133135
virtual QVariant portData(NodeId nodeId, PortType portType, PortIndex index, PortRole role) const
134136
= 0;
135137

136138
/**
137-
* A utility function that unwraps the `QVariant` value returned from the
138-
* standard `QVariant AbstractGraphModel::portData(...)` function.
139-
*/
139+
* A utility function that unwraps the `QVariant` value returned from the
140+
* standard `QVariant AbstractGraphModel::portData(...)` function.
141+
*/
140142
template<typename T>
141143
T portData(NodeId nodeId, PortType portType, PortIndex index, PortRole role) const
142144
{
@@ -155,74 +157,76 @@ class NODE_EDITOR_PUBLIC AbstractGraphModel : public QObject
155157
virtual bool deleteNode(NodeId const nodeId) = 0;
156158

157159
/**
158-
* Reimplement the function if you want to store/restore the node's
159-
* inner state during undo/redo node deletion operations.
160-
*/
160+
* Reimplement the function if you want to store/restore the node's
161+
* inner state during undo/redo node deletion operations.
162+
*/
161163
virtual QJsonObject saveNode(NodeId const) const { return {}; }
162164

163165
/**
164-
* Reimplement the function if you want to support:
165-
*
166-
* - graph save/restore operations,
167-
* - undo/redo operations after deleting the node.
168-
*
169-
* QJsonObject must contain following fields:
170-
*
171-
*
172-
* ```
173-
* {
174-
* id : 5,
175-
* position : { x : 100, y : 200 },
176-
* internal-data {
177-
* "your model specific data here"
178-
* }
179-
* }
180-
* ```
181-
*
182-
* The function must do almost exacly the same thing as the normal addNode().
183-
* The main difference is in a model-specific `inner-data` processing.
184-
*/
166+
* Reimplement the function if you want to support:
167+
*
168+
* - graph save/restore operations,
169+
* - undo/redo operations after deleting the node.
170+
*
171+
* QJsonObject must contain following fields:
172+
*
173+
*
174+
* ```json
175+
* {
176+
* id : 5,
177+
* position : { x : 100, y : 200 },
178+
* internal-data {
179+
* "your model specific data here"
180+
* }
181+
* }
182+
* ```
183+
*
184+
* The function must do almost exacly the same thing as the normal addNode().
185+
* The main difference is in a model-specific `inner-data` processing.
186+
*/
185187
virtual void loadNode(QJsonObject const &) {}
186188

189+
virtual bool loopsEnabled() const { return true; }
190+
187191
public:
188192
/**
189-
* Function clears connections attached to the ports that are scheduled to be
190-
* deleted. It must be called right before the model removes its old port data.
191-
*
192-
* @param nodeId Defines the node to be modified
193-
* @param portType Is either PortType::In or PortType::Out
194-
* @param first Index of the first port to be removed
195-
* @param last Index of the last port to be removed
196-
*/
193+
* Function clears connections attached to the ports that are scheduled to be
194+
* deleted. It must be called right before the model removes its old port data.
195+
*
196+
* @param nodeId Defines the node to be modified
197+
* @param portType Is either PortType::In or PortType::Out
198+
* @param first Index of the first port to be removed
199+
* @param last Index of the last port to be removed
200+
*/
197201
void portsAboutToBeDeleted(NodeId const nodeId,
198202
PortType const portType,
199203
PortIndex const first,
200204
PortIndex const last);
201205

202206
/**
203-
* Signal emitted when model no longer has the old data associated with the
204-
* given port indices and when the node must be repainted.
205-
*/
207+
* Signal emitted when model no longer has the old data associated with the
208+
* given port indices and when the node must be repainted.
209+
*/
206210
void portsDeleted();
207211

208212
/**
209-
* Signal emitted when model is about to create new ports on the given node.
210-
* @param first Is the first index of the new port after insertion.
211-
* @param last Is the last index of the new port after insertion.
212-
*
213-
* Function caches existing connections that are located after the `last` port
214-
* index. For such connections the new "post-insertion" addresses are computed
215-
* and stored until the function AbstractGraphModel::portsInserted is called.
216-
*/
213+
* Signal emitted when model is about to create new ports on the given node.
214+
* @param first Is the first index of the new port after insertion.
215+
* @param last Is the last index of the new port after insertion.
216+
*
217+
* Function caches existing connections that are located after the `last` port
218+
* index. For such connections the new "post-insertion" addresses are computed
219+
* and stored until the function AbstractGraphModel::portsInserted is called.
220+
*/
217221
void portsAboutToBeInserted(NodeId const nodeId,
218222
PortType const portType,
219223
PortIndex const first,
220224
PortIndex const last);
221225

222226
/**
223-
* Function re-creates the connections that were shifted during the port
224-
* insertion. After that the node is updated.
225-
*/
227+
* Function re-creates the connections that were shifted during the port
228+
* insertion. After that the node is updated.
229+
*/
226230
void portsInserted();
227231

228232
Q_SIGNALS:

include/QtNodes/internal/DataFlowGraphModel.hpp

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class NODE_EDITOR_PUBLIC DataFlowGraphModel : public AbstractGraphModel, public
4343

4444
NodeId addNode(QString const nodeType) override;
4545

46+
4647
bool connectionPossible(ConnectionId const connectionId) const override;
4748

4849
void addConnection(ConnectionId const connectionId) override;
@@ -72,16 +73,19 @@ class NODE_EDITOR_PUBLIC DataFlowGraphModel : public AbstractGraphModel, public
7273

7374
QJsonObject saveNode(NodeId const) const override;
7475

75-
QJsonObject save() const override;
76-
7776
void loadNode(QJsonObject const &nodeJson) override;
7877

78+
79+
// From Serializable
80+
QJsonObject save() const override;
81+
82+
// From Serializable
7983
void load(QJsonObject const &json) override;
8084

8185
/**
82-
* Fetches the NodeDelegateModel for the given `nodeId` and tries to cast the
83-
* stored pointer to the given type
84-
*/
86+
* Fetches the NodeDelegateModel for the given `nodeId` and tries to cast the
87+
* stored pointer to the given type
88+
*/
8589
template<typename NodeDelegateModelType>
8690
NodeDelegateModelType *delegateModel(NodeId const nodeId)
8791
{
@@ -94,6 +98,9 @@ class NODE_EDITOR_PUBLIC DataFlowGraphModel : public AbstractGraphModel, public
9498
return model;
9599
}
96100

101+
/// Loops do not make any sense in uni-direction data propagation
102+
bool loopsEnabled() const override { return false; }
103+
97104
Q_SIGNALS:
98105
void inPortDataWasSet(NodeId const, PortType const, PortIndex const);
99106

@@ -106,15 +113,15 @@ class NODE_EDITOR_PUBLIC DataFlowGraphModel : public AbstractGraphModel, public
106113

107114
private Q_SLOTS:
108115
/**
109-
* Fuction is called in three cases:
110-
*
111-
* - By underlying NodeDelegateModel when a node has new data to propagate.
112-
* @see DataFlowGraphModel::addNode
113-
* - When a new connection is created.
114-
* @see DataFlowGraphModel::addConnection
115-
* - When a node restored from JSON an needs to send data downstream.
116-
* @see DataFlowGraphModel::loadNode
117-
*/
116+
* Fuction is called in three cases:
117+
*
118+
* - By underlying NodeDelegateModel when a node has new data to propagate.
119+
* @see DataFlowGraphModel::addNode
120+
* - When a new connection is created.
121+
* @see DataFlowGraphModel::addConnection
122+
* - When a node restored from JSON an needs to send data downstream.
123+
* @see DataFlowGraphModel::loadNode
124+
*/
118125
void onOutPortDataUpdated(NodeId const nodeId, PortIndex const portIndex);
119126

120127
/// Function is called after detaching a connection.

0 commit comments

Comments
 (0)