Node
类是 QtNodes 框架中的核心类之一,代表流程图中的一个节点。它负责管理节点的数据模型、图形表示、状态和连接关系。
主要功能
-
节点管理:创建、保存和恢复节点
-
数据传播:处理节点间的数据流动
-
图形表示:管理节点的可视化部分
-
连接处理:管理与该节点相关的连接
#pragma once
#include "ConnectionGraphicsObject.hpp"
#include "Export.hpp"
#include "NodeData.hpp"
#include "NodeGeometry.hpp"
#include "NodeGraphicsObject.hpp"
#include "NodeState.hpp"
#include "PortType.hpp"
#include "Serializable.hpp"
#include "memory.hpp"
#include <QtCore/QJsonObject>
#include <QtCore/QObject>
#include <QtCore/QUuid>
namespace QtNodes
{
class Connection;
class ConnectionState;
class NodeGraphicsObject;
class NodeDataModel;
class NODE_EDITOR_PUBLIC Node
: public QObject
, public Serializable
{
Q_OBJECT
public:
/// NodeDataModel should be an rvalue and is moved into the Node
Node(std::unique_ptr<NodeDataModel> &&dataModel);
virtual ~Node();
public:
QJsonObject save() const override;
void restore(QJsonObject const &json) override;
public:
QUuid id() const;
void reactToPossibleConnection(PortType, std::shared_ptr<NodeDataType>, QPointF const &scenePoint);
void resetReactionToConnection();
public:
NodeGraphicsObject const &nodeGraphicsObject() const;
NodeGraphicsObject &nodeGraphicsObject();
void setGraphicsObject(std::unique_ptr<NodeGraphicsObject> &&graphics);
NodeGeometry &nodeGeometry();
NodeGeometry const &nodeGeometry() const;
NodeState const &nodeState() const;
NodeState &nodeState();
NodeDataModel *nodeDataModel() const;
public Q_SLOTS: // data propagation
/// Propagates incoming data to the underlying model.
void propagateData(PortIndex inPortIndex) const;
/// Fetches data from model's OUT #index port
/// and propagates it to the connection
void onDataUpdated(PortIndex index);
/// update the graphic part if the size of the embeddedwidget changes
void onNodeSizeUpdated();
private:
// addressing
QUuid _uid;
// data
std::unique_ptr<NodeDataModel> _nodeDataModel;
NodeState _nodeState;
// painting
NodeGeometry _nodeGeometry;
std::unique_ptr<NodeGraphicsObject> _nodeGraphicsObject;
};
} // namespace QtNodes
#include "Node.hpp"
#include "ConnectionGraphicsObject.hpp"
#include "ConnectionState.hpp"
#include "FlowScene.hpp"
#include "NodeDataModel.hpp"
#include "NodeGraphicsObject.hpp"
#include <QtCore/QObject>
#include <iostream>
#include <utility>
using QtNodes::Node;
using QtNodes::NodeData;
using QtNodes::NodeDataModel;
using QtNodes::NodeDataType;
using QtNodes::NodeGeometry;
using QtNodes::NodeGraphicsObject;
using QtNodes::NodeState;
using QtNodes::PortIndex;
using QtNodes::PortType;
Node::Node(std::unique_ptr<NodeDataModel> &&dataModel)
: _uid(QUuid::createUuid()), _nodeDataModel(std::move(dataModel)), _nodeState(_nodeDataModel), _nodeGeometry(_nodeDataModel),
_nodeGraphicsObject(nullptr)
{
_nodeGeometry.recalculateSize();
// propagate data: model => node
connect(_nodeDataModel.get(), &NodeDataModel::dataUpdated, this, &Node::onDataUpdated);
connect(_nodeDataModel.get(), &NodeDataModel::embeddedWidgetSizeUpdated, this, &Node::onNodeSizeUpdated);
}
Node::~Node() = default;
QJsonObject Node::save() const
{
QJsonObject nodeJson;
nodeJson["id"] = _uid.toString();
nodeJson["model"] = _nodeDataModel->save();
QJsonObject obj;
obj["x"] = _nodeGraphicsObject->pos().x();
obj["y"] = _nodeGraphicsObject->pos().y();
nodeJson["position"] = obj;
return nodeJson;
}
void Node::restore(QJsonObject const &json)
{
_uid = QUuid(json["id"].toString());
QJsonObject positionJson = json["position"].toObject();
QPointF point(positionJson["x"].toDouble(), positionJson["y"].toDouble());
_nodeGraphicsObject->setPos(point);
_nodeDataModel->restore(json["model"].toObject());
}
QUuid Node::id() const
{
return _uid;
}
void Node::reactToPossibleConnection(PortType reactingPortType, std::shared_ptr<NodeDataType> reactingDataType, QPointF const &scenePoint)
{
QTransform const t = _nodeGraphicsObject->sceneTransform();
QPointF nodePoint = t.inverted().map(scenePoint);
_nodeGeometry.setDraggingPosition(nodePoint);
_nodeGraphicsObject->update();
_nodeState.setReaction(NodeState::REACTING, reactingPortType, reactingDataType);
}
void Node::resetReactionToConnection()
{
_nodeState.setReaction(NodeState::NOT_REACTING);
_nodeGraphicsObject->update();
}
NodeGraphicsObject const &Node::nodeGraphicsObject() const
{
return *_nodeGraphicsObject.get();
}
NodeGraphicsObject &Node::nodeGraphicsObject()
{
return *_nodeGraphicsObject.get();
}
void Node::setGraphicsObject(std::unique_ptr<NodeGraphicsObject> &&graphics)
{
_nodeGraphicsObject = std::move(graphics);
_nodeGeometry.recalculateSize();
}
NodeGeometry &Node::nodeGeometry()
{
return _nodeGeometry;
}
NodeGeometry const &Node::nodeGeometry() const
{
return _nodeGeometry;
}
NodeState const &Node::nodeState() const
{
return _nodeState;
}
NodeState &Node::nodeState()
{
return _nodeState;
}
NodeDataModel *Node::nodeDataModel() const
{
return _nodeDataModel.get();
}
void Node::propagateData(PortIndex inPortIndex) const
{
NodeState const &state = nodeState();
NodeState::ConnectionPtrSet connections = state.connections(PortType::In, inPortIndex);
std::vector<std::shared_ptr<NodeData>> nodeData;
nodeData.reserve(connections.size());
for (const auto &connection : connections)
{
Connection *c = connection.second;
Node *outNode = c->getNode(PortType::Out);
PortIndex outNodeIndex = c->getPortIndex(PortType::Out);
std::shared_ptr<NodeData> outData = outNode->nodeDataModel()->outData(outNodeIndex);
TypeConverter converter = c->getTypeConverter();
if (converter != nullptr)
{
outData = converter(outData);
}
nodeData.push_back(outData);
}
_nodeDataModel->setInData(std::move(nodeData), inPortIndex);
// 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
_nodeGraphicsObject->setGeometryChanged();
_nodeGeometry.recalculateSize();
_nodeGraphicsObject->update();
_nodeGraphicsObject->moveConnections();
}
void Node::onDataUpdated(PortIndex index)
{
auto connections = _nodeState.connections(PortType::Out, index);
for (auto const &c : connections) c.second->propagateData();
}
void Node::onNodeSizeUpdated()
{
if (nodeDataModel()->embeddedWidget())
{
nodeDataModel()->embeddedWidget()->adjustSize();
}
nodeGeometry().recalculateSize();
for (PortType type : { PortType::In, PortType::Out })
{
for (auto &conn_set : nodeState().getEntries(type))
{
for (auto &pair : conn_set)
{
Connection *conn = pair.second;
conn->getConnectionGraphicsObject().move();
}
}
}
}
类成员详解
构造函数
Node(std::unique_ptr<NodeDataModel> &&dataModel)
-
使用给定的
NodeDataModel
创建一个新节点 -
自动生成唯一 ID (QUuid)
-
初始化节点状态、几何信息和图形对象
-
设置数据模型信号与节点槽的连接
序列化方法
QJsonObject save() const override void restore(QJsonObject const &json) override
-
save()
: 将节点状态保存为 JSON 对象,包括 ID、模型数据和位置 -
restore()
: 从 JSON 对象恢复节点状态
节点标识
QUuid id() const
-
返回节点的唯一标识符
连接反应处理
void reactToPossibleConnection(PortType, std::shared_ptr<NodeDataType>, QPointF const &scenePoint) void resetReactionToConnection()
-
处理可能的连接操作时的视觉反馈
-
重置连接反应状态
访问器方法
// 图形对象访问
NodeGraphicsObject const &nodeGraphicsObject() const
NodeGraphicsObject &nodeGraphicsObject()
void setGraphicsObject(std::unique_ptr<NodeGraphicsObject> &&graphics)
// 几何信息访问
NodeGeometry &nodeGeometry()
NodeGeometry const &nodeGeometry() const
// 状态访问
NodeState const &nodeState() const
NodeState &nodeState()
// 数据模型访问
NodeDataModel *nodeDataModel() const
数据传播槽
void propagateData(PortIndex inPortIndex) const void onDataUpdated(PortIndex index) void onNodeSizeUpdated()
-
propagateData()
: 将输入端口的数据传播到模型 -
onDataUpdated()
: 当模型数据更新时通知连接的连接 -
onNodeSizeUpdated()
: 当节点大小变化时更新几何信息和连接位置
内部实现细节
数据成员
-
_uid
: 节点的唯一标识符 -
_nodeDataModel
: 节点数据模型的唯一指针 -
_nodeState
: 节点状态对象,管理连接状态 -
_nodeGeometry
: 节点几何信息对象 -
_nodeGraphicsObject
: 节点图形对象的唯一指针
数据传播机制
propagateData()
方法实现了从输入连接获取数据并传递给模型的核心逻辑:
-
获取指定输入端口的连接集合
-
从每个连接的输出节点获取数据
-
应用类型转换器(如果有)
-
将数据集合传递给模型的
setInData()
方法 -
更新节点的视觉表示
使用场景
-
创建新节点:通过传入数据模型创建节点
-
保存/加载流程图:使用序列化方法保存和恢复节点状态
-
处理用户交互:通过图形对象处理鼠标事件
-
数据流处理:当连接建立或数据更新时自动传播数据
与其他类的关系
-
NodeDataModel: 包含节点的业务逻辑和数据
-
NodeGraphicsObject: 处理节点的可视化表示
-
NodeGeometry: 管理节点的几何信息
-
NodeState: 跟踪节点的连接状态
-
Connection: 表示节点间的连接关系
Node
类作为这些组件的协调者,将它们整合为一个完整的节点功能单元。