1#include <Dataflow/QtGui/GraphEditor/GraphModel.hpp>
3#include <Dataflow/QtGui/GraphEditor/WidgetFactory.hpp>
4#include <Gui/ParameterSetEditor/ParameterSetEditor.hpp>
10namespace GraphEditor {
11using namespace Ra::Dataflow::Core;
14 m_graph { graph }, m_next_node_id { 0 } {
19GraphModel::~GraphModel() {
23void GraphModel::fill_factory_map() {
24 auto factories = NodeFactoriesManager::factory_manager();
25 for (
const auto& [factoryName, factory] : factories ) {
26 for (
const auto& [model_name, creator] :
factory->factory_map() ) {
27 auto f = creator.first;
28 m_model_name_to_factory[model_name] = f;
38GraphModel::allConnectionIds( NodeId
const nodeId )
const {
44 [&nodeId]( ConnectionId
const& cid ) {
45 return cid.inNodeId == nodeId || cid.outNodeId == nodeId;
52GraphModel::connections( NodeId nodeId, PortType portType, PortIndex portIndex )
const {
58 [&portType, &portIndex, &nodeId]( ConnectionId
const& cid ) {
59 return ( getNodeId( portType, cid ) == nodeId &&
60 getPortIndex( portType, cid ) == portIndex );
66bool GraphModel::connectionExists( ConnectionId
const connectionId )
const {
67 return ( m_connectivity.find( connectionId ) != m_connectivity.end() );
70void GraphModel::addInputOutputNodesForGraph() {
71 m_graph->add_input_output_nodes();
75GraphModel::NodeId GraphModel::addNode( QString
const nodeType ) {
76 if ( nodeType.toStdString() == GraphInputNode::node_typename() ||
77 nodeType.toStdString() == GraphOutputNode::node_typename() ) {
78 m_graph->add_input_output_nodes();
81 m_node_id_to_ptr.end(),
82 [node_typename = nodeType.toStdString()](
const auto& n ) {
83 return n.second->model_name() == node_typename;
87 auto f = m_model_name_to_factory.at( nodeType.toStdString() );
89 m_graph->add_node( n );
91 NodeId newId = newNodeId();
92 m_node_ids.insert( newId );
93 m_node_id_to_ptr[newId] = n;
95 Q_EMIT nodeCreated( newId );
100bool GraphModel::connectionPossible( ConnectionId
const connectionId )
const {
101 auto in_node_id = connectionId.inNodeId;
102 auto out_node_id = connectionId.outNodeId;
104 auto in_port_id = connectionId.inPortIndex;
105 auto out_port_id = connectionId.outPortIndex;
107 bool ret = m_connectivity.find( connectionId ) == m_connectivity.end() &&
108 m_graph->can_link( m_node_id_to_ptr.at( out_node_id ).get(),
110 m_node_id_to_ptr.at( in_node_id ).get(),
100bool GraphModel::connectionPossible( ConnectionId
const connectionId )
const {
…}
116void GraphModel::addConnection( ConnectionId
const connectionId ) {
117 m_connectivity.insert( connectionId );
118 auto in_node_id = connectionId.inNodeId;
119 auto out_node_id = connectionId.outNodeId;
120 auto in_port_id = connectionId.inPortIndex;
121 auto out_port_id = connectionId.outPortIndex;
123 m_graph->add_link( m_node_id_to_ptr.at( out_node_id ),
125 m_node_id_to_ptr.at( in_node_id ),
128 Q_EMIT connectionCreated( connectionId );
129 Q_EMIT nodeUpdated( in_node_id );
130 Q_EMIT nodeUpdated( out_node_id );
133bool GraphModel::nodeExists( NodeId
const nodeId )
const {
134 return ( m_node_ids.find( nodeId ) != m_node_ids.end() );
138 QWidget* controlPanel =
new QWidget;
139 controlPanel->setStyleSheet(
"background-color:transparent;" );
140 QVBoxLayout* layout =
new QVBoxLayout( controlPanel );
142 auto node_inputs = node->input_variables();
143 if ( node_inputs.size() > 0 ) {
145 controlPanelInputs->setShowUnspecified(
true );
147 WidgetFactory ui_builder { node_inputs, controlPanelInputs, {} };
148 node_inputs.visit( ui_builder );
150 if ( controlPanelInputs->children().size() > 3 )
151 layout->addWidget( controlPanelInputs );
153 delete controlPanelInputs;
155 if ( node->parameters().size() > 0 ) {
157 controlPanelParams->setShowUnspecified(
true );
159 WidgetFactory ui_builder { node->parameters(), controlPanelParams, {} };
160 node_inputs.visit( ui_builder );
162 layout->addWidget( controlPanelParams );
166 auto b =
new QPushButton(
"Edit" );
168 connect( b, &QPushButton::clicked, [
this, node]() {
169 emit
const_cast<GraphModel*
>( this )->node_edited( node );
171 layout->addWidget( b );
176QVariant GraphModel::nodeData( NodeId nodeId, NodeRole role )
const {
179 auto node_ptr = m_node_id_to_ptr.at( nodeId );
183 result = QString::fromStdString( node_ptr->model_name() );
186 case NodeRole::Position:
187 result = m_node_geometry_data[nodeId].pos;
191 result = m_node_geometry_data[nodeId].size;
194 case NodeRole::CaptionVisible:
198 case NodeRole::Caption:
199 result = QString::fromStdString( node_ptr->display_name() );
202 case NodeRole::Style: {
203 auto style = StyleCollection::nodeStyle();
204 result = style.toJson().toVariantMap();
207 case NodeRole::InternalData:
210 case NodeRole::InPortCount: {
211 unsigned int count = ( node_ptr->inputs().size() );
212 if ( node_ptr == m_graph->output_node() ) ++
count;
216 case NodeRole::OutPortCount: {
217 unsigned int count = ( node_ptr->outputs().size() );
218 if ( node_ptr == m_graph->input_node() ) ++
count;
222 case NodeRole::Widget:
223 if (
auto node_itr = m_node_widget.find( nodeId ); node_itr == m_node_widget.end() ) {
224 m_node_widget[nodeId] = getWidget( node_ptr );
226 result = QVariant::fromValue( m_node_widget[nodeId] );
233bool GraphModel::setNodeData( NodeId nodeId, NodeRole role, QVariant value ) {
235 auto node_ptr = m_node_id_to_ptr.at( nodeId );
240 case NodeRole::Position: {
241 auto pos = value.value<QPointF>();
243 m_node_geometry_data[nodeId].pos = pos;
244 nlohmann::json json = { {
"position", { {
"x", pos.x() }, {
"y", pos.y() } } } };
245 node_ptr->add_metadata( json );
246 emit nodePositionUpdated( nodeId );
251 case NodeRole::Size: {
252 m_node_geometry_data[nodeId].size = value.value<QSize>();
256 case NodeRole::CaptionVisible:
259 case NodeRole::Caption:
262 case NodeRole::Style:
265 case NodeRole::InternalData:
268 case NodeRole::InPortCount:
271 case NodeRole::OutPortCount:
274 case NodeRole::Widget:
282GraphModel::portData( NodeId nodeId, PortType portType, PortIndex portIndex, PortRole role )
const {
284 auto n = m_node_id_to_ptr.at( nodeId );
285 if ( n == m_graph->input_node() || n == m_graph->output_node() ) {
291 case PortRole::DataType: {
293 if ( ( n == m_graph->input_node() && portIndex == n->inputs().size() ) ||
294 ( n == m_graph->output_node() && portIndex == n->inputs().size() ) )
295 s = QString(
"any" );
297 auto p = ( portType == PortType::In ) ? n->port_by_index(
"in", portIndex )
298 : n->port_by_index(
"out", portIndex );
300 s = QString::fromStdString( Ra::Core::Utils::simplifiedDemangledType( p->type() ) );
302 return QVariant::fromValue( QtNodes::NodeDataType { s, s } );
305 case PortRole::ConnectionPolicyRole:
306 if ( portType == PortType::In )
307 return QVariant::fromValue( ConnectionPolicy::One );
309 return QVariant::fromValue( ConnectionPolicy::Many );
312 case PortRole::CaptionVisible:
316 case PortRole::Caption: {
317 if ( n == m_graph->input_node() && portIndex == n->inputs().size() )
318 return QString(
"new" );
319 if ( n == m_graph->output_node() && portIndex == n->inputs().size() )
320 return QString(
"new" );
322 auto p = ( portType == PortType::In ) ? n->port_by_index(
"in", portIndex )
323 : n->port_by_index(
"out", portIndex );
324 return QString::fromStdString( p->name() );
335 case PortRole::DataType: {
336 auto p = ( portType == PortType::In ) ? n->port_by_index(
"in", portIndex )
337 : n->port_by_index(
"out", portIndex );
338 QString s = QString::fromStdString( Ra::Core::Utils::simplifiedDemangledType( p->type() ) );
339 return QVariant::fromValue( QtNodes::NodeDataType { s, s } );
342 case PortRole::ConnectionPolicyRole:
343 if ( portType == PortType::In )
344 return QVariant::fromValue( ConnectionPolicy::One );
346 return QVariant::fromValue( ConnectionPolicy::Many );
349 case PortRole::CaptionVisible:
353 case PortRole::Caption: {
354 auto p = ( portType == PortType::In ) ? n->port_by_index(
"in", portIndex )
355 : n->port_by_index(
"out", portIndex );
356 return QString::fromStdString( p->name() );
363bool GraphModel::setPortData( NodeId nodeId,
366 QVariant
const& value,
369 Q_UNUSED( portType );
370 Q_UNUSED( portIndex );
377bool GraphModel::deleteConnection( ConnectionId
const connectionId ) {
378 bool disconnected =
false;
380 auto it = m_connectivity.find( connectionId );
382 if ( it != m_connectivity.end() ) {
384 auto in_node_id = connectionId.inNodeId;
385 auto in_port_id = connectionId.inPortIndex;
387 m_graph->remove_link( m_node_id_to_ptr.at( in_node_id ), in_port_id );
388 m_connectivity.erase( it );
391 if ( disconnected ) emit connectionDeleted( connectionId );
396bool GraphModel::deleteNode( NodeId
const nodeId ) {
398 auto connectionIds = allConnectionIds( nodeId );
400 for (
auto& cId : connectionIds ) {
401 deleteConnection( cId );
404 m_graph->remove_node( m_node_id_to_ptr.at( nodeId ) );
405 m_node_id_to_ptr.erase( nodeId );
406 m_node_widget.erase( nodeId );
408 m_node_ids.erase( nodeId );
409 m_node_geometry_data.erase( nodeId );
411 emit nodeDeleted( nodeId );
416QJsonObject GraphModel::saveNode( NodeId
const nodeId )
const {
417 QJsonObject nodeJson;
419 auto node = m_node_id_to_ptr.at( nodeId );
423 node->toJson( json );
426 json[
"id"] =
static_cast<qint64
>( nodeId );
427 QPointF
const pos = nodeData( nodeId, NodeRole::Position ).value<QPointF>();
428 json[
"position"] = { {
"x", pos.x() }, {
"y", pos.y() } };
432 QJsonDocument jsonResponse = QJsonDocument::fromJson( json.dump().c_str() );
433 QJsonObject jsonObject = jsonResponse.object();
434 for (
auto it = jsonObject.constBegin(); it != jsonObject.constEnd(); it++ ) {
435 nodeJson.insert( it.key(), it.value() );
441void GraphModel::loadNode( QJsonObject
const& nodeJson ) {
444 auto json = nlohmann::json::parse( QJsonDocument( nodeJson ).toJson() );
445 auto f = m_model_name_to_factory[json[
"model"][
"name"]];
447 m_graph->add_node( n );
450 NodeId restoredNodeId =
static_cast<NodeId
>( nodeJson[
"id"].toInt() );
453 m_next_node_id =
std::max( m_next_node_id, restoredNodeId + 1 );
456 m_node_ids.insert( restoredNodeId );
459 m_node_ids.insert( restoredNodeId );
460 m_node_id_to_ptr[restoredNodeId] = n;
462 Q_EMIT nodeCreated( restoredNodeId );
465 QJsonObject posJson = nodeJson[
"position"].toObject();
466 QPointF
const pos( posJson[
"x"].toDouble(), posJson[
"y"].toDouble() );
468 setNodeData( restoredNodeId, NodeRole::Position, pos );
441void GraphModel::loadNode( QJsonObject
const& nodeJson ) {
…}
477void GraphModel::sync_data() {
479 m_node_id_to_ptr.clear();
480 m_connectivity.clear();
481 m_node_geometry_data.clear();
482 m_node_widget.clear();
486 for (
const auto& n : m_graph->nodes() ) {
487 NodeId newId = newNodeId();
488 m_node_ids.insert( newId );
489 m_node_id_to_ptr[newId] = n;
490 if (
auto position = n->metadata().find(
"position" ); position != n->metadata().end() ) {
491 m_node_geometry_data[newId].pos.setX( position->at(
"x" ) );
492 m_node_geometry_data[newId].pos.setY( position->at(
"y" ) );
497 for (
const auto& in_node : m_graph->nodes() ) {
500 m_node_id_to_ptr.begin(), m_node_id_to_ptr.end(), [in_node](
const auto& pair ) {
501 return pair.second.get() == in_node.get();
503 if ( in_node_itr == m_node_id_to_ptr.end() ) {
504 LOG( Ra::Core::Utils::logERROR ) <<
"error graph structure in_node";
509 if ( in_node_itr->second == m_graph->input_node() ) {
continue; }
511 const auto& in_node_id = in_node_itr->first;
513 for (
size_t in_port_id = 0; in_port_id < in_node->inputs().size(); ++in_port_id ) {
514 const auto& in_port = in_node->inputs()[in_port_id];
515 const auto& out_port = in_port->link();
519 auto out_node = out_port->node();
523 const auto graph_out_node =
dynamic_cast<const GraphOutputNode*
>( out_node );
524 if ( graph_out_node && graph_out_node->graph() != m_graph.get() ) {
525 out_node = graph_out_node->graph();
529 m_node_id_to_ptr.begin(),
530 m_node_id_to_ptr.end(),
531 [out_node](
const auto& pair ) { return pair.second.get() == out_node; } );
532 if ( out_node_itr == m_node_id_to_ptr.end() ) {
533 LOG( Ra::Core::Utils::logERROR )
534 <<
"error graph structure out_node, port " << out_port->name()
535 <<
" in node " << in_node->display_name() <<
" " << in_port->name();
539 const auto& out_node_id = out_node_itr->first;
543 find_if( out_node->outputs().begin(),
544 out_node->outputs().end(),
545 [out_port](
auto p ) { return p.get() == out_port; } );
546 if ( out_port_itr == out_node->outputs().end() ) {
547 LOG( Ra::Core::Utils::logERROR )
548 <<
"error graph structure, out node " << out_node->display_name()
549 <<
" out_port, in node " << in_node->display_name() <<
" "
553 const auto out_port_id =
557 ConnectionId connection_id;
559 connection_id.inNodeId = in_node_id;
560 connection_id.outNodeId = out_node_id;
561 connection_id.inPortIndex = in_port_id;
562 connection_id.outPortIndex = out_port_id;
564 m_connectivity.insert( connection_id );
572void GraphModel::clear_node_widget(
Core::Node* node ) {
574 std::find_if( m_node_id_to_ptr.begin(), m_node_id_to_ptr.end(), [node](
const auto& pair ) {
575 return pair.second.get() == node;
577 if ( node_itr == m_node_id_to_ptr.end() ) {
578 LOG( Ra::Core::Utils::logERROR ) <<
"error try to clear widget of an unmanaged node";
581 const auto& node_id = node_itr->first;
582 m_node_widget.erase( node_id );
Represent a set of connected nodes that define a Direct Acyclic Computational Graph Ownership of node...
Base abstract class for all the nodes added and used by the node system.
Simple Widget for RenderParameter editing The editor will expose a control panel containing all of th...
auto factory(const NodeFactorySet::key_type &name) -> NodeFactorySet::mapped_type
Gets the given factory from the manager.
hepler function to manage enum as underlying types in VariableSet