Qt Plugin创建及调用

转自:https://blog.csdn.net/yizhou2010/article/details/78261643

示例资源:EchoPluginTest

如果没有积分,可在这里下载:https://files-cdn.cnblogs.com/files/warmlight/EchoPluginTest.rar

概述
插件是一种遵循一定规范的应用程序接口编写出来的程序,定位于开发实现应用软件平台不具备的功能的程序。插件与宿主程序之间通过接口联系,就像硬件插卡一样,可以被随时删除,插入和修改,所以结构很灵活,容易修改,方便软件的升级和维护。

Qt中的插件
Qt提供了两种API用于创建插件:

一种是高阶API用于扩展Qt本身的功能:如自定义数据库驱动,图像格式,文本编码,自定义样式等等;
一种低阶API用于扩展Qt应用程序。
本文主要是通过低阶API来创建Qt插件,并通过静态、动态两种方式来调用插件,即通过应用程序预留四则预算接口,通过插件来定义四则运算的简单例子。

框架自动搭建
我们利用QT Creator把应用程序和插件的运行框架搭建起来,在EchoPluginTest项目项目新建一个名为EchoPluginApp的应用程序,和一个名为EchoPluginLib的插件。

1、首先创建一个子目录项目,名为EchoPluginTest

 

 

 

 2、创建应用程序EchoPluginApp

 

 这里我们选择的是Qt Widgets Application,并命名为EchoPluginApp

 

 3、创建EchoPluginLib插件

 

 选择:Library –> C++库,并在“项目介绍和位置”选择类型为“Qt Plugin”,名称为“EchoPluginLib”

 

 在类信息中输入类名:“EchoPlugin”,基类默认为“QGenericPlugin”

 

 

至此,基本框架自动生成完成,我们还需进一步对其进行修改。

应用程序中的插件接口创建

在子项目EchoPluginApp中添加一个echointerface.h文件,在该头文件中定义插件接口,应用程序正是通过这些接口来实现额外的功能。

 1 class EchoInterface
 2 {
 3 public:
 4     virtual ~EchoInterface() {}
 5 
 6     virtual QStringList CalculateType(void) const =0;
 7     virtual double Calculate(QString &type,double xvar, double yvar) =0;
 8 };
 9 #define EchoInterface_iid "EchoPluginTest.EchoPluginLib.EchoInterface"
10 Q_DECLARE_INTERFACE(EchoInterface, EchoInterface_iid)

EchoInterface 类定义了两个纯虚函数,第一个函数CalculateType() 返回QStringList 用于标识插件中定义的运算类型,之所以采用 QStringList 是因为一个插件中可能存在多个运算类型。第二个函数Calculate(),用于根据传入的运算类型和运算参数xvar和yvar来返回计算值。
为了能够在运行时查询插件是否实现给定的接口,我们必须使用宏Q_DECLARE_INTERFACE(),该宏的第一参数为接口类的名称,第二个参数是一个字符串,用于唯一标记该接口类。

应用程序中插件接口的使用
在应用程序中,首先我们需要定义loadPlugins() 用于查找插件

