前言
本文Qt中创建应用程序的插件主要有两方面:
一、创建插件
二、在应用程序中使用插件
一、创建插件
先用文字描述一下创建插件的主要步骤
①、首先新建一个插件的接口类,这个类只包含纯虚函数,这个纯虚函数是用于给接口类实现具体功能的。并在接口类中使用Q_DECLARE_INTERFACE()宏在Qt的元对象系统中注册该接口。
②、新建一个插件类,这个插件类一定要继承QObject 和①中新建好的接口类,并要求实现接口类的纯虚函数(即这个插件所需要实现的功能)。并使用 Q_PLUGIN_METADATA()宏导出插件、Q_INTERFACES()在Qt的元对象系统中注册该接口。
③、编写好.pro文件,用于编译构建生成.so文件。
下面具体代码实现创建插件
1、新建插件的接口类
//PluginInterface.h
#ifndef PLUGININTERFACE
#define PLUGININTERFACE
#include <QString>
class PluginInterface
{
public:
virtual ~PluginInterface(){}
virtual QString regexp(const QString &message) = 0;
};
Q_DECLARE_INTERFACE(PluginInterface,"pluginInterface") // Q_DECLARE_INTERFACE()在Qt元对象系统中注册了该接口,其中第一个参数为接口类名,第二个参数为一个字符串,用来确保这个接口与其他接口不相同
/*Q_DECLARE_INTERFACE 宏告诉Qt 这个纯虚类是一个插件接口类。
Q_DECLARE_INTERFACE 宏是与qobject_cast相关的。
第一个参数是接口类名,第二个参数是插件标志符,标识符是大小写敏感的且必须是唯一。*/
#endif // PLUGININTERFACE
2、新建插件类
// MyPlugin.h
#ifndef MYPPLUGIN_H
#define MYPPLUGIN_H
#include <QObject>
#include "plugininterface.h"
class MyPlugin:public QObject,PluginInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "myplugin") //导出插件 (ps:Q_EXPORT_PLUGIN2()宏已经废弃,用时会报错,所以用Q_PLUGIN_METADATA()),第一个参数IID是必须的,同插件标识一样,而第二个参数FILE是可选的.
Q_INTERFACES(PluginInterface) //在Qt的元对象系统中注册该接口
public:
QString regexp(const QString &message) Q_DECL_OVERRIDE; // 实现接口类的纯虚函数,这里功能是输出字符串中出现的的第一个数字
};
/*
Q_PLUGIN_METADATA : Q_PLUGIN_METADATA 宏用于描述插件元数据,第一个参数 IID 是必须的,同插件标识符一样,
而第二个参数FILE是可选的,指定一个本地的 json 文件,该文件可以描述插件的相关数据信息。
Q_INTERFACES :若一个头文件或源文件中用到了 Q_INTERFACES 宏,那么在调用这个宏之前,
必须存在一个 Q_DECLARE_INTERFACE 宏声明相应的接口
(或者包含一个用 Q_DECLARE_INTERFACE 宏声明了该接口的头文件),
MOC会检查这一点,因为它在为 Q_INTERFACES 宏生成代码时要用到Q_DECLARE_INTERFACE 宏的 IID 参数。
Q_INTERFACES宏也是与qobject_cast相关,没有 Q_DECLARE_INTERFACE 和 Q_INTERFACES 这两个宏,
就无法对从插件中获取的实例指针进行qobject_cast映射
Q_DECL_OVERRIDE :Q_DECL_OVERRIDE 宏来声明这是一个对虚函数进行定义的方法
,编译器会验证该方法名是否是父类中所有的,如果没有则报错(这个类似 Java里的 @Override 注解)。
*/
#endif // MYPPLUGIN_H
//myPlugin.cpp
#include "myPlugin.h"
#include <QRegExp>
#include <QtPlugin>
QString myPlugin::regexp(const QString &message)
{
QRegExp rx("\\d+");
rx.indexIn(message);
QString str = rx.cap(0);
return str;
}
3、配置.pro文件
HEADERS += regexpplugin.h regexpinterface.h //用到的.h文件
SOURCES += regexpplugin.cpp //用到的.cpp文件
TEMPLATE = lib //表明要构建的是库文件,而不是可执行文件(TEMPLATE = app)
CONFIG += plugin //告知qmake要创建一个插件
TARGET = mypplugin //生成库的名字 (mypplugin.so)
DESTDIR = ../plugins //生成库存放的位置
4、在项目中点击构建项目,这时可以看到plugins文件夹下生成了mypplugin.so文件了,这个就是插件,下面我们需要通过应用程序使用该插件来实现功能。
二、在应用程序中使用插件
应用程序主要实现的功能是:在QLineEdit中输入一段字符串,当点击一下QPushButton后,就会在QLabel中显示在字符串中出现的第一个数字。
//widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "plugininterface.h" //在上一步中写好的插件接口类
namespace Ui {
class widget;
}
class widget : public QWidget
{
Q_OBJECT
public:
explicit widget(QWidget *parent = 0);
~widget();
private slots:
void on_pushButton_clicked();
private:
Ui::widget *ui;
PluginInterface *pluginInterface;
bool loadPlugin(); // 加载插件
};
#endif // WIDGET_H
//widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPluginLoader>
#include <QMessageBox>
#include <QDir>
widget::widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::widget)
{
ui->setupUi(this);
if(!loadPlugin()) // 加载插件,如果插件加载不成功则显示提示框
{
QMessageBox::information(this,"Error","Could not load the plugin");
ui->lineEdit->setEnabled(false);
ui->pushButton->setEnabled(false);
}
}
widget::~widget()
{
delete ui;
}
bool widget::loadPlugin()
{
QDir pluginsDir("../plugins"); // 我的插件存放在 ../plugins目录下
foreach(QString fileName,pluginsDir.entryList(QDir::Files)) // 便利目录下的所有普通文件
{
QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName)); //利用pluginLoader来加载插件
QObject *plugin = pluginLoader.instance();//返回插件的根组件对象。如果需要插件会被加载。如果无法加载插件,或者无法实例化根组件对象,则函数返回0。
if(plugin)
{
regexpInterface = qobject_cast<PluginInterface *>(plugin);//测试插件是否实现了PluginInterface接口
if(regexpInterface) // 插件加载成功
return true;
}
}
return false; // 没有插件或者插件加载失败
}
void widget::on_pushButton_clicked()
{
QString str = regexpInterface->regexp(ui->lineEdit->text());
ui->labelNum->setText(str);
}
3、运行结果如下所示