前面介绍了如何将QWidget封装成dll库并且使用,这样存在的一个问题就是 :必须要配置.pro文件,建立lib路径连接,并且需要在使用到的地方include相应的头文件。

除了在.pro中配置动态库,调用动态库的方式还有QLibrary和QPluginLoader两种。

相比于QLibrary调用动态库,QPluginloader可以将封装成动态库的界面程序实例化,而QLibrary则只能访问动态库中的函数,无法将DLL实例化,因此在使用由界面封装而来的dll时,用QPluginLoader加载动态库更为合适。

下面将详细介绍QPluginLoader库的封装及使用。

在中已经说明了如何将QWidget封装成动态库,但是通过这种方法封装出来的动态库无法被QPluginLoader加载,因此需要在原有的基础上进行一些改进。

首先还是需要创建一个接口(抽象基类)AbstractProcess,然后需要在接口中添加以下宏命令



QT_BEGIN_NAMESPACE
#define AbstractProcess_IID "org.qter.Examples.myplugin.AbstractProcess"
Q_DECLARE_INTERFACE(AbstractProcess,AbstractProcess_IID)
QT_END_NAMESPACE


红色部分是需要要添加的宏,橙色部分内容可以自定义。只有添加了这个宏之后,这个插件才能被QT识别。

整个基类头文件如下



#ifndef ABSTRACTPROCESS_H
#define ABSTRACTPROCESS_H

#include <QObject>
#include <QWidget>
#include <QJsonObject>
#include <QPaintEvent>
#include <QPainter>
#include <QStyleOption>

#if defined(ABSTRACTPROCESS_LIBRARY)
# define ABSTRACTPROCESSSHARED_EXPORT Q_DECL_EXPORT
#else
# define ABSTRACTPROCESSSHARED_EXPORT Q_DECL_IMPORT
#endif

class HttpConnector;
class ABSTRACTPROCESSSHARED_EXPORT AbstractProcess:public QWidget
{
Q_OBJECT
public:
explicit AbstractProcess(QWidget *parent = nullptr);
virtual ~AbstractProcess();
virtual void InitProcess() = 0;//初始化
virtual void SetProcessParameter(QJsonObject&) = 0;//设置一些必要的参数
virtual void PostOperateScore(HttpConnector*) = 0;//发送考核项目分数
virtual void PostOperateLog(HttpConnector*) = 0;//发送考核项目日志
virtual void ChangeOperateItem(int operateCode) = 0;//更换当前检定项目,跳转到对应的界面
protected:
virtual void paintEvent(QPaintEvent *);
signals:
void OperateIndexDone(int deviceCode,int indexCode);
void ProcessMessage(const QString& info);//发送信息到主进程

};

//封装成插件需要在原本封装dll的基础上添加以下语句
QT_BEGIN_NAMESPACE
#define AbstractProcess_IID "org.qter.Examples.myplugin.AbstractProcess"
Q_DECLARE_INTERFACE(AbstractProcess,AbstractProcess_IID)
QT_END_NAMESPACE

#endif // ABSTRACTPROCESS_H


如上面注释内容一样,和普通的QWidget封装dll相比,仅仅需要在头文件中添加Q_DECLARE_INTERFACE()宏。

创建好接口之后,就需要继承接口创建一个可以实例化的插件dll。

在子类的头文件中需要添加以下两个宏命令



Q_PLUGIN_METADATA(IID AbstractProcess_IID)
Q_INTERFACES(AbstractProcess)


子类头文件如下



#ifndef PROCESSMULITMETER_H
#define PROCESSMULITMETER_H

#include <QWidget>
#include "abstractprocess.h"

#if defined(PROCESSMULTIMER_LIBRARY)
# define PROCESSMULTIMERSHARED_EXPORT Q_DECL_EXPORT
#else
# define PROCESSMULTIMERSHARED_EXPORT Q_DECL_IMPORT
#endif

namespace Ui {
class ProcessMulitmeter;
}

class PROCESSMULTIMERSHARED_EXPORT ProcessMultimeter : public AbstractProcess
//class ProcessMultimeter : public AbstractProcess
{
Q_OBJECT
//和原本封装QWidget的DLL相比,需要添加以下宏命令
Q_PLUGIN_METADATA(IID AbstractProcess_IID)
Q_INTERFACES(AbstractProcess)

public:
explicit ProcessMultimeter(QWidget *parent = nullptr);
~ProcessMultimeter();
void InitProcess() {;}//初始化
void SetProcessParameter(QJsonObject&) {;}//设置一些必要的参数
void PostOperateScore(HttpConnector*) {;}//发送考核项目分数
void PostOperateLog(HttpConnector*) {;}//发送考核项目日志
void ChangeOperateItem(int operateCode) {Q_UNUSED(operateCode);}//更换当前检定项目,跳转到对应的界面

private:
Ui::ProcessMulitmeter *ui;
};

#endif // PROCESSMULITMETER_H


需要注意的是,Q_PLUGIN_METADATA()宏有两个参数,第一个参数为IID,与接口的IID相同,将接口的IID复制过来就行了,第二个参数FILE是可选的,指定一个本地json文件,用于描述插件的相关数据信息。

如果没有特别的需求,第二个参数可以省略,json文件也不需要创建。

在完成编译之后,就可以看到debug文件夹中生成的dll

QT 自定义插件及使用(QPluginLoader)_json

 

 

 如果是插件的话只需要将dll复制到需要使用的工程目录中,和exe同级。不需要添加头文件和lib,也不需要在.pro中进行配置,以及#include

比调用动态库方便太多。

之后就可以在程序中用QPluginLoader加载这个库



QPluginLoader loader("ProcessMultimeterD.dll");
if(loader.load())
{
if(QObject * plugin = loader.instance())
{
process = dynamic_cast<AbstractProcess *>(plugin);
qDebug()<<process;
process->setParent(ui->ExamStackedWidget->widget(1));
}
}
else
{
qDebug()<<loader.errorString();
}


因为我这里dll和exe在同一级文件夹中,因此不需要在前面添加文件路径什么的,直接将dll名字作为Loader参数就可以找到这个插件。

这里的process是一个AbstractProcess指针,要创建dll的实例化对象,依然需要包含接口的头文件,lib和dll,子类的只需要包含dll。

程序运行之后

QT 自定义插件及使用(QPluginLoader)_#include_02

 

 

 就可以看到中间空白界面上出现两个仪器,这两个仪器就是刚才的dll插件。