1. 程式人生 > 其它 >QT 自定義外掛及使用(QPluginLoader)

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外掛。