当前位置: 首页 > ops >正文

Qt节点编辑器设计与实现:动态编辑与任务流可视化(一)

文章目录

    • 一、项目概述
    • 二、整体架构:模型-视图分离的设计哲学
      • 1. 模型层:数据与业务逻辑的核心
      • 2. 视图层:图形渲染与用户交互
      • 3. 交互层:连接模型与视图的桥梁
    • 三、核心模块解析
      • 1. 样式管理系统:视觉表现的基石
      • 2. 图形数据模型:数据与关系的核心载体
      • 3. 节点交互与几何计算
      • 4. 跨平台与模块化设计
    • 四、实战示例:任务流配置与加载
    • 五、总结

在这里插入图片描述
在这里插入图片描述
Qt节点编辑器设计与实现:动态编辑与任务流可视化(一)
深入解析Qt节点编辑器框架:交互逻辑与样式系统(二)
深入解析Qt节点编辑器框架:数据流转与扩展机制(三)
深入解析Qt节点编辑器框架:高级特性与性能优化(四)

  • 本项目是由Qt开源项目NodeEditor二次开发而来,项目地址,这是个非常不错的项目,熟悉并了解它对自身与Qt画板框架的使用会有很大提升。

一、项目概述

该节点编辑器旨在提供简单易用的任务流可视化编辑功能,支持节点自由布局、动态状态设置与流程连接管理。从代码结构来看,系统采用模块化设计,主要包含样式管理、图形数据模型、节点交互逻辑等核心模块,基于Qt框架实现跨平台兼容,可广泛应用于工作流设计、数据处理流程编排等场景。
包含核心功能如下:

  1. 节点编辑:支持节点的添加、删除、移动、调整大小、动态端口、动态变量等操作。
  2. 连接编辑:支持连接的创建、删除、标签编辑等操作。。
  3. 画板操作:支持撤销、重做、复制、粘贴、旋转、缩放、筛选节点等操作。
  4. 动态样式:支持节点、连接线与画板的动态样式设置,如颜色、字体、边框等。
  5. 菜单栏操作:支持文件操作新建、打开、保存、导出图片等。
  6. 保存与加载:支持将当前任务流保存为文件,后续可加载编辑。
  7. 任务流加载:支持通过通信动态加载节点与连接线,从而达到任务流显示效果。

二、整体架构:模型-视图分离的设计哲学

该框架严格遵循模型-视图(Model-View) 设计模式,将数据逻辑与图形展示解耦,这是处理复杂交互场景的关键。

1. 模型层:数据与业务逻辑的核心

模型层由AbstractGraphModel(抽象基类)和DataFlowGraphModel(具体实现)构成,负责管理节点、连接的核心数据与业务规则:

  • 节点数据:包括节点类型、端口信息、位置尺寸、内部状态等。
  • 连接数据:以ConnectionId(包含源节点、源端口、目标节点、目标端口)唯一标识一条连接,维护连接的有效性规则。
  • 业务逻辑:如连接是否允许创建(类型匹配、无循环)、端口动态增删时的连接调整等。

代码中,DataFlowGraphModel通过_models(存储节点实例)和_connectivity(存储连接ID集合)管理核心数据,对外提供addNodeaddConnection等接口,确保数据操作的一致性。

2. 视图层:图形渲染与用户交互

视图层由BasicGraphicsSceneDataFlowGraphicsScene等场景类,以及NodeGraphicsObjectConnectionGraphicsObject等图形对象类构成:

  • 场景类:继承自QGraphicsScene,负责管理所有图形对象的生命周期,处理全局事件(如右键菜单、保存加载)。
  • 图形对象类:继承自QGraphicsItem,负责单个节点/连接的渲染(如节点样式、连接线条)和局部交互(如拖拽、点击)。

例如,BasicGraphicsScenetraverseGraphAndPopulateGraphicsObjects方法会遍历模型中的节点和连接,创建对应的图形对象,实现模型到视图的初始同步。

3. 交互层:连接模型与视图的桥梁

交互层通过信号槽机制实现模型与视图的实时同步:

  • 模型变化时(如nodeCreatedconnectionDeleted),发送信号通知视图更新图形对象。
  • 视图接收用户操作(如拖拽节点、创建连接),通过命令模式(QUndoStack)修改模型,确保操作可撤销。

