前面介绍了如何将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
如果是插件的话只需要将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。
程序运行之后
就可以看到中间空白界面上出现两个仪器,这两个仪器就是刚才的dll插件。