1 void MainWindow::loadPlugins()
2 {
3     //调用静态插件
4     foreach (QObject *plugin, QPluginLoader::staticInstances())
5         AddToCombo(plugin);

首先通过QPluginLoader::staticInstances() 函数寻找静态插件,并更新到界面中下拉控件中。

接下来,我们去加载动态插件:

 1     //动态调用插件
 2     QDir pluginDir(qApp->applicationDirPath());
 3     if(pluginDir.dirName().toLower() == tr("debug") || pluginDir.dirName().toLower() == tr("release") )
 4     {
 5         pluginDir.cdUp();
 6         pluginDir.cdUp();
 7         pluginDir.cd("plugins");
 8     }
 9 
10     //遍历当前 文件夹下文件
11     foreach (QString filename, pluginDir.entryList(QDir::Files))
12     {
13         QPluginLoader pluginloader(pluginDir.absoluteFilePath(filename));
14         QObject *plugin = pluginloader.instance();
15         if(plugin != 0)
16             AddToCombo(plugin);
17     }

将变量pluginDir 设置到当前应用程序工作目录对应的Plugins目录下,并通过函数entryList 遍历该目录下所有的文件,对每个文件利用QPluginLoader 去尝试加载插件。通过QPluginLoader::instance() 函数去识别由插件返回的QObject对象,因为如果动态链接库不是qt插件或者编译版本不兼容则,函数将返回空指针。
当函数返回有效插件时,我们将其更新到下拉列表中去。

AddToCombo函数

 1 void MainWindow::AddToCombo(QObject *pplugin)
 2 {
 3     EchoInterface *eInterface = qobject_cast<EchoInterface *>(pplugin);
 4     if(eInterface != 0)
 5     {
 6         QStringList typelist = eInterface->CalculateType();
 7         foreach (QString ctype, typelist)
 8         {
 9             ui->calType->addItem(ctype);
10             PluginItem item;
11             item.text = ctype;
12             item.plugin =pplugin;
13             m_pluginItemList.append(item);
14         }
15     }
16 }

对于每一个插件(不管是动态的,还是静态的),我们都利用qobject_cast() 函数去检验它的接口类,如果为EchoInterface接口则将插件的运算类型添加到下拉列表中去。

下拉列表选择变化响应槽函数

 1 void MainWindow::on_calType_currentIndexChanged(int index)
 2 {
 3     if(index >=0 && index < m_pluginItemList.size())
 4     {
 5         curType = m_pluginItemList.at(index).text;
 6         m_EchoInterface = qobject_cast<EchoInterface *>(m_pluginItemList.at(index).plugin);
 7     }
 8     else
 9     {
10         curType = tr("");
11         m_EchoInterface = 0;
12     }
13 }

计算按钮响应槽函数

1 void MainWindow::on_calculate_clicked()
2 {
3     if(m_EchoInterface !=0 )
4     {
5         double value = m_EchoInterface->Calculate(curType, xvar, yvar);
6         ui->resultVal->setValue(value);
7     }
8 }

至此,简单的应用程序基本完成,下面可以通过预留的接口来开发具备各种运算功能的插件了。

插件开发
在开发插件之前,我们需要对Qt Creator自动生成的插件项目做一些修改,因为其是基于高阶应用生成的。
首先,在EchoPluginLib.pro 文件中的DESTDIR改为

DESTDIR =../plugins

其次,在EchoPluginLib.h 文件中将类EchoPlugin 从继承自public QGenericPlugin改为继承自public QObject, public EchoInterface,更改宏Q_PLUGIN_METADATA中IID参数为”EchoPluginTest.EchoPluginLib.EchoInterface”,并添加宏Q_INTERFACES,和重写接口类的虚函数。

 1 class EchoPlugin : public QObject, public EchoInterface
 2 {
 3     Q_OBJECT
 4 #if QT_VERSION >= 0x050000
 5     Q_PLUGIN_METADATA(IID "EchoPluginTest.EchoPluginLib.EchoInterface" FILE "EchoPluginLib.json")
 6 #endif // QT_VERSION >= 0x050000
 7 
 8     Q_INTERFACES(EchoInterface)
 9 
10 public:
11     EchoPlugin(QObject *parent = 0);
12     virtual QStringList CalculateType() const override;
13     virtual double Calculate(QString &type,double xvar, double yvar)override;
14 };

宏Q_INTERFACES是必须的,用于告诉qt的meta-object compiler,插件接口的基础类,如果没有这个宏,那么在应用程序中,我们无法使用qobject_cast()去检测接口类。
宏Q_PLUGIN_METADATA用于导出该插件,必须包含插件的IID参数,和可选的参数(用于指向一个包含插件MetaData的Json文件)。
插件中重写函数的实现

 1 QStringList EchoPlugin::CalculateType() const
 2 {
 3     return QStringList()<< tr("Add")<<tr("Sub");
 4 }
 5 double EchoPlugin::Calculate(QString &type, double xvar, double yvar)
 6 {
 7     if(type == tr("Add"))
 8         return xvar + yvar;
 9     else if(type == tr("Sub"))
10         return xvar - yvar;
11     else return 0.0;
12 }

至此,一个简单的插件就开发完成了。

编译运行

为了项目EchoPluginTest项目编译子项目的顺序(先EchoPluginLib后EchoPluginApp),我们打开EchoPluginTest.pro文件,将其中的SUBDIRS设置如下:

1 SUBDIRS += \
2     EchoPluginLib \
3     EchoPluginApp

编译并运行EchoPluginTest,应用程序EchoPluginApp中可使用插件EchoPluginLib中定义的加法、减法运算。

 

 

插件的静态调用
如需静态调用插件,以上项目需做如下更改:
1. 在子项目EchoPluginLib中,打开EchoPluginLib.pro文件,在CONFIG后添加static:

CONFIG += plugin static

2.在子项目EchoPluginApp中,打开main.cpp文件,添加宏Q_IMPORT_PLUGIN,如

 1 Q_IMPORT_PLUGIN(EchoPlugin)
 2 
 3 int main(int argc, char *argv[])
 4 {
 5     QApplication a(argc, argv);
 6     MainWindow w;
 7     w.show();
 8 
 9     return a.exec();
10 }

3.在子项目EchopluginApp中,打开EchoPluginApp.pro文件中添加插件库。可在EchopluginApp通过右键,选择“添加库”–>“外部库”来自动添加。例如

1 win32: LIBS += -L$$PWD/../../***/plugins/ -lEchoPluginLib
2 
3 INCLUDEPATH += $$PWD/../../***/plugins
4 DEPENDPATH += $$PWD/../../***/plugins

4.在子项目EchopluginApp项目中的loadplugins函数中使用QPluginLoader::staticInstances()函数来加载插件。


更多的资料可参考官网相关例子:Plug & Paint Example, Plug & Paint Basic Tools Example, Plug & Paint Extra Filters Example, Echo Plugin Example。
如果没有积分,可从这里下载:https://files-cdn.cnblogs.com/files/warmlight/EchoPluginTest.rar

posted @ 2020-04-03 15:33  阳光下的小土豆  阅读(3397)  评论(0编辑  收藏  举报