前面介紹了如何將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插件。