工作流说明¶
一个普通的工作流如下所示

普通的工作流是一个有向图,出度和入度没有名字属性,只有数量属性(如上图节点4有2个入度),这种工作流非常适合处理事务,尤其适合进行一些网络业务的处理,许多大规模事件处理框架都是基于此类工作流
但针对一些相对复杂的情景,工作流中的节点要具体化,节点不仅仅代表一个事务,而是一个带有参数的函数,那么节点的入度和出度就需要有名字属性,如下图所示

而本程序的workflow就是这种出入度带有名字属性的工作流
工作流模块DAWorkFlow¶
工作流主要的类有3个,为DAWorkFlow、DAAbstractNodeFactory和DAAbstractNode,DAWorkFlow负责整个工作流图的组织,DAAbstractNode为每个工作流的节点,节点的生成通过DAAbstractNodeFactory
另外还有DAWorkFlowExecuter负责工作流的执行,这几个类的UML图如下图所示:

插件库提供了DAAbstractNodePlugin插件,负责管理工作流的节点
节点元数据DANodeMetaData¶
工作流的每个节点都有一个唯一的元数据,DAAbstractNodeFactory通过元数据生产出对应的节点
节点元数据描述了节点的固定信息,如节点名称、节点图标、节点描述等,最核心的是节点原型(Node Prototype),通过此信息区分每个不同的节点,因此,自定义节点需要提供一个不会和其他节点重复的原型(Node Prototype)信息
节点¶
节点DAAbstractNode包含了如下信息:
- 元数据
DANodeMetaData - 入度(入口)和出度(出口)信息(名称,数量)
- 入度出度对应的数据
- 入度节点和出度节点
- 一个唯一id
通过上述信息基本可描述一个有向图结构,而DAWorkFlow就是用于管理和生成这个有向图
生成节点¶
每个节点对应一个DAAbstractNode类,每个节点内部保留着它的MetaData,而节点的创建是通过DAAbstractNodeFactory来实现的,DAWorkFlow负责节点的管理
生成一个节点的方式可以通过DAWorkFlow生成也可以通过DANodeGraphicsScene生成:
DAWorkFlow::createNodeDANodeGraphicsScene::createNodeDANodeGraphicsScene::createNode_
DAWorkFlow只会生成节点,DANodeGraphicsScene可以把节点和和图元一起生成,同时DANodeGraphicsScene还提供了createNode和createNode_函数,在data-work-flow中,所有带xxx_函数一般是表示可支持redo/undo的函数
通过上面的函数,只要传入DANodeMetaData即可生成对应的节点
节点生成过程实际是调用了工作流中的节点工厂,其生成过程如下:

连接点¶
你可以把节点理解为一个函数,输入节点是函数的输入参数,输出节点是函数的返回参数
在工作流中,节点的链接点是一个名字,DAAbstractNode::getInputKeys用来获取输入的连接点,DAAbstractNode::getOutputKeys用来获取输出的连接点
连接点携带的参数统一通过QVariant携带,你可以通过DAAbstractNode::getInputData和DAAbstractNode::getOutputData获取输入或输出连接点的数据
连接点是可以动态添加和删除的,通过DAAbstractNode::addInputKey/DAAbstractNode::addOutputKey可以对连接点进行添加,通过DAAbstractNode::removeInputKey/DAAbstractNode::removeOutputKey可以对连接点进行删除
工作流渲染¶
工作流的渲染绘制通过DANodeGraphicsScene配合DAAbstractNodeGraphicsItem完成,DAWorkFlow的渲染依赖DAGraphicsView库,DAGraphicsView库提供了可缩放图元及redo/undo的集成
工作流渲染相关的关键类如下:
DANodeGraphicsScene:负责场景的管理DAAbstractNodeGraphicsItem:每个节点在场景的渲染DAAbstractNodeLinkGraphicsItem:节点之间连接线的渲染DANodeLinkPoint:节点连接点的渲染
场景和工作流的关系¶
工作流场景包含工作流,场景设置了工作流后,可执行工作流的操作,这时场景相当于工作流的wrapper,但场景具有对工作流的渲染功能,如果类比为MVC架构,工作流相当于是Model,场景这是Control和view层

