【大话QT之十六】使用ctkPluginFramework插件系统构建项目实战

"使用ctkPluginFramework插件系统构建项目实战",这篇文章是写博客以来最纠结的一篇文章。倒不是因为技术都多么困难,而是想去描述一个项目架构采用ctkPluginFramework来构建总是未尽其意,描述的太少未免词不达意,描述的太多又显得太啰嗦。有些看过之前写的【大话QT之四】ctkPlugin插件系统实现项目插件式开发这篇文章的朋友也想了解一下到底如果从零开始架构一个项目。在写这篇文章的时候又回头总结了下我之前认为已经懂了的东西,发现还是好多东西没有真正明白其原理是什么,在文章下面的描述中,对构建项目中的关键流程给出实现过程,关于整体细节之处大家可以看例子中的代码。

一、准备阶段

因为本次想实现的项目架构就是基于ctkPluginFramework的,因此开始之前需要:

1) Windows下安装VS2010和QT环境。

2) CTK编译好的CTKCore.dll、CTKPluginFramework.dll以及相关的头文件。

3) 了解QT中基本插件的制作与加载方式。

二、实战阶段

2.1 项目架构分析

每一个中型或大型的项目,在实际开发之前,其代码组织架构一定会经过仔细的规划,比如:目录架构(头文件放在哪个目录;lib库放在哪个目录;开发源代码放在哪个目录;生成的项目插件放在哪个目录;最终发布程序放在哪个目录)。现在,假设我们要为图书馆开发一个图书管理程序,称之为:LibraryProject,然后在该文件夹下有:includes,libs,plugins,bin,application等文件夹,其基本结构如下:

其基本用途如下:applications用于放置项目加载程序的源码;bin下包含conf、plugins目录,bin下存放项目入口exe程序以及独立运行时依赖的dll文件,bin/conf下存放项目配置文件,bin/plugins下放置项目中所有使用到自己开发的插件;includes目录用于放置头文件,包括第三方头文件以及项目中自己定义的接口文件等;libs用于放置项目中使用的第三方开源库的lib文件;plugins用于放置项目中所有的插件开发源码;而上图中的cuc_base.pri是用来定义或加载项目中通用的内容,比如:INCLUDEPATH在该文件中定义则在其它插件子项目中只要include一下这个文件,就可以使用includes中包含的所有头文件。

接下来说一下项目整体运作流程。首先,项目是基于插件开发,因而项目中所有的功能模块都是以dll插件的形式提供。插件大致分为两种类型,即界面插件和功能插件,界面插件主要用来完成界面显示,以及界面上的操作流程,功能插件主要用来提供某一方面的功能。以Ftp客户端工具为例,其操作界面就可以采用一个界面插件来实现,而其本地文件系统数据的提供、文件上传下载等功能的实现就可以使用功能插件来提供。其次,虽然项目所有界面显示以及功能实现都采用dll插件来提供,难道整个项目就是一堆dll吗?并非如此,一个项目总归需要一个启动入口,即传统意义上的exe文件,它既可以复杂到实现所有的功能,也可以简单到只负责加载一个逻辑插件运行就可以了,就好像上程序设计课时老师提及的一句话,main()函数是一定存在的。在本实现中即需要一个这样的最简化的如果程序exe来通过某种方式调用到主逻辑插件,然后开始运行。

在实际动手前我们再总结一下前面提到的几个关键模块:项目启动程序、完成插件注册的插件、功能插件、主逻辑插件以及它们之间的相互关系:

从简单来说,整个项目的运行就如上如所示,读配置,注册插件,加载插件;从复杂来说,在实现上远没有那么简答,总之在使用过程中会碰上各种各样的问题,只有在反复的使用过程中才能不断了解,对出现问题时才能迅速地排查。

2.2 构建项目启动程序

下面,我们就来逐步构建这个项目。首先,项目启动程序最终生成的是exe文件,它是项目的主入口,其主要功能是:1) 通过QPluginLoader的方式来加载 “用于注册插件的插件”。2) 利用 “用于注册插件的插件”来运行主逻辑插件。关键代码如下:

QSettings settings(strConfFile, QSettings::IniFormat);settings.setIniCodec(QTextCodec::codecForName("UTF-8"));QString strPluginLoader = settings.value(CUC_PLUGIN_LOADER).toString();QString strPluginPortal = settings.value(CUC_PLUGIN_PORTAL).toString();//!加载controller插件(用于加载其它插件)QString strPluginLoaderPath = QString(CUC_PLUGIN_PATH).arg(qApp->applicationDirPath()) + strPluginLoader + QString(".dll");QString strPluginPortalPath = QString(CUC_PLUGIN_PATH).arg(qApp->applicationDirPath()) + strPluginPortal + QString(".dll");if (!QFile(strPluginLoaderPath).exists()){qDebug() << "[Error] Controller Plugin does not exists …";return CUC_FAILED;}QPluginLoader PluginLoader(strPluginLoaderPath);CUControllerInterface *Controller = qobject_cast<CUControllerInterface *>(PluginLoader.instance());if (!Controller){qCritical() << QObject::tr("The required module (%1) is invalid, the application will quit.(%2)").arg(strPluginLoader).arg(PluginLoader.errorString());return CUC_FAILED;}else{CUCParameters Parameters;Parameters[CUC_KEY_CONF_FILE] = strConfFile;Parameters[CUC_KEY_PLUGIN_PATH] = strPluginPath;Parameters[CUC_KEY_MAIN_HANDLE] = qVariantFromValue((void *)&CUC);if (Controller->Init(Parameters) != CUC_SUCCESS){return CUC_FAILED;}Controller->LoadAllPlugin(strPluginPath, settings.value(CUC_PLUGIN_EXCLUDE).toString());if (Controller->ExecutePlugin(strPluginPortalPath, Parameters) != CUC_SUCCESS){return CUC_FAILED;}} 代码中,Init()、LoadAllPlugin()以及ExecutePlugin()函数均是在“用于注册插件”的插件中提供的,具体实现请看2.3。

最好的感觉就是你什么都跟我说。

【大话QT之十六】使用ctkPluginFramework插件系统构建项目实战

相关文章:

你感兴趣的文章:

标签云: