Qt插件之創建應用程序插件

創建一個插件時,要先創建一個接口,接口就是一個類,它只包含純虛函數。插件類要繼承自該接口。插件類存儲在一個共享庫中, 因此可 以在應用程序運行時進行加載。創建一個 插件包括分三個部分:

1、插件類     2、插件接口    3、調用插件

所以分爲以下幾步:

  1. 定 義一個插件類,它需要同時繼承自 QObject 類和該插件所提供的功能對應的接口類
  2. 用 QJNTERFACESO宏在 Qt 的元對象 系統中註冊該接口;
  3. 用 Q_PLUGIN_METADATA()宏聲明該插件;
  4. 修改插件的. pro 文件構建該插件編譯生成dll。/* 此時插件類部分完成  到應用程序部分中加載dll */
  5. 定義一組接口(只有純虛函數的抽象類)
  6. 用 Q_DECLARE_INTERFACE()宏在 Qt 的元對象 系統中註冊該接口;
  7. 應 用程序中使用 QPluginLoader 來加載插件;
  8. 用 qobject_cast()來測試插件是否實現了給定的接口

上代碼

插件類:

新建一個空的項目:New Project->其他項目->Empty qmake Project

添加自定義的項目名,此時我的是PlugApp_1

添加一個C++的類,名稱爲myPlugin,內容爲

myPlugin.h  

其實的myplugin.json隨意設置一個內容爲空的文件即可 

#ifndef MYPLUGIN_H
#define MYPLUGIN_H

#include <QObject>
#include "widgetPluginTest/plugininterface.h"

/* 1、創建一個插件類,它需要同時繼承自QObject類和該插件所提供的功能對應的接口類 */
class myPlugin : public QObject,PluginInterface
{
public:
    Q_OBJECT
    /* 2、在Qt的元對象系統中註冊該接口 */
    Q_INTERFACES(PluginInterface)

    /* 3、聲明該插件的元數據,其實必須指明IID標識符,標識符必須保證它的唯一性 */
    Q_PLUGIN_METADATA(IID "qt.myplugin.test" FILE "myplugin.json")
    /* 4、聲明瞭一個 print 函數, 它是在 PluginInterface 中定 義的一個純虛函數。 這裏通過重寫 它來實現該插件具體的功能*/
    void print(const QString &message);
};

#endif // MYPLUGIN_H

myPlugin.c

#include "myplugin.h"
#include <QtPlugin>
#include <QDebug>
void myPlugin::print(const QString &message)
{
    qDebug() << "login print,message: " << message;
}

PlugApp_1.pro

TEMPLATE = lib /* 表明該項目要構建 庫文件 */
CONFIG += plugin /* 告知 qmake 要創建一個 插件 */ 
HEADERS += \
    myplugin.h

SOURCES += \
    myplugin.cpp
INCLUDEPATH += wingetPluginTest /* 插件接口類的路徑添加到INCLUDEPATH */  
TARGET = myplugin /* TARGET 指定了產生的 dll 文件的 名字 */ 
DESTDIR = plugins /* DESTDIR 指定了生成的 dll 文件所在的目錄 */ 

插件類已經結束了,使 用插件擴展應用程序

第一步,新建 Qt Widgets 應用。項目名稱爲 widgetPluginTest選擇路徑時仍選擇前面建立的 PlugApp_1 目錄。基類選擇 QWidgetÿ類名保持 Widget 不變。建立完成後,向該項目中添加新文件,模 板選擇 C++頭文件,名 稱爲 PluginInterface.h
第二步,定義接口。將 PluginInterface. h 文件的內容更 改如下:

#ifndef PLUGININTERFACE_H
#define PLUGININTERFACE_H

#include <QString>
/* 這個類只能包含純虛函數 */
class PluginInterface
{

public:
    virtual ~PluginInterface() {}
    virtual void print(const QString &message) = 0;
};
/*  Qt 元對象系統中註冊了該接口 */
Q_DECLARE_INTERFACE(PluginInterface,"qt.myplugin.test")
#endif // PLUGININTERFACE_H

第三步,編寫調用插件應用程序 

widget.h中

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include "plugininterface.h"
class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();
private:
    PluginInterface *myPluginInterface;
    bool loadPlugin();
};

#endif // WIDGET_H

widget.c中

#include "widget.h"
#include <QPluginLoader>
#include <QMessageBox>
#include <QDir>
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    if (!loadPlugin()) { // 如果無法加載插件
        QMessageBox::information(this, "Error", "Could not load the plugin");
    }
}

Widget::~Widget()
{

}

bool Widget::loadPlugin()
{
    QDir pluginsDir("../plugins");

    // 遍歷插件目錄
    foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
        /* 2.3、在應用程序中使用pluginLoader來加載插件 */
        QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
        qDebug() << fileName;
        QObject *plugin = pluginLoader.instance();
        if (plugin) {
            /* 2.4、測試插件是否實現了給定的接口 */
            myPluginInterface = qobject_cast<PluginInterface *>(plugin);
            if (myPluginInterface){
                myPluginInterface->print("hello world!!!");
                return true;
            }
            pluginLoader.unload();
        }
    }
    return false;
}

第四步,運行程序。 先構建 myplugin 項目,在 編輯模式左側項目列 表中的 myplugin 目錄上右擊,在彈出的級聯菜單中選擇“ 構建”。構建完成後,會生成plugins 目錄,裏面包含了生成的 dll 文件。然後運行應用程序 
注:Qt文檔中說明,QPluginLoader 釋放之後,其獲取到的插件不會釋放,所以,需要手動 delete 插件,或者使用 unload() 方法釋放。

Qt的插件系統歸根到底還是 dll 的動態調用時獲取其中的類那一招,dll只可以導出函數,不可以導出指針,但是爲了能得到dll中的類,可以導出一個接口,使用接口獲取對象指針。但是在dll的調用一方,卻無法識別獲取到的類指針,所以利用C++的多態和繼承的特性來完成。使用抽象類,聲明好抽象接口,將需要導出的類繼承自該抽象類,並實現其聲明的接口。dll的導出方和調用方公用一個抽象類聲明文件,在dll導出函數內部,利用多態,將抽象的類指針new成子類對象,並返回出來,則調用方就可以識別得到的類指針了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章