数据工作流设计器
逻辑编辑器负责组织描述逻辑,以工作流为基础,DataWorkFlow的工作流以插件模式加载入程序中,主要涉及以下两个基础个模块(模块顺序为依赖顺序):DAPlugin
、DANode
,同时Plugin\DAUtilNodePlugin
为DataWorkFlow的基础节点插件,其他的插件编写可参考此插件编写。
DAPlugin
模块是为了提供一个统一且简单的插件管理功能,此模块提供了插件的基类DAAbstractPlugin
,作为DataWorkFlow的插件,只要继承此类,即可实现插件。
同时,DAPlugin
模块提供了所有插件管理的单例:DAPluginManager
,这是一个单例,可以用来加载和卸载插件,下面是通过此类加载插件的例子:
//加载插件
DAPluginManager& plugin = DAPluginManager::instance();
if (!plugin.isLoaded()) {
plugin.load();
}
//获取插件
QList<DAPluginOption> plugins = plugin.getPluginOptions();
for (int i = 0; i < plugins.size(); ++i)
{
DAPluginOption opt = plugins[i];
if (!opt.isValid()) {
continue;
}
DAAbstractPlugin *p = opt.plugin();
//开始通过dynamic_cast判断插件的具体类型
if (DAAbstractNodePlugin *np = dynamic_cast<DAAbstractNodePlugin *>(p)) {
//说明是节点插件
...
qDebug() << tr("succeed load plugin ") << np->getName();
}
}
DAPluginOption
维护着每个插件的内容,内部包含了此插件(dll)的一些基本信息
要实现一个插件,必须在lib中实现两个函数,和实现一个插件类,以DAUtilNodePlugin
为例,具体可参见src\Plugin\DAUtilNodePlugin
DAAbstractNodePlugin
实现自己的插件类,这里为了进一步抽象,定义了节点插件的基类DAAbstractNodePlugin
,DAUtilNodePlugin
继承于DAAbstractNodePlugin
class DANODE_API DAAbstractNodePlugin : public DAAbstractPlugin
{
public:
DAAbstractNodePlugin();
virtual ~DAAbstractNodePlugin();
...
};
class DAUtilNodePlugin : public DAAbstractNodePlugin
{
public:
DAUtilNodePlugin();
~DAUtilNodePlugin();
};
DAUtilNodePlugin
里将实现具体的功能,这里不描述
plugin_create
和plugin_destory
的实现在实现了DAUtilNodePlugin
类后,还需要实现两个C标准的导出函数,plugin_create
和plugin_destory
:
extern "C" DAUTILNODEPLUGIN_API DAAbstractPlugin *plugin_create();
extern "C" DAUTILNODEPLUGIN_API void plugin_destory(DAAbstractPlugin *p);
这两个函数是关键,插件系统在加载时需压根据c函数名来查找这两个函数
这两个函数的功能很简单, 就是生成插件,和删除插件,具体实现如下:
DAAbstractPlugin *plugin_create()
{
return (new DAUtilNodePlugin());
}
void plugin_destory(DAAbstractPlugin *p)
{
delete p;
}
这样,一个插件就能正常运行。
工作流主要的模块为DANode
,这个模块为工作流的核心和最基本的抽象,此模块主要的几个类为:
工作流的基本逻辑类:
DAWorkFlow
,DAAbstractNode
,DAAbstractNodeFactory
工作流的显示类:
DAAbstractNodeGraphicsItem
,DAAbstractNodeLinkGraphicsItem
,DAAbstractNodeWidget
,DANodeGraphicsScene
,DANodeGraphicsView
工作流基于插件模式编写,后续用户可基于DANode
模块实现自己的逻辑,具体可参见src\Plugin\DAUtilNodePlugin
工作流由节点组成,节点间传递数据,每个节点可以运行用户定义的逻辑,一个工作流就是多个节点以及节点间的关系描述,节点有输入和输出,节点的输出可流入到下一个节点中,作为下个节点的输入。 每个节点的数据流出时,会收集输入数据,进行逻辑运算,再输出数据。
因此工作流的关键就是每个节点,每个节点可以理解为编程过程中的函数,节点的输入就是函数的传参,节点的输出就类似于函数的返回。
节点作为工作流的关键,DANode
模块的关键类就是DAAbstractNode
,工作流都面向这个基类,具体功能的节点由用户自己继承DAAbstractNode
来实现,例如src\Plugin\DAUtilNodePlugin\DAVariantValueNode.h
DAAbstractNode
描述了节点的抽象信息,包括输入,输出,节点的连接,如果以一个函数为例,要通过一个节点描述一个类似函数的功能,这个节点应该具备如下要素:
因此DataWorkFlow的节点定义如下:
这个节点表示了一个加法函数
def add(a,b):
return a+b
它有两个输入端a和b,他有一个输出端,由于函数的return
是不需要具体描述的,但节点的输出要为其定义名称,因为后面节点的传递过程需要用到上个节点的输出,为了能知道哪个输出,所以输出也要定义名称。
因此节点的抽象类定义了输入参数名和输出参数名,对应两个接口函数:getInputKeys
和getOutputKeys
//获取所有输入的参数名
virtual QList<QString> getInputKeys() const = 0;
//获取所有输出的参数名
virtual QList<QString> getOutputKeys() const = 0;
要实现一个节点,必须指定这个节点输入什么,输出什么,当然,可以没有输入,也可以没有输出。
上面说了节点的抽象,实际一个工作流是由多个节点组成,而DataWorkFlow是由插件组成,如何知道程序有哪些节点,这里参考程序开发的import
节点的生成由节点工厂实现,节点工厂可以理解为一个程序包,程序包里有多个函数,节点工厂正是这样一个程序包。
DAAbstractNodeFactory
类就是节点的抽象工厂,此类关键的函数是create
接口,工厂将通过DANodeMetaData
来生成一个节点
//工厂函数,创建一个DAAbstractNode,工厂不持有DAAbstractNode的管理权
virtual DAAbstractNode *create(const DANodeMetaData& meta) = 0;
DANodeMetaData
是节点的基本信息,其关键函数是:
QString getNodePrototype() const;
DANodeMetaData
通过NodePrototype来决定new
哪个节点。
一个程序可以import
多个包,因此工作流也应该有多个节点工厂,汇总到一个"集团"中,"集团"再生成节点
这个"集团"就是DAWorkFlow
类,这个类汇总了所有工厂,其关键函数如下:
//注册工厂
void registFactory(DAAbstractNodeFactory *factory);
//创建节点,会触发信号nodeCreated,DAWorkFlow保留节点的内存管理权
DAAbstractNodeSmtPtr createNode(const DANodeMetaData& md);
"集团"汇总了所有工厂,节点通过"集团"创建,程序启动过程,先加载插件,插件生成工厂,工厂在集团中注册,最后再通过集团生成节点,具体流程如下:
在程序启动完成后,所有节点工厂到注册到DAWorkFlow
中,通过DAWorkFlow
即可生成节点。
节点的渲染基于Qt的Graphics View Framework
,通过Graphics View来实现渲染,整个工作流作为一个画布(QGraphicsScene),为此DANode
模块提供了DANodeGraphicsScene
,DANodeGraphicsView
和DAAbstractNodeGraphicsItem
来实现
DAAbstractNodeGraphicsItem
对应一个DAAbstractNode
,实际是通过DAAbstractNode
的createGraphicsItem
接口来生成DAAbstractNodeGraphicsItem
因此,要实例化一个节点还需要实现接口函数DAAbstractNode::createGraphicsItem
//节点对应的item显示接口,所有node都需要提供一个供前端的显示接口
virtual DAAbstractNodeGraphicsItem *createGraphicsItem() = 0;
这个接口返回DAAbstractNode
对应的GraphicsItem,DANode
模块把节点的GraphicsItem抽象为DAAbstractNodeGraphicsItem
,DAAbstractNodeGraphicsItem
实现了节点的基本渲染,结合DANodeGraphicsScene
和DAAbstractNodeLinkGraphicsItem
实现节点输入输出的连接工作。
对于用户来说,用户操作节点实现了节点之间的关系连接,这个要反应到逻辑层,逻辑层既为DAAbstractNode
,因此,前端看到的连线动作,逻辑端实际是调用DAAbstractNode
的linkTo
函数,
//建立连接,如果基础的对象需要校验,可继承此函数
virtual bool linkTo(const QString& outpt, Pointer toItem, const QString& inpt);
在前端是通过DAAbstractNodeLinkGraphicsItem
实现两个DAAbstractNodeGraphicsItem
的连接,用户基本不需要关注DAAbstractNodeLinkGraphicsItem
,用户只需要关心节点的连接点的生成。
用户实现自己的节点需要继承paint
和boundingRect
函数
//绘图
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
//绘图相关
QRectF boundingRect() const;
DAAbstractNodeGraphicsItem
已经默认实现了输入输出点的绘制,如果需要自定义,可以重写paintLinkPoint
函数
//绘制某个连接点
virtual void paintLinkPoint(const DANodeLinkPoint& pl, QPainter *painter);
DANode
模块提供了标准的节点渲染类DAStandardNodeGraphicsItem
,此类实现了节点的名字显示和输入输出的显示。
节点存在一些动作,如被传入参数等操作时,应该反映到GraphicsItem执行某些绘图操作,这时可以通过重写DAAbstractNodeGraphicsItem::nodeAction
函数来实现
virtual void nodeAction(int action, const QVariant& v);
action动作参见DAAbstractNode::NodeAction
枚举
标准节点渲染渲染了DAAbstractNode
的输入端,输出端,和节点名,其效果如下:
上面演示了两种节点,一个节点只有输出,一个节点有3个输入,2个输出。
标准渲染节点具备如下特性
用户对节点的操作,例如赋予初始值等等需要用过界面实现,这时需要提供一个窗口给用户,DANode
模块DAAbstractNodeWidget
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。