前面介绍了如何将QWidget封装成dll库并且使用,这样存在的一个问题就是 :必须要配置.pro文件,建立lib路径连接,并且需要在使用到的地方include相应的头文件。
除了在.pro中配置动态库,调用动态库的方式还有QLibrary和QPluginLoader两种。
相比于QLibrary调用动态库,QPluginloader可以将封装成动态库的界面程序实例化,而QLibrary则只能访问动态库中的函数,无法将DLL实例化,因此在使用由界面封装而来的dll时,用QPluginLoader加载动态库更为合适。
下面将详细介绍QPluginLoader库的封装及使用。
在https://www.cnblogs.com/leocc325/p/15001467.html中已经说明了如何将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插件。