【文档】Feature Overview — QtNodes 3.0 documentation
Qt NodeEditor 是基于 Qt 框架开发的节点编辑器解决方案,非常适合需要嵌入到现有 Qt 应用程序中的图形化编程需求。
功能概述
1. 图形对象ID
节点(Nodes
)和连接(Connections
)没有独立的数据实例,所有相关数据均存储在用户自定义的 GraphModel
类中(需继承 AbstractGraphModel
)。
-
节点ID:
// Definitions.hpp using NodeId = unsigned int;
-
每个节点关联唯一的
NodeId
,通过NodeId AbstractGraphModel::addNode(QString)
生成。
注意:GraphModel
需自行保证NodeId
的唯一性。 -
连接ID:
struct ConnectionId { NodeId outNodeId; // 输出节点ID PortIndex outPortIndex; // 输出端口索引 NodeId inNodeId; // 输入节点ID PortIndex inPortIndex; // 输入端口索引 };
2. 序列化
当前仅 DataFlowGraphModel
支持序列化。若自定义 AbstractGraphModel
子类,需自行实现序列化逻辑(可参考 src/DataFlowGraphModel.cpp
)。
-
保存场景:
-
遍历所有节点,将数据存入 JSON 的
"nodes"
数组。 -
遍历所有连接,存入
"connections"
数组。
-
-
节点JSON示例:
{ "id": 0, "internal-data": { "model-name": "Subtraction" }, "position": { "x": -383, "y": -95 } }
-
internal-data
由DataFlowGraphModel::saveNode(NodeId)
填充,存储节点内部状态(如模型名称)。 -
连接JSON示例:
{ "inPortIndex": 0, "intNodeId": 1, "outNodeId": 0, "outPortIndex": 0 }
-
由
DataFlowGraphModel::saveConnection
生成。
代码示例:
参考 src/DataFlowGraphModel.cpp
中的 DataFlowGraphModel::save()
。
3. 撤销/重做(Undo/Redo)
通过 Qt 的 QUndoStack
实现,默认命令(如 DeleteCommand
)已实现在 src/QUndoCommands.cpp
-
关键点:
-
删除操作会调用
AbstractGraphModel::saveNode()
和AbstractGraphModel::saveConnection()
保存状态。 -
自定义模型需重写这两个函数。
-
4. 自定义图结构封装
若需可视化自定义图结构(非数据流语义),需继承 AbstractGraphModel
并实现以下功能:
-
核心机制:类似 Qt 的
QAbstractItemModel
,通过NodeRole
枚举定义节点信息类型:
NodeRole | 描述 | 数据类型 |
---|---|---|
Type | 节点类型(如模型名称) | QString |
Position | 节点场景坐标 | QPointF |
Size | 节点内部区域大小 | QSize |
CaptionVisible | 是否显示标题 | bool |
Caption | 节点标题文本 | QString |
Style | 节点样式(颜色、渐变等) | QVariantMap |
InternalData | 节点内部状态(序列化为JSON) | QVariantMap |
InPortCount | 输入端口数量 | unsigned int |
OutPortCount | 输出端口数量 | unsigned int |
Widget | 嵌入节点的控件(无则为 nullptr ) | QWidget* |
代码示例:
参考 examples/simple_graph_model
。
5. 节点与场景样式
默认样式通过 StyleCollection
管理,从 JSON 字符串解析:
-
视图样式 (
GraphicsViewStyle
):{ "BackgroundColor": [53, 53, 53], "FineGridColor": [60, 60, 60], "CoarseGridColor": [25, 25, 25] }
-
节点样式 (
NodeStyle
):{ "NormalBoundaryColor": [255, 255, 255], "SelectedBoundaryColor": [255, 165, 0], "GradientColor0": "gray", "ConnectionPointDiameter": 8.0 }
-
连接样式 (
ConnectionStyle
):{ "NormalColor": "darkcyan", "SelectedColor": [100, 100, 100], "LineWidth": 3.0 }
代码示例:
参考 examples/styles
和 examples/connection_colors
。
6. 垂直布局(实验性)
节点端口沿垂直方向排列,布局如下:
-------o-------------o------- | 端口标题A 端口标题B | | | | 节点标题 | | | | 端口标题C | --------------o-------------
代码示例:
参考 examples/vertical_layout
。
7. 动态端口
通过 AbstractGraphModel
的以下函数管理:
-
portsAboutToBeInserted
/portsInserted
-
portsAboutToBeDeleted
/portsDeleted
操作流程:
-
调用
portsAboutToBeInserted
预计算新端口索引。 -
修改底层数据。
-
调用
portsInserted
应用变更。
代码示例:
参考 examples/dynamic_ports
。
8. 锁定节点与连接
-
锁定节点:
NodeFlags YourGraphModel::nodeFlags(NodeId nodeId) const { auto flags = DataFlowGraphModel::nodeFlags(nodeId); if (_nodesLocked) flags |= NodeFlag::Locked; // 禁用移动和选择 return flags; }
-
禁用连接分离:
重写AbstractGraphModel::detachPossible(ConnectionId)
返回false
。
代码示例:
参考 examples/lock_nodes_and_connections
。
9. 数据传播
数据流通过 NodeDelegateModel
触发:
-
源节点调用
dataUpdated(PortIndex)
发射信号。 -
DataFlowGraphModel::onOutPortDataUpdated
读取数据并传递给下游节点。 -
目标节点通过
NodeDelegateModel::setInData()
接收数据。
信号链:
NodeDelegateModel::dataUpdated → DataFlowGraphModel::onOutPortDataUpdated
→ setPortData → 目标节点setInData
10. 无界面模式(Headless Mode)
-
AbstractGraphModel
可独立于场景使用,直接操作图数据。 -
BasicGraphicsScene
可不绑定GraphicsView
运行。
代码示例:
参考 examples/calculator/headless_main.cpp
,直接加载计算图并执行运算。