| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [View Raw Code] [Original HTTPS Page] |
Nodes and Connections do not have any dedicated data instances. Instead, all the relevant data is stored inside a user-defined GraphModel class inherited from AbstractGraphModel.
// Definitions.hpp using NodeId = unsigned int;
Each node is associated with a unique integer NodeId which is returned from the function NodeId AbstractGraphModel::addNode(QString).
The ConnectionId is nothing else but a combination of input and output NodeId values with the corresponding PortIndex:
// Definitinos.hpp
struct ConnectionId
{
NodeId outNodeId;
PortIndex outPortIndex;
NodeId inNodeId;
PortIndex inPortIndex;
};
The serialization is supported at the moment by DataFlowGraphModel. Should you implement your own derivative of the class AbstractGraphModel, it's up to you to support the serialization using the existing helper functions. An example of such a code could be found in src/DataFlowGraphModel.cpp.
In order so save the whole scene we normally make a loop over all the nodes and save their data in a Json arrray with a key "nodes" and then over all the connections which are saved in an array "connections".
A typical node Json saved for the data flow scenario looks as follows:
{
"id" : 0,
"internal-data" : {
"model-name" : "Subtraction"
},
"position" : {
"x" : -383,
"y" : -95
}
}
The section internal-data is filled by the specific data of the node itself and is created in the function DataFlowGraphModel::saveNode(NodeId). In the data-propagation workflow we store a name of the model there. In your application it could be any internal additional data, i.e. an internal node state.
The Json for a serialized Connection in this case looks very simple:
{
"inPortIndex" : 0,
"intNodeId" : 1,
"outNodeId" : 0,
"outPortIndex" : 0
}
The data above is produced by a function DataFlowGraphModel::saveConnection.
In order to support the undo/redo capabilities we employ the standar Qt's class QUndoStack . We keep a stack instance inside your BasicGraphicsScene (or its derivatives).
Some default QUndoCommand s are already implemented in the file src/QUndoCommands.cpp
The command DeleteCommand uses serialization to store the information of the removed objects, namely AbstractGraphModel::saveNode(NodeId) and AbstractGraphModel::saveConnection(ConnectionId). Make sure you override these functions in your derived graph models.
If your task is to visualize a graph specific to your application and you do not need the basic "data propagation" semantic implemented in this library (DataFlowGraphModel, DataFlowGraphicsScene), you might need to derive from AbstractGraphModel and implement several abstract functions.
The models follows pretty much the ideas underlying the Qt's QAbstractItemModel. The class delivers the IDs for all the existing scene objects, the model is responsible for generating such unique IDs. You should deliver information about nodes via AbstractGraphModel::nodeData and assign the data to nodes using AbstractGraphModel::setNodeData. The passed and returned data is wrapped into the data type QVariant
The pivotal enum that defines type of the information we need to obtain is called NodeRole. See the file include/QtNodes/internal/Definitions.hpp.
| NodeRole | Description |
|---|---|
| Type | It corresponds to the type of the node and is described by a QString value. For example, in the data-flow models we return the name of the NodeDelegateModel that needs to be instantiated by NodeDelegateModelRegistry. |
| Position | QPointF position of the node on the scene. |
| Size | QSize the size of the internal area of thet node. Typically it is used by embedding a widged inside a node. |
| CaptionVisible | bool that defines whether to show a node's caption. |
| Caption | QString defines whether to show a node's caption. |
| Style | Node editor's internal json structure returned as a QVariantMap that defines colors, gradients and effects for the node painting |
| InternalData | QJsonObject converted to QVariantMap that serializes the iternal node's state. |
| InPortCount | unsigned int -- the number of input ports. |
| OutPortCount | unsigned int -- the number of output ports. |
| Widget | QWidget* a pointer to allocated QWidget instance that must be embedded into a node. If nothing is embedded, it must be nullptr by default. |
Default Node, Connection, and GraphicsView styles are stored in a centrall class StyleCollection.
Each default style is parsed from an internal Json string and stored in a corresponding data-class. Below you'll find the contents of the Json strings at the moment of writing this documentation.
GraphicsViewStyle
{
"GraphicsViewStyle": {
"BackgroundColor": [53, 53, 53],
"FineGridColor": [60, 60, 60],
"CoarseGridColor": [25, 25, 25]
}
}
NodeStyle
{
"NodeStyle": {
"NormalBoundaryColor": [255, 255, 255],
"SelectedBoundaryColor": [255, 165, 0],
"GradientColor0": "gray",
"GradientColor1": [80, 80, 80],
"GradientColor2": [64, 64, 64],
"GradientColor3": [58, 58, 58],
"ShadowColor": [20, 20, 20],
"FontColor" : "white",
"FontColorFaded" : "gray",
"ConnectionPointColor": [169, 169, 169],
"FilledConnectionPointColor": "cyan",
"ErrorColor": "red",
"WarningColor": [128, 128, 0],
"PenWidth": 1.0,
"HoveredPenWidth": 1.5,
"ConnectionPointDiameter": 8.0,
"Opacity": 0.8
}
}
ConnectionStyle
{
"ConnectionStyle": {
"ConstructionColor": "gray",
"NormalColor": "darkcyan",
"SelectedColor": [100, 100, 100],
"SelectedHaloColor": "orange",
"HoveredColor": "lightcyan",
"LineWidth": 3.0,
"ConstructionLineWidth": 2.0,
"PointDiameter": 10.0,
"UseDataDefinedColors": false
}
}
This feature might seem to be a bit "raw". I haven't had good use cases from real life projects to polish the code and the resulting node layout and rendering.
The current node layout in a vertical mode looks as follows:
-------o-------------o------- | PortCaption PortCaption | | | | Node Caption | | | | | | PortCaption | --------------o--------------
Dynamic Ports operations are driven by functions of the class AbstractGraphModel:
The function with the name "AboutTo" prepares the changes:
The functions porstDeleted and portsInserted create the new precomputed connections with the correct IDs.
If you want to modify the number of ports in your code, it should approximately as follows:
void YourGraphModel::addPort()
{
portsAboutToBeInserted(nodeId, PortType::Out, 1, 2);
// DO YOUR UNDERLYING DATA MODIFICATIONS HERE
// The function call above has prepared the insertion of new output ports
// with the indexes 1 and 2.
// All the existed connectes below the new port 2 would be deleted and
// re-inserted with the new IDs (shifted by 2).
porstInserted();
}
It is possible to completely disable or "freeze" the nodes. This would make them insensitive to moving and selecting events with the mouse.
In order to achieve such a behavior set specific flags and return from your graph model:
NodeFlags
YourGraphModel::
nodeFlags(NodeId nodeId) const override
{
auto basicFlags = DataFlowGraphModel::nodeFlags(nodeId);
if (_nodesLocked)
basicFlags |= NodeFlag::Locked;
return basicFlags;
}
For disabling detaching the connections from certain nodes override the function virtual bool AbstractGraphModel::detachPossible(ConnectionId const) const. The default implementaion always returns true.
Data-propagating classes add extra funtionality to the basic AbstractGraphModel which allows them to push the data from node to node upon creating a connection.
The chain starts from the instance of a NodeDelegateModel. It emits a Qt signal dataUpdated(PortIndex). We always assume that the data is emitted from one of the right hand side ports (PortType::Out).
Then the function DataFlowGraphModel::onOutPortDataUpdated(NodeId, PortIndex) comes into play. It reads the data from the output port, collects all the attached connections for the given PortIndex and sets the data to the connected nodes using DataFlowGraphModel::setPortData. After setting the data to the input delegate model via NodeDelegateModel::setInData(...) we emit the signal inPortDataWasSet(nodeId, portType, portIndex). The signal is used to redraw the receiver node and could be hooked up for other user's purposes.
NodeDelegateModel:::dataUpdated(PortIndex) // Source Delegate Model -> source NodeId DataFlowGraphModel::onOutPortDataUpdated(NodeId, PortIndex) // soure NodeId -> target NodeId DataFlowGraphModel::setPortData() // target NodeId -> target Delegate Model NodeDelegateModel::setInData(NodeData, portIndex) DataFlowGraphModel::setPortData()
The class AbstractGraphModel is independent of any scenes or visualization windows. It is possible to instantiate a descendant of this abstract class and populate the graph model.
Any instantiated BasicGraphicsScene could be also used without attaching it to a dedicated GraphicsView.
| Back | FazBrowse Home | New Git URL |