DANodeGraphicsScene必须设置工作流后才能正常工作:DANodeGraphicsScene::setWorkFlow
节点的显示¶
节点DAAbstractNode类提供纯虚函数createGraphicsItem接口,用于生图元:
1 2 | |
通过此函数返回一个DAAbstractNodeGraphicsItem对象,DAAbstractNodeGraphicsItem对象负责节点在画布(DANodeGraphicsScene)上的显示
DAAbstractNodeGraphicsItem内部维护了DAAbstractNode的weak point,通过DAAbstractNodeGraphicsItem::node函数可以获取节点的指针实例,如果节点已经删除,将获取空的节点指针
节点渲染第一步是调用DAAbstractNode::createGraphicsItem生成DAAbstractNodeGraphicsItem,并把它添加到场景中
场景中的操作会反应到工作流和ui中,因此,在GUI层面,直接操作场景可实现工作流的读写也可以同步实现渲染
DANodeGraphicsScene提供了DANodeGraphicsScene::createNode函数可以创建节点,此函数会先调用DAWorkFlow::createNode,然后通过创建的node,调用DAAbstractNode::createGraphicsItem接口,获取节点对应的item,并把生成的item返回,此时就完成了节点的生成和渲染
节点在场景中包含了位置坐标,渲染方式,但实际上,对于一个有向图来说,节点是不存在这些信息,一个工作流图仅仅包含关系属性
节点连接点的绘制¶
连接点由DANodeLinkPoint来描述,主要包含4个信息:
1 2 3 4 5 6 7 8 | |
DAAbstractNodeGraphicsItem的generateLinkPoint函数负责生成DANodeLinkPoint,此函数是虚函数,用户可以自定义改变连接点的信息,DAAbstractNodeGraphicsItem在构造时会调用resetLinkPoint函数,resetLinkPoint函数内部会调用generateLinkPoint生成连接点信息
正常连接点信息生成完成就不会发生变化,但如果节点图元缩放时,需要更新连接点的位置信息,需要通过DAAbstractNodeGraphicsItem::updateLinkPointPos函数进行位置信息的更新,此函数会遍历每个连接点,并调用DAAbstractNodeGraphicsItem::changeLinkPointPos函数,对每个连接点进行重新设置
1 2 | |
DAAbstractNodeGraphicsItem::changeLinkPointPos是虚函数,用户如果有自己设置连接点位置的方式可以重载此函数
连接点的生成到更新的相关函数关系如下:

DAAbstractNodeGraphicsItem已经继承了节点连接点的绘制,如果要绘制自己的连接点,可注入DANodeLinkPointDrawDelegate
节点连接点的绘制主要通过DAAbstractNodeGraphicsItem的成员函数paintLinkPoints实现:
1 | |
注意,paintLinkPoints函数并非虚函数,因此,若想更改连接点的绘制,可通过setLinkPointDrawDelegate来设置绘制代理实行连接点的自定义绘制
1 | |
DAWorkFlow库已经提供了DAStandardNode***GraphicsItem相关的图元,用户无需关注过多的连接点重绘即可快速实现自己的图元绘制
连接线的绘制¶
连接线实际上也是一个图元,由DAAbstractNodeLinkGraphicsItem负责绘制,实际上用户并不需要关心连接线是如何绘制的
连接线提供了几种绘制方案,通过DAAbstractNodeLinkGraphicsItem::LinkLineStyle枚举:
1 2 3 4 5 6 7 8 9 | |
通过void setLinkLineStyle(LinkLineStyle s)函数即可实现连接线绘制的改变
节点连接过程¶
连接线连接过程主要由DANodeGraphicsScene触发和管理,DANodeGraphicsScene的mousePressEvent对每次鼠标的点击会进行如下判断:
- 判断是否点击到了节点
- 判断当前的StartLink状态,如果是非开始连接状态调用
DAAbstractNodeGraphicsItem::prepareLinkInput回调,如果是开始连接状态,掉用DAAbstractNodeGraphicsItem::prepareLinkOutput回调 - 判断是否点击到连接点
- 如果点击到连接点,发射
DANodeGraphicsScene::nodeItemLinkPointSelected信号
其流程图如下

DANodeGraphicsScene内部绑定了nodeItemLinkPointSelected信号的槽函数,具体连接线的建立在此槽函数中实现,具体流程如下图所示,这里不再描述

prepareLinkInput和prepareLinkOutput的回调针对大部分情况是不需要实现的,这两个回调主要是针对一些不确定连接点的情况,连接点不是固定的,只有点击节点时才能判断是否应该存在连接点,这时候就可以重载这两个回调函数来动态生成连接点
工作流执行¶
工作流通过DAWorkFlowExecuter执行,每次执行都会生成一个DAWorkFlowExecuter并把它转移到线程中执行,DAWorkFlowExecuter继承于QObject,这里线程调用使用QObject的moveToThread方法,把DAWorkFlowExecuter转移到线程中执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
在工作流执行和结束前,可以注册执行和结束的回调函数
1 2 3 4 5 6 7 8 | |
这两个回调函数都会在线程中执行,因此务必注意,注册的回调函数不要进行界面的操作,否则会导致程序崩溃
工作流在执行的时候会涉及到3个信号:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
finished信号可以判断工作流是否执行成功