代码中,BasicGraphicsScene在构造函数中连接了模型的多个信号(如connectionCreatedonConnectionCreated),保证视图能及时响应模型变化。

三、核心模块解析

1. 样式管理系统:视觉表现的基石

节点编辑器的视觉体验直接影响用户交互效率,该系统通过NodeStyleConnectionStyleStyleCollection构建了灵活的样式管理机制。

  • 多状态样式支持NodeStyle类通过_styleData字典存储不同状态(如正常、选中、错误)的样式配置,包括边界颜色、渐变颜色、阴影效果等。例如,在加载JSON配置时,既支持兼容旧版本的默认样式,也支持多状态样式定义:

    // 多状态样式加载逻辑
    for (auto it = nodeStyleObj.begin(); it != nodeStyleObj.end(); ++it) {QString state = it.key();QJsonObject stateObj = it.value().toObject();NodeStyleData data;// 解析颜色、尺寸等属性..._styleData[state] = data;
    }
    
  • JSON序列化与反序列化:样式配置支持从JSON文件/文本加载,也可导出为JSON,便于配置共享与保存。颜色解析通过parseColor辅助函数实现,支持RGB数组与十六进制字符串两种格式,提升配置灵活性。

  • 全局样式管理StyleCollection作为单例模式,统一管理节点、连接与视图的样式,提供setNodeStyle等方法实现全局样式切换,确保界面风格一致性。

2. 图形数据模型:数据与关系的核心载体

