1Advanced Use (C++) 2============================ 3 4Using from C++ 5------------------ 6 7QuickQanava rely on and underlying QML engine to efficiently draw visual content using QML scene graph (eventually with HW OpenGL acceleration). While the API is almost 100% usable from C++, a QML engine must be initialized in the background for rendering topology, a minimal QuickQanava initialization from QML should thus create graph view and a graph topology components: 8 9``` cpp hl_lines="10" 10// main.qml 11import QuickQanava 2.0 as Qan 12import "qrc:/QuickQanava" as Qan 13 14ApplicationWindow { 15 title: "QuickQanava cpp API" 16 Qan.GraphView { 17 anchors.fill: parent 18 graph : Qan.Graph { 19 objectName: "graph" 20 anchors.fill: parent 21 } // Qan.Graph: graph 22 } 23} 24``` 25 26On the C++ side, a QML engine should be initialized before starting the application event loop: 27 28``` cpp hl_lines="7" 29// main.cpp 30int main( int argc, char** argv ) 31{ 32 QGuiApplication app(argc, argv); 33 QQmlApplicationEngine engine; 34 QuickQanava::initialize(&engine); // Mandatory 35 engine.load(QUrl("qrc:/main.qml")); 36 // Custom "synchronous" topology initialization should be added here 37 return app.exec(); 38} 39``` 40 41It is then easy to access `#!js Qan.Graph{ objectName:"graph"}` QML component trough it's `#!cpp qan::Graph` virtual interface: 42``` cpp hl_lines="3" 43 QPointer<CustomGraph> graph = nullptr; 44 for (const auto rootObject : engine.rootObjects()) { 45 graph = qobject_cast<CustomGraph*>(rootObject->findChild<QQuickItem *>("graph")); 46 if (graph) 47 break; 48 } 49 if (graph) { 50 // Go on 51 } 52``` 53 54At this point, any modification should be done: 55 56- Synchronously before application event loop is started using app.exec(). 57- In signals handlers reacting to `qan::Graph` or `qan::GraphView` events such as `nodeClicked()` or `groupClicked()` and so on (See [qan::Graph](https://github.com/cneben/QuickQanava/blob/master/src/qanGraph.h) and [qan::GraphView](https://github.com/cneben/QuickQanava/blob/master/src/qanGraphView.h) signals). 58- Using behavior observers (See [Behaviors section](advanced.md#observation-of-topological-modifications)). 59 60Please refer to the [cpp sample](https://github.com/cneben/QuickQanava/tree/master/samples/cpp) and more specifically [cpp_sample.cpp](https://github.com/cneben/QuickQanava/blob/master/samples/cpp/cpp_sample.cpp) for a sample about using `qan::Graph` topology related methods. 61 62Defining Custom Topology 63------------------ 64 65QuickQanava topology is described using GTpo library. Topology is modelled using non visual objects modelling node ([qan::Node](https://github.com/cneben/QuickQanava/blob/master/src/qanNode.h) connected by directed edges ([qan::Edge](https://github.com/cneben/QuickQanava/blob/master/src/qanEdge.h) eventually grouped in [qan::Group](https://github.com/cneben/QuickQanava/blob/master/src/qanGroup.h). These non-visual objects (called primitives) are mapped to QML QQuickItem based visual items. Concrete QQuickItem are generated on demand using QML QQmlComponent objects. 66 67QuickQanava provide default delegates for all primitives, but custom delegate could be specify by providing an argument for all primitive creation functions: `qan::Graph::insertNode()`, `qan::Graph::insertEdge()`, `qan::Graph::insertGroup()` and their QML counterpart in `Qan.Graph`. Simple custom delegate mapping is described in [custom nodes](nodes.md#defining-custom-nodes) and [custom groups](nodes.md#custom-groups) sections. 68 69Primitives classes could be subclassed from c++ to provide specific customization. Mapping between non-visual topology primitive and their QtQuick counterpart is managed trought static singleton factories defined in `qan::Node`, `qan::Edge` and `qan::Group`. Theses factories are automatically called from `qan::Graph` when a primitive creation request happen: a visual item with a specific style is then automatically created. 70 71Creation of a custom graph (MyGraph) with custom node (MyNode) and a dedicated visual item (MyNodeItem) could be achieved with the following architecture: 72 73![Graph Class Diagram](advanced/class-custom-nodes.png) 74 75Factories have to be redefined in primitive subclasses: 76 77 - `#!cpp static QQmlComponent* delegate(QQmlEngine& engine) noexcept`: Return a (usually singleton) QML component that will be used for primitive visual delegate. 78 - `#!cpp static qan::NodeStyle* style() noexcept`: Return a (usually singleton) style used as primitive default style. 79 80Custom content is then created from a specialized `qan::Graph` class: 81 82``` cpp hl_lines="4" 83// class MyGraph : public qan::Graph ... 84 85qan::Node* MyGraph::insertMyNode() noexcept { 86 return insertNode<MyNode>() 87} 88``` 89 90``` cpp 91// In a custom MyNode.cpp defining MyNode (inheriting from qan::Node) 92QQmlComponent* MyNode::delegate(QQmlEngine& engine) noexcept 93{ 94static std::unique_ptr<QQmlComponent> MyNode_delegate; 95 if ( !MyNode_delegate ) 96 CustomRectNode_delegate = std::make_unique<QQmlComponent>(&engine, "qrc:/MyNode.qml"); 97 return CustomRectNode_delegate.get(); 98} 99 100qan::NodeStyle* MyNode::style() noexcept 101{ 102 static std::unique_ptr<qan::NodeStyle> MyNode_style; 103 if ( !MyNode_style ) { 104 MyNode_style = std::make_unique<qan::NodeStyle>(); 105 MyNode_style->setBackColor(QColor("#ff29fc")); // Initialize primitive default style here 106 } 107 return MyNode_style.get(); 108} 109``` 110 111!!! note "Selection, visual connection and navigation will works out of the box for custom primitives (either nodes, edges or groups)." 112 113Insertion of non Visual Content 114------------------ 115 116Non visual edge or node could be used in graph to model complex topologies or add internal non-visual logic with: 117 118- `#!cpp qan::Graph::insertNonVisualNode<>()`: default graph `nodeDelegate` will no be used, custom node `delegate()` may be oeither undefined or return nullptr. 119- `#!cpp qan::Graph::insertNonVisualEdge<>(source, destination)`: Edge could be a regular node -> node edge or an oriented hyper edge node -> edge. 120 121 122Observation of Topological Modifications 123------------------ 124 125QuickQanava provide a full observation interface with the qan::Behaviour concept to react when underlying graph topology is modified. All primitives (nodes, edges or groups) could define custom behaviours to observe and react to topological changes. 126 127- `qan::NodeBehaviour`: 128 129A behaviour could then be registered using: `registerBehaviour()` method in `qan::Node`. 130 131``` cpp hl_lines="12 13" 132#include <QuickQanava> 133 134class CustomBehaviour : public qan::NodeBehaviour 135{ 136 Q_OBJECT 137public: 138 explicit NodeBehaviour( QObject* parent = nullptr ) : 139 qan::NodeBehaviour{ "Custom Behaviour", parent } { } 140 virtual ~NodeBehaviour() { /* Nil */ } 141 NodeBehaviour( const NodeBehaviour& ) = delete; 142protected: 143 virtual void inNodeInserted( qan::Node& inNode, qan::Edge& edge ) noexcept override; 144 virtual void inNodeRemoved( qan::Node& inNode, qan::Edge& edge ) noexcept override; 145}; 146``` 147 148Such a custom node behaviour could be installed with the following code: 149 150``` cpp hl_lines="4" 151{ 152 qan::Graph graph; 153 auto node = graph.insertNode(); 154 node->attachBehaviour( std::make_unique<CustomBehaviour>() ); 155 // node will now react when an in node is inserted or removed 156 auto source = graph.insertNode(); 157 auto edge = graph.insertEdge(source, node); // CustomBehaviour::Inserted() called 158 graph.removeEdge(edge); // CustomBehaviour::inNodeRemoved() called 159} 160``` 161 162Methods `inNodeInserted()` and `inNodeRemoved()` are called automatically when an in node is inserted or removed on behaviour target node. 163 164Reference documentation: 165 166 - [qan::NodeBehaviour](https://github.com/cneben/QuickQanava/blob/master/src/qanBehaviour.h) 167 - [qan::Node::installBehaviour()](https://github.com/cneben/QuickQanava/blob/425f1de0c75e1be85f51b90de517d75612978485/src/qanNode.h#L139) 168 169