一:qtplugin 介绍
Qt Plugin和其他类型的插件一样,是一种计算机应用程序,它和主应用程序(host application)互相交互,以提供特定的功能。应用程序支持Plugin有许多原因,一些主要原因包括:使得第三方开发者有能力扩展应用程序,以提供无法先期预料的特色;减小应用程序的大小;由于软件版权之间的不兼容性将源代码和应用程序分享。Qt Plugin 分动态插件和静态插件两种。
二:qtplugin 创建和使用方法
Qt有两种与插件有关的API。一种用来扩展Qt本身的功能,如自定义数据库驱动,图像格式,文本编解码,自定义分格,等等,称为Higher-Level API。另一种用于应用程序的功能扩展,称为Lower-Level API。前一种是建立在后一种的基础之上的。这里讨论的是后一种,即用来扩展应用程序的Lower-level API。
让应用程序支持插件扩展的步骤:
1. 定义一个接口集(只有纯虚函数的类),用来与插件交流。
2. 用宏Q_DECLARE_INTERFACE()将该接口告诉Qt元对象系统。
Q_DECLARE_INTERFACE(BrushInterface,"com.trolltech.PlugAndPaint.BrushInterface/1.0")
3. 应用程序中用QPluginLoader来装载插件。
4. 用宏qobject_cast()来确定一个插件是否实现了接口。
QObject *obj = new QTimer;
QTimer *timer = qobject_cast<QTimer*>(obj);
写一个插件的步骤:
1. 声明插件类,该类从QObject和该插件希望实现的接口继承而来。
2. 用宏Q_INTERFACES()将该接口告诉Qt元对象系统。
classBasicToolsPlugin : public QObject,
public BrushInterface,
publicShapeInterface,
publicFilterInterface
{
Q_OBJECT
Q_INTERFACES(BrushInterface ShapeInterface FilterInterface)
public:
...
};
3. 用宏Q_EXPORT_PLUGIN2()导出插件。
Q_EXPORT_PLUGIN2 ( PluginName, ClassName )
4. 用适当的.pro文件构建插件。
下面的代码声明了一个接口类:
class FilterInterface
{
public:
virtual ~FilterInterface() {}
virtual QStringList filters() const = 0;
virtual QImage filterImage(const QString &filter, const QImage&image, QWidget* parent)=0;
};
Q_DECLARE_INTERFACE(FilterInterface,"com.trolltech.PlugAndPaint.FilterInterface/1.0")
这里是实现该接口的插件类的定义:
#include <QObject>
#include <QStringList>
#include <QImage>
#include <plugandpaint/interfaces.h>
class ExtraFiltersPlugin : public QObject,public FilterInterface
{
Q_OBJECT
Q_INTERFACES(FilterInterface)
public:
QStringList filters() const;
QImage filterImage(const QString&filter, const QImage &image,
QWidget *parent);
};
根据插件的类型不同,pro文件中配置上有不同。下面是pro文件分析:
TEMPLATE = lib // 声明为lib,动态和静态插件一样。
CONFIG += plugin static //声明为plugin,带static表面为静态,否则为动态。
INCLUDEPATH += ../..
HEADERS = basictoolsplugin.h
SOURCES = basictoolsplugin.cpp
TARGET = $$qtLibraryTarget(pnp_basictools) //指明插件的名称
DESTDIR = ../../plugandpaint/plugins
加载插件的主应用程序默认在当前目录下的plugins文件夹中寻找可用插件,如果是动态插件,则直接放在plugins文件夹中便可,如果是静态,则需要在主应用程序的main函数的开始的地方用宏:Q_IMPORT_PLUGIN(pluginname(和pro文件中声明的一致))声明需要加载的插件并在工程配置中指明插件的lib位置。
三:基于qtplugin 技术的框架结构设想
1. 愿景
由于我们目前系统功能多,模块多,缺乏系统的整体性。我们想借助Qt Plugin技术,把各个独立的功能模块实现为一个个插件,统一在主体框架中,并能根据不同地方的用户的不同需求,在主框架中加载不同的功能模块,以实现整个系统的功能集中,体现出系统的整体性。
2. plugin接口
通过技术验证得出,目前我们采用动态插件,各个功能的插件实现定义的统一接口,具体功能放在插件界面中实现,此部分就像开发独立的应用程序,只是需要注意的是:
① 功能部分的主界面需要继承至插件界面基类:PluginWidget,插件接口中用具体的实现类指针给插件界面基类指针赋值,在加载插件的主框架中通过插件接口中定义的基类指针统一调用,利用C++动态技术动态识别具体指向的实现类。
② 插件界面类必须实现基类的虚函数:CreateActions()用于创建Action
③ 创建Action需要使用基类的方法newAction创建,在此函数中加入了保存创建的Action功能。
插件接口定义如下:
class QPluginInterface
{
public:
// 析构函数
virtual ~QPluginInterface() {}
// 插件名称
virtual QString PluginName()= 0;
// 插件显示在主框架中的图标文件路径
virtual QString PluginIconurl() = 0;
// 插件提供的对外操作接口集
virtual QList<QAction*>*Actions() = 0;
// 创建插件提供的操作方法
virtual void CreateActions()=0;
// 插件的主界面
virtual QWidget* Widget() =0;
protected:
// 插件的主界面基类
PluginWidget*pluginWidget;
};
插件界面基类定义如下:
class PluginWidget :public QMainWindow
{
Q_OBJECT
public:
PluginWidget(QWidget*parent=0);
~PluginWidget();
QList<QAction*>* Actions();
virtual void CreateActions(){}
QAction * newAction(const QIcon &icon,const QString &text,QObject*parent);
QAction * newAction(const QString &text,QObject*parent);
void AppendAction(QAction*act);
protected:
// action链表
QList<QAction*> *m_actlist;
};
下图是一个实现案例中各类之间的关系图:
3. 插件调用
插件在主框架中动态加载,目前考虑主框架基本结构是继承至QMainWindow,工具栏上显示当前加载的插件的功能键,并留有返回键可以回退到上一级。主工作区是一个QStackWidget,保存插件的界面,并把插件序号和插件对应的界面建立映射,保存在QMap<int,QWidget>中。通过序号到QStackWidget中切换界面。
下图是把 DBManager 做成插件加载到主框架的运行界面:下图是把一个简单的绘图程序做成了插件,加载到主框架的运行界面:
四:总结
目前只是通过实现两个动态插件在主框架中运行,基本算是功能性的验证,离具体实施还有很多工作需要进一步的研究,比如主框架的风格,插件的管理等等。由于本人的能力有限,可能有很多认识不够的地方,请指正。