图形数据模型是节点编辑器的"大脑",负责管理节点、连接及其关系。系统通过AbstractGraphModelStandardModelDataFlowGraphModel构建了层次化的模型体系。

  • 节点与连接管理StandardModel实现了节点的增删、连接的创建与删除等基础功能。通过_models存储节点实例,_connectivity维护连接关系,支持allNodeIdsconnections等方法查询数据:

    // 节点添加逻辑
    NodeId StandardModel::addNode(QString const nodeType) {std::unique_ptr<NodeDelegateModel> model = _registry->create(nodeType);if (model) {NodeId newId = newNodeId();_models[newId] = std::move(model);Q_EMIT nodeCreated(newId);return newId;}return InvalidNodeId;
    }
    
  • 动态端口处理:节点端口的动态增删是高级功能,AbstractGraphModel通过portsAboutToBeDeletedportsInserted等方法,在端口变化时自动调整关联连接,避免连接失效:

    // 端口删除前处理关联连接
    void AbstractGraphModel::portsAboutToBeDeleted(...) {for (PortIndex portIndex = first; portIndex <= clampedLast; ++portIndex) {std::unordered_set<ConnectionId> conns = connections(nodeId, portType, portIndex);for (auto connectionId : conns) {deleteConnection(connectionId);}}
    }
    
  • 连接合法性检查DataFlowGraphModel在创建连接时,通过connectionPossible方法检查数据类型匹配、端口是否可用及是否存在循环依赖,确保流程逻辑正确:

    // 无环检查(深度优先遍历)
    auto hasLoops = [this, &connectionId]() -> bool {std::stack<NodeId> filo;filo.push(connectionId.inNodeId);while (!filo.empty()) {auto id = filo.top();filo.pop();if (id == connectionId.outNodeId) return true; // 发现循环// 递归检查下游节点}return false;
    };
    

3. 节点交互与几何计算

节点的交互体验依赖于精准的几何计算,AbstractNodeGeometry类封装了端口位置计算、碰撞检测等核心逻辑:

  • 端口位置计算portScenePosition方法结合节点变换矩阵,计算端口在场景中的绝对位置,为连接绘制提供坐标基础。

  • 端口碰撞检测checkPortHit通过计算鼠标位置与端口的距离,判断是否点击端口,支持用户发起连接操作:

    PortIndex AbstractNodeGeometry::checkPortHit(...) const {double const tolerance = 2.0 * nodeStyle.getNodeStyleData(nodeState).ConnectionPointDiameter;for (unsigned int portIndex = 0; portIndex < n; ++portIndex) {auto pp = portPosition(nodeId, portType, portIndex);QPointF p = pp - nodePoint;auto distance = std::sqrt(QPointF::dotProduct(p, p));if (distance < tolerance) return portIndex;}return InvalidPortIndex;
    }
    

4. 跨平台与模块化设计

系统通过Compiler.hppExport.hpp实现跨平台兼容,支持MinGW、Clang、MSVC等主流编译器,并通过宏定义统一动态库导出/导入逻辑:

// 跨平台导出宏定义
#ifdef NODE_EDITOR_PLATFORM_WINDOWS
#define NODE_EDITOR_EXPORT __declspec(dllexport)
#define NODE_EDITOR_IMPORT __declspec(dllimport)
#elif NODE_EDITOR_COMPILER_GNU_VERSION_MAJOR >=4 || defined(NODE_EDITOR_COMPILER_CLANG)
#define NODE_EDITOR_EXPORT __attribute__((visibility("default")))
#endif

节点数据处理采用模块化设计,MathOperationDataModelDecimalData展示了如何实现具体节点逻辑(如数值计算),通过setInData接收输入、compute处理数据、outData输出结果,便于扩展新节点类型。

四、实战示例:任务流配置与加载

MainWidget中的test方法展示了如何通过JSON配置初始化任务流,这是我随意模拟的数据,实际上的数据应该是通信数据解析而来。示例JSON包含节点(如"星期一"、“上班”)与连接关系,加载后通过定时器动态更新数据,模拟任务流的运行状态:

// 从JSON字符串加载任务流
QJsonDocument doc = QJsonDocument::fromJson(jsonString.toUtf8());
m_jsonObj = doc.object();
// 定时器更新数据
m_testTimer->start(1000);

五、总结

  • 本身也就是为了学习更加深入的Qt画板框架,所以项目开源
  • 节点编辑器框架的设计与实现,从基础到高级,覆盖了模型-视图分离、交互逻辑、数据流转、扩展机制等多个方面。
  • 框架的核心技术点包括模型-视图分离、命令模式、信号槽机制、类型安全的传递机制、依赖驱动的更新逻辑、惰性计算、抽象接口与工厂模式、序列化等。
  • 框架的优势在于其高度的可定制性、可扩展性、可维护性。
  • 框架的应用场景包括可视化编程、数据处理流水线、工业控制流程图等。
  • 未来可进一步优化的方向包括分布式节点计算(跨进程/网络)、节点性能分析工具、AI辅助节点推荐等。
http://www.xdnf.cn/news/18953.html

相关文章:

  • 【拍摄学习记录】07-影调、直方图量化、向右向左
  • 经典扫雷游戏实现:从零构建HTML5扫雷游戏
  • 【Python】Python 实现 PNG 转 ICO 图标转换工具
  • LightGBM 在金融逾期天数预测任务中的经验总结
  • Qt自定义聊天消息控件ChatMessage:初步实现仿微信聊天界面
  • Linux之Shell编程(一)
  • Linux笔记12——shell编程基础-6
  • Swift 解法详解 LeetCode 365:水壶问题
  • Java -- 文件基础知识--Java IO流原理--FileReader
  • 了解ADS中信号和电源完整性的S参数因果关系
  • hintcon2025 Verilog OJ
  • 【python】python进阶——生成器
  • 数据结构01:顺序表
  • 次元小镇官网入口 - 二次元动漫社区|COS绘画插画壁纸分享
  • [数据结构] ArrayList与顺序表(下)
  • STM32——PWR
  • 机器视觉学习-day06-图像旋转
  • KafKa学习笔记
  • 【Day 35】Linux-Mysql错误总结
  • DA14531(Cortex-M0+)之Wake-up Interrupt Controller (WIC)
  • React学习教程,从入门到精通, ReactJS - 安装:初学者指南(3)
  • linux 网络:并发服务器及IO多路复用
  • 如何将yolo训练图像数据库的某个分类的图像取出来
  • element-plus的el-scrollbar显示横向滚动条
  • 使用华为 USG6000防火墙配置安全策略
  • 传输层协议介绍
  • 企业通讯软件以安全为基,搭建高效的通讯办公平台
  • Python篇---返回类型
  • 【论文阅读】PEPNet
  • amis上传组件导入文件接口参数为base64格式的使用示例