在前一篇文章中介绍了插件的定义及Qt中插件的基本概念,那么这里通过一个最简单的demo来对Qt插件开发有一定的了解。
要想使用插件来扩展应用程序,那么首先在主程序中的步骤如下:
编写扩展 Qt 应用程序的插件,步骤如下:
我使用的环境:MacOS + Qt 5.12.3
这里只是为了演示插件的创建和使用,所以做了一个功能非常简单的代码。在插件中计算两个数据的和并返回结果到主工程中进行显示。
项目名称为PluginDemo
创建好后会提示创建一个子项目,这里直接创建一个最简单的widget主工程项目,并命名为MainWindow
。
接着,我们再创建一个插件的工程,右击主工程,添加子项目:
选择一个空项目添加:
OK,主工程和插件工程已创建完成。
接下来在主工程中新建一个头文件,用于定义与插件通信的接口(只有纯虚函数的类)
#ifndef CALINTERFACE_H
#define CALINTERFACE_H
//定义接口
class CalInterface
{
public:
virtual ~CalInterface() {}
virtual int add(int a,int b) = 0;
};
#define CalInterface_iid "Examples.Plugin.CalInterface"
QT_BEGIN_NAMESPACE
Q_DECLARE_INTERFACE(CalInterface,CalInterface_iid)
QT_END_NAMESPACE
#endif // CALINTERFACE_H
为方面演示,这里只做了非常简单的加法运算,所以创建了一个纯虚函数接口add,这里的CalInterface_iid
宏定义字符串一定要是唯一的,然后使用宏Q_DECLARE_INTERFACE
来声明该接口。
插件通信接口写完了,然后开始来编写插件,在插件工程下创建一个新的类,同时继承于QObject和通信接口类CalInterface,如下:
#include
#include
#include "calinterface.h"
class CalPlugin : public QObject,public CalInterface
{
Q_OBJECT
Q_INTERFACES(CalInterface)
Q_PLUGIN_METADATA(IID CalInterface_iid FILE "calplugin.json")
public:
explicit CalPlugin(QObject *parent = nullptr);
int add(int a,int b);
};
#include "calplugin.h"
CalPlugin::CalPlugin(QObject *parent) : QObject(parent)
{
}
int CalPlugin::add(int a, int b)
{
return a + b;
}
注意,在头文件中一定要用Q_INTERFACES
宏来声明接口类。
这里的calplugin.json
是为插件提供插件信息的,并使用Q_PLUGIN_METADATA
声明(实例化该对象的)插件的元数据,元数据是插件的一部分。
Q_PLUGIN_METADATA
这个宏所在的类必须是默认可构造的。
FILE 是可选的,并指向一个 Json 文件。
Json 文件必须位于构建系统指定的包含目录之一中。当无法找到指定的文件时,moc 会出现错误。
如果不想为插件提供信息,当然不会有任何问题,只需保证 Json 文件为空就行。
由于插件编译出来也是dll的形式,所以我们需要在工程文件中将类型改成lib,以及其他的一些配置,如下:
TEMPLATE = lib
CONFIG += plugin
QT += widgets
INCLUDEPATH += ../MainWindow
TARGET = $$qtLibraryTarget(calplugin)
DESTDIR = ../plugins
EXAMPLE_FILES = calplugin.json
HEADERS += \
calplugin.h
SOURCES += \
calplugin.cpp
CONFIG += install_ok
#include
#include "calinterface.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_pushButton_clicked();
private:
bool loadPlugin();
private:
Ui::Widget *ui;
CalInterface * m_pInterface = nullptr;
};
cpp文件
#include
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
if(!loadPlugin()){
QMessageBox::warning(this, "Error", "Could not load the plugin");
}
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
int a = ui->lineEdit_a->text().toInt();
int b = ui->lineEdit_b->text().toInt();
int r = -1;
if(m_pInterface)
r = m_pInterface->add(a,b);
ui->lineEdit_r->setText(QString::number(r));
}
bool Widget::loadPlugin()
{
QDir pluginsDir(qApp->applicationDirPath());
#if defined(Q_OS_WIN)
if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
pluginsDir.cdUp();
#elif defined(Q_OS_MAC)
if (pluginsDir.dirName() == "MacOS") {
pluginsDir.cdUp();
pluginsDir.cdUp();
pluginsDir.cdUp();
}
#endif
pluginsDir.cd("plugins");
foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
QObject *plugin = pluginLoader.instance();
qDebug() << __FUNCTION__ << pluginLoader.errorString();
if (plugin) {
m_pInterface = qobject_cast(plugin);
if (m_pInterface)
return true;
}
}
return false;
}
其中loadPlugin()函数就是用于加载本地的插件文件,以上是动态加载的方式实现。
示例Demo