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