Qt Plugin創建及調用


轉自:https://blog.csdn.net/yizhou2010/article/details/78261643

示例資源:EchoPluginTest

如果沒有積分,可在這里下載:https://files-cdn.cnblogs.com/files/warmlight/EchoPluginTest.rar

概述
插件是一種遵循一定規范的應用程序接口編寫出來的程序,定位於開發實現應用軟件平台不具備的功能的程序。插件與宿主程序之間通過接口聯系,就像硬件插卡一樣,可以被隨時刪除,插入和修改,所以結構很靈活,容易修改,方便軟件的升級和維護。

Qt中的插件
Qt提供了兩種API用於創建插件:

一種是高階API用於擴展Qt本身的功能:如自定義數據庫驅動,圖像格式,文本編碼,自定義樣式等等;
一種低階API用於擴展Qt應用程序。
本文主要是通過低階API來創建Qt插件,並通過靜態、動態兩種方式來調用插件,即通過應用程序預留四則預算接口,通過插件來定義四則運算的簡單例子。

框架自動搭建
我們利用QT Creator把應用程序和插件的運行框架搭建起來,在EchoPluginTest項目項目新建一個名為EchoPluginApp的應用程序,和一個名為EchoPluginLib的插件。

1、首先創建一個子目錄項目,名為EchoPluginTest

 

 

 

 2、創建應用程序EchoPluginApp

 

 這里我們選擇的是Qt Widgets Application,並命名為EchoPluginApp

 

 3、創建EchoPluginLib插件

 

 選擇:Library –> C++庫,並在“項目介紹和位置”選擇類型為“Qt Plugin”,名稱為“EchoPluginLib”

 

 在類信息中輸入類名:“EchoPlugin”,基類默認為“QGenericPlugin”

 

 

至此,基本框架自動生成完成,我們還需進一步對其進行修改。

應用程序中的插件接口創建

在子項目EchoPluginApp中添加一個echointerface.h文件,在該頭文件中定義插件接口,應用程序正是通過這些接口來實現額外的功能。

 1 class EchoInterface
 2 {
 3 public:
 4     virtual ~EchoInterface() {}
 5 
 6     virtual QStringList CalculateType(void) const =0;
 7     virtual double Calculate(QString &type,double xvar, double yvar) =0;
 8 };
 9 #define EchoInterface_iid "EchoPluginTest.EchoPluginLib.EchoInterface"
10 Q_DECLARE_INTERFACE(EchoInterface, EchoInterface_iid)

EchoInterface 類定義了兩個純虛函數,第一個函數CalculateType() 返回QStringList 用於標識插件中定義的運算類型,之所以采用 QStringList 是因為一個插件中可能存在多個運算類型。第二個函數Calculate(),用於根據傳入的運算類型和運算參數xvar和yvar來返回計算值。
為了能夠在運行時查詢插件是否實現給定的接口,我們必須使用宏Q_DECLARE_INTERFACE(),該宏的第一參數為接口類的名稱,第二個參數是一個字符串,用於唯一標記該接口類。

應用程序中插件接口的使用
在應用程序中,首先我們需要定義loadPlugins() 用於查找插件

1 void MainWindow::loadPlugins()
2 {
3     //調用靜態插件
4     foreach (QObject *plugin, QPluginLoader::staticInstances())
5         AddToCombo(plugin);

首先通過QPluginLoader::staticInstances() 函數尋找靜態插件,並更新到界面中下拉控件中。

接下來,我們去加載動態插件:

 1     //動態調用插件
 2     QDir pluginDir(qApp->applicationDirPath());
 3     if(pluginDir.dirName().toLower() == tr("debug") || pluginDir.dirName().toLower() == tr("release") )
 4     {
 5         pluginDir.cdUp();
 6         pluginDir.cdUp();
 7         pluginDir.cd("plugins");
 8     }
 9 
10     //遍歷當前 文件夾下文件
11     foreach (QString filename, pluginDir.entryList(QDir::Files))
12     {
13         QPluginLoader pluginloader(pluginDir.absoluteFilePath(filename));
14         QObject *plugin = pluginloader.instance();
15         if(plugin != 0)
16             AddToCombo(plugin);
17     }

將變量pluginDir 設置到當前應用程序工作目錄對應的Plugins目錄下,並通過函數entryList 遍歷該目錄下所有的文件,對每個文件利用QPluginLoader 去嘗試加載插件。通過QPluginLoader::instance() 函數去識別由插件返回的QObject對象,因為如果動態鏈接庫不是qt插件或者編譯版本不兼容則,函數將返回空指針。
當函數返回有效插件時,我們將其更新到下拉列表中去。

AddToCombo函數

 1 void MainWindow::AddToCombo(QObject *pplugin)
 2 {
 3     EchoInterface *eInterface = qobject_cast<EchoInterface *>(pplugin);
 4     if(eInterface != 0)
 5     {
 6         QStringList typelist = eInterface->CalculateType();
 7         foreach (QString ctype, typelist)
 8         {
 9             ui->calType->addItem(ctype);
10             PluginItem item;
11             item.text = ctype;
12             item.plugin =pplugin;
13             m_pluginItemList.append(item);
14         }
15     }
16 }

對於每一個插件(不管是動態的,還是靜態的),我們都利用qobject_cast() 函數去檢驗它的接口類,如果為EchoInterface接口則將插件的運算類型添加到下拉列表中去。

下拉列表選擇變化響應槽函數

 1 void MainWindow::on_calType_currentIndexChanged(int index)
 2 {
 3     if(index >=0 && index < m_pluginItemList.size())
 4     {
 5         curType = m_pluginItemList.at(index).text;
 6         m_EchoInterface = qobject_cast<EchoInterface *>(m_pluginItemList.at(index).plugin);
 7     }
 8     else
 9     {
10         curType = tr("");
11         m_EchoInterface = 0;
12     }
13 }

計算按鈕響應槽函數

1 void MainWindow::on_calculate_clicked()
2 {
3     if(m_EchoInterface !=0 )
4     {
5         double value = m_EchoInterface->Calculate(curType, xvar, yvar);
6         ui->resultVal->setValue(value);
7     }
8 }

至此,簡單的應用程序基本完成,下面可以通過預留的接口來開發具備各種運算功能的插件了。

插件開發
在開發插件之前,我們需要對Qt Creator自動生成的插件項目做一些修改,因為其是基於高階應用生成的。
首先,在EchoPluginLib.pro 文件中的DESTDIR改為

DESTDIR =../plugins

其次,在EchoPluginLib.h 文件中將類EchoPlugin 從繼承自public QGenericPlugin改為繼承自public QObject, public EchoInterface,更改宏Q_PLUGIN_METADATA中IID參數為”EchoPluginTest.EchoPluginLib.EchoInterface”,並添加宏Q_INTERFACES,和重寫接口類的虛函數。

 1 class EchoPlugin : public QObject, public EchoInterface
 2 {
 3     Q_OBJECT
 4 #if QT_VERSION >= 0x050000
 5     Q_PLUGIN_METADATA(IID "EchoPluginTest.EchoPluginLib.EchoInterface" FILE "EchoPluginLib.json")
 6 #endif // QT_VERSION >= 0x050000
 7 
 8     Q_INTERFACES(EchoInterface)
 9 
10 public:
11     EchoPlugin(QObject *parent = 0);
12     virtual QStringList CalculateType() const override;
13     virtual double Calculate(QString &type,double xvar, double yvar)override;
14 };

宏Q_INTERFACES是必須的,用於告訴qt的meta-object compiler,插件接口的基礎類,如果沒有這個宏,那么在應用程序中,我們無法使用qobject_cast()去檢測接口類。
宏Q_PLUGIN_METADATA用於導出該插件,必須包含插件的IID參數,和可選的參數(用於指向一個包含插件MetaData的Json文件)。
插件中重寫函數的實現

 1 QStringList EchoPlugin::CalculateType() const
 2 {
 3     return QStringList()<< tr("Add")<<tr("Sub");
 4 }
 5 double EchoPlugin::Calculate(QString &type, double xvar, double yvar)
 6 {
 7     if(type == tr("Add"))
 8         return xvar + yvar;
 9     else if(type == tr("Sub"))
10         return xvar - yvar;
11     else return 0.0;
12 }

至此,一個簡單的插件就開發完成了。

編譯運行

為了項目EchoPluginTest項目編譯子項目的順序(先EchoPluginLib后EchoPluginApp),我們打開EchoPluginTest.pro文件,將其中的SUBDIRS設置如下:

1 SUBDIRS += \
2     EchoPluginLib \
3     EchoPluginApp

編譯並運行EchoPluginTest,應用程序EchoPluginApp中可使用插件EchoPluginLib中定義的加法、減法運算。

 

 

插件的靜態調用
如需靜態調用插件,以上項目需做如下更改:
1. 在子項目EchoPluginLib中,打開EchoPluginLib.pro文件,在CONFIG后添加static:

CONFIG += plugin static

2.在子項目EchoPluginApp中,打開main.cpp文件,添加宏Q_IMPORT_PLUGIN,如

 1 Q_IMPORT_PLUGIN(EchoPlugin)
 2 
 3 int main(int argc, char *argv[])
 4 {
 5     QApplication a(argc, argv);
 6     MainWindow w;
 7     w.show();
 8 
 9     return a.exec();
10 }

3.在子項目EchopluginApp中,打開EchoPluginApp.pro文件中添加插件庫。可在EchopluginApp通過右鍵,選擇“添加庫”–>“外部庫”來自動添加。例如

1 win32: LIBS += -L$$PWD/../../***/plugins/ -lEchoPluginLib
2 
3 INCLUDEPATH += $$PWD/../../***/plugins
4 DEPENDPATH += $$PWD/../../***/plugins

4.在子項目EchopluginApp項目中的loadplugins函數中使用QPluginLoader::staticInstances()函數來加載插件。


更多的資料可參考官網相關例子:Plug & Paint Example, Plug & Paint Basic Tools Example, Plug & Paint Extra Filters Example, Echo Plugin Example。
如果沒有積分,可從這里下載:https://files-cdn.cnblogs.com/files/warmlight/EchoPluginTest.rar


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM