QT 自定義外掛及使用(QPluginLoader)
前面介紹瞭如何將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外掛。