Qt開發技術:Qt的動態靜態插件框架介紹和Demo

若該文爲原創文章,未經允許不得轉載
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客導航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105481285
各位讀者,知識無窮而人力有窮,要麼改需求,要麼找專業人士,要麼自己研究

目錄

前言

創建Qt插件

高級API:編寫Qt擴展(這不是我們當前所關注的,可以略過)

低級API:擴展Qt應用程序(這是我們需要的)

部署插件位置

靜態插件

鏈接靜態插件的詳細信息

創建靜態插件

部署和調試插件

部署插件

插件目錄

動態加載和驗證插件

調試插件

補充:QT_DEBUG_PLUGINS在調試xcb錯誤時,非常重要,請查看

使用qt.conf

格式和位置

重寫路徑

爲平臺插件配置參數

Demo:擴展Qt應用程序動態插件

Demo運行效果

動態插件的pro文件:sharePlugin.pro

動態插件的接口頭文件(只有頭文件):Interface.h

動態插件的具體實現頭文件:SharePlugin.h

動態插件的具體實現源文件:SharePlugin.cpp

測試Demo熱加載關鍵代碼

測試Demo測試代碼

Demo:擴展Qt應用程序靜態插件

Demo運行效果

靜態插件的pro文件:staticPlugin.pro

靜態插件的接口頭文件(只有頭文件):StaticInterface.h

靜態插件的具體實現頭文件:StaticPlugin.h

靜態插件的具體實現源文件:StaticPlugin.cpp

測試Demo加載關鍵代碼

測試Demo測試代碼

工程模板v1.0.0

下載工程模板


Qt開發專欄:開發技術

 

    Qt開發技術:Qt的動態靜態插件框架介紹和Demo

 

前言

Qt的插件化開發框架類似於前後端的微服務的場景,授權哪個微服務則前端可以使用哪個微服務,作爲進一步的貨站,軟件中也可以先出現圖標,點擊後進行動態加載,減少軟件的部署大小同時更重要的是增加了程序的靈活性。

 

創建Qt插件

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

  • 編寫Qt擴展的高級API:自定義數據庫驅動程序、圖像格式、文本編解碼器、自定義樣式等;
  • 用於擴展Qt應用程序的低級API;

例如,如果您想編寫一個定製的QStyle子類並讓Qt應用程序動態加載它,那麼您將使用更高級別的API。

      由於較高級別的API構建在較低級別的API之上,因此兩個API都存在一些共同的問題。

如果您想提供與Qt設計器一起使用的插件,請參閱Qt設計器模塊文檔

      (注意:此處我們的插件主要有研究動態下載加載)

高級API:編寫Qt擴展(這不是我們當前所關注的,可以略過)

編寫擴展Qt本身的插件是通過子類化適當的插件基類、實現一些函數和添加宏來實現的。

有幾個插件基類。默認情況下,派生插件存儲在標準插件目錄的子目錄中。如果插件沒有存儲在適當的目錄中,Qt將找不到它們。

下表總結了插件基類。有些類是私有的,因此沒有文檔記錄。您可以使用它們,但不能保證與更高版本的Qt兼容。

序號

基類

文件夾名

Qt模塊

大小寫

1

QAccessibleBridgePlugin

accessiblebridge

Qt GUI

區分

2

QImageIOPlugin

imageformats

Qt GUI

區分

3

QPictureFormatPlugin (obsolete)

pictureformats

Qt GUI

區分

4

QAudioSystemPlugin

audio

Qt Multimedia

不區分

5

QDeclarativeVideoBackendFactoryInterface

video/declarativevideobackend

Qt Multimedia

不區分

6

QGstBufferPoolPlugin

video/bufferpool

Qt Multimedia

不區分

7

QMediaPlaylistIOPlugin

playlistformats

Qt Multimedia

不區分

8

QMediaResourcePolicyPlugin

resourcepolicy

Qt Multimedia

不區分

9

QMediaServiceProviderPlugin

mediaservice

Qt Multimedia

不區分

10

QSGVideoNodeFactoryPlugin

video/videonode

Qt Multimedia

不區分

11

QBearerEnginePlugin

bearer

Qt Network

區分

12

QPlatformInputContextPlugin

platforminputcontexts

Qt Platform

 Abstraction

不區分

13

QPlatformIntegrationPlugin

platforms

Qt Platform

Abstraction

不區分

14

QPlatformThemePlugin

platformthemes

Qt Platform Abstraction

不區分

15

QGeoPositionInfoSourceFactory

position

Qt Positioning

區分

16

QPlatformPrinterSupportPlugin

printsupport

Qt Print

 Support

不區分

17

QSGContextPlugin

scenegraph

Qt Quick

區分

18

QScriptExtensionPlugin

script

Qt Script

區分

19

QSensorGesturePluginInterface

sensorgestures

Qt Sensors

區分

20

QSensorPluginInterface

sensors

Qt Sensors

區分

21

QSqlDriverPlugin

sensors

Qt SQL

區分

22

QIconEnginePlugin

iconengines

Qt SVG

不區分

23

QAccessiblePlugin

accessible

Qt Widgets

區分

24

QStylePlugin

styles

Qt Widgets

不區分

如果有一個名爲MyStyle的新樣式類要作爲插件提供,則該類需要定義如下(MyStyle plugin.h):

class MyStylePlugin : public QStylePlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID \
                        "org.qt-project.Qt.QStyleFactoryInterface" \
                        FILE
                        "mystyleplugin.json")
public:
    QStyle *create(const QString &key);
};

確保類實現位於.cpp文件中:

#include "mystyleplugin.h"
QStyle *MyStylePlugin::create(const QString &key)
{
    if (key.toLower() == "mystyle")
        return new MyStyle;
    return 0;
}

(請注意,QStylePlugin不區分大小寫,create()實現中使用了該鍵的小寫版本;大多數其他插件區分大小寫。)

此外,大多數插件都需要一個包含描述插件的元數據的json文件(mystyleplugin.json)。對於樣式插件,它只包含插件可以創建的樣式列表:

json文件中需要提供的信息類型依賴於插件,有關需要包含在文件中的信息的詳細信息,請查看具體的類描述。

對於數據庫驅動程序、圖像格式、文本編解碼器和大多數其他插件類型,不需要顯式創建對象。Qt將根據需要找到並創建它們。樣式是一個例外,因爲您可能希望在代碼中顯式設置樣式。要應用樣式,請使用以下代碼:

QApplication::setStyle(QStyleFactory::create("MyStyle"));

一些插件類需要實現額外的函數。有關必須爲每種插件重新實現的虛擬函數的詳細信息,請參閱類文檔。

樣式插件示例演示如何實現擴展QStylePlugin基類的插件。

低級API:擴展Qt應用程序(這是我們需要的)

不僅Qt本身,Qt應用程序也可以通過插件進行擴展。這需要應用程序使用QPluginLoader檢測和加載插件。在這種情況下,插件可以提供任意的功能,並且不限於數據庫驅動程序、圖像格式、文本編碼、樣式和擴展Qt功能的其他類型的插件。

通過插件使應用程序可擴展包括以下步驟:

  • 步驟一:定義一組用於與插件對話的接口(只有純虛擬函數的類,在調用插件時就是使用給該接口類實例調用接口);
  • 步驟二:使用Q_DECLARE_INTERFACE()宏告訴Qt的元對象系統有關該接口的信息;
  • 步驟三:在應用程序中,使用QPluginLoader加載插件;
  • 步驟四:使用qobject_cast()測試插件是否實現給定接口;

例如,下面是接口類的定義:

class FilterInterface
{
public:
    virtual ~FilterInterface() {}

    virtual QStringList filters() const = 0;
virtual QImage filterImage(const QString &filter,
                           const QImage &image,
                           QWidget *parent) = 0;
};

下面是實現該接口的插件類的定義:

#include <QObject>
#include <QtPlugin>
#include <QStringList>
#include <QImage>

#include <plugandpaint/interfaces.h>

class ExtraFiltersPlugin : public QObject, public FilterInterface
{
Q_OBJECT
    Q_PLUGIN_METADATA(IID \
                        "org.qt-project.Qt.Examples.PlugAndPaint.FilterInterface" \
                        FILE \
                        "extrafilters.json")
    Q_INTERFACES(FilterInterface)

public:
QStringList filters() const; 
    QImage filterImage(const QString &filter,
                     const QImage &image,
                     QWidget *parent);
};

必須先初始化QCoreApplication,然後才能加載插件。

部署插件位置

Qt應用程序自動知道哪些插件可用,因爲插件存儲在標準插件子目錄中。因爲這個應用程序不需要任何代碼來查找和加載插件,因爲Qt會自動處理它們。

在開發過程中,插件的目錄是QTDIR/plugins(其中QTDIR是Qt的安裝目錄),每種類型的插件都位於該類型的子目錄中,例如styles。

如果您希望應用程序使用插件,但不希望使用標準插件路徑,請讓安裝過程確定要用於插件的路徑,並保存路徑,例如,使用QSettings,以便應用程序在運行時讀取。然後,應用程序可以使用此路徑調用QCoreApplication::addLibraryPath(),應用程序將可以使用您的插件。請注意,路徑的最後部分(例如,樣式)不能更改。

如果您希望插件可加載,那麼一種方法是在應用程序下創建一個子目錄,並將插件放在該目錄中。如果分發Qt附帶的任何插件(位於plugins目錄中的插件),則必須將插件所在的插件下的子目錄複製到應用程序根文件夾(即,不包括插件目錄)。

靜態插件

將插件包含在應用程序中的最常規和最靈活的方法是將其編譯到單獨提供、在運行時檢測和加載的動態庫中。

插件可以靜態地鏈接到應用程序中。如果您構建Qt的靜態版本,這是包含Qt的預定義插件的唯一選項。使用靜態插件使部署不易出錯,但有一個缺點,即如果不完全重建和重新分發應用程序,就無法添加插件的任何功能。

要靜態鏈接插件,需要使用QTPLUGIN將所需的插件添加到構建中。

在應用程序的.pro文件中,需要以下條目:

QTPLUGIN + = qjpeg \
             qgif \
             qkrcodecs

make會自動將QTPLUGIN中使用的Qt模塊通常需要的插件添加到QTPLUGIN中,而更多專用插件需要手動添加。每個類型都可以覆蓋自動添加的插件的默認列表。例如,要鏈接最小插件而不是默認的Qt平臺適配插件,使用:

QTPLUGIN.platforms = qminimal

如果既不希望默認的,也不希望最小的QPA插件自動鏈接,使用:

QTPLUGIN.platforms = -

默認設置被調整爲最佳的開箱即用體驗,但可能會不必要地使應用程序膨脹。建議檢查qmake構建的鏈接器命令行,並消除不必要的插件。

鏈接靜態插件的詳細信息

要使靜態插件真正被鏈接和實例化,應用程序代碼中還需要Q_IMPORT_PLUGIN()宏,但這些宏是由qmake自動生成並添加到應用程序項目中的。

如果不希望自動鏈接添加到QTPLUGIN的所有插件,請從配置變量中刪除import_plugins:

CONFIG -= import_plugins

創建靜態插件

可以通過以下步驟創建自己的靜態插件:

  • 步驟一:將CONFIG+=static添加到插件的.pro文件中。
  • 步驟二:在應用程序中使用Q_IMPORT_PLUGIN()宏。
  • 步驟三:如果插件附帶qrc文件,請在應用程序中使用Q_INIT_RESOURCE()宏。
  • 步驟四:使用.pro文件中的LIBS將應用程序與插件庫鏈接。

注意:如果不使用qmake構建插件,則需要確保已定義QT_STATICPLUGIN預處理器宏。

 

部署和調試插件

部署插件

部署Qt或應用程序在運行時應加載的插件庫。如果使用靜態插件,那麼插件代碼已經是應用程序可執行文件的一部分,不需要單獨的部署步驟。

插件目錄

當應用程序運行時,Qt首先將應用程序的可執行目錄作爲搜索插件的基本目錄。例如,如果應用程序位於C:\ Program Files\MyApp中並且有一個樣式插件,Qt將在C:\ Program Files\MyApp\styles中查找。(具體可查看QCoreApplication::applicationDirPath(),找到應用程序的可執行文件)。Qt還將查找QLibraryInfo::location(QLibraryInfo::PluginsPath)指定的目錄,該目錄通常位於QTDIR/plugins(其中QTDIR是Qt的安裝目錄)。

如果希望Qt在其他地方查找,可以通過調用QCoreApplication::addLibraryPath()添加所需的路徑。如果要設置自己的路徑,可以使用QCoreApplication::setLibraryPaths()。還可以使用qt.conf文件覆蓋編譯到qt庫中的硬編碼路徑。還有一種可能是在運行應用程序之前設置QT_PLUGIN_PATH環境變量。如果設置了,Qt將在變量中指定的路徑(由系統路徑分隔符分隔)中查找插件。

注意:QT_PLUGIN_PATH不應作爲系統範圍的環境變量導出,因爲它可能會干擾其他QT安裝。

動態加載和驗證插件

加載插件時,Qt庫會進行一些健全性檢查,以確定插件是否可以加載和使用。這提供了並行安裝Qt庫的多個版本和配置的能力。

  • 與版本號較高的Qt庫鏈接的插件不會被版本號較低的庫加載;

例如:Qt 5.0.0 不會加載 Qt 5.0.1編譯的插件;

  • 與主版本號較低的Qt庫鏈接的插件不會由主版本號較高的庫加載;

例如:Qt 5.0.1 不會加載 Qt 4.8.2編譯的插件;

例如:Qt 5.1.1 不會加載 Qt 5.1.0和Qt 5.0.3編譯的插件;

在構建插件以擴展應用程序時,確保插件的配置方式與應用程序相同非常重要。這意味着如果應用程序是以發佈模式構建的,那麼插件也應該以發佈模式構建。除了Unix操作系統,插件系統不會加載以不同模式構建的插件。

如果您將Qt配置爲在調試和發佈模式下構建,但只在發佈模式下構建應用程序,則需要確保插件也在發佈模式下構建。默認情況下,如果Qt的調試版本可用,則插件將僅在調試模式下生成。要強制插件以發佈模式構建,請將以下行添加到插件的項目文件中:

CONFIG += release

這將確保插件與應用程序中使用的庫版本兼容。

調試插件

有許多問題可能會阻止正確編寫的插件與設計用來使用它們的應用程序一起工作。其中許多都與插件和應用程序構建方式的差異有關,這些差異通常是由獨立的構建系統和過程引起的。

下表描述了開發人員在創建插件時遇到的常見問題的原因:

問題

原因

解決方法

即使被應用程序直接打開,插件也無法加載。Qt設計器在其"幫助|關於插件"對話框中顯示插件庫,但每個插件下都沒有列出插件。

應用程序及其插件以不同的模式構建。

要麼共享相同的生成信息,要麼在調試和發佈模式下通過將調試和發佈附加到每個項目文件中的CONFIG變量來生成插件。

您還可以使用QT_DEBUG_PLUGINS環境變量從QT獲取有關試圖加載的每個插件的診斷信息。在啓動應用程序的環境中將此變量設置爲非零值。

補充:QT_DEBUG_PLUGINS在調試xcb錯誤時,非常重要,請查看

Qt實用技巧:ubuntu發佈程序打包流程(解決插件xcb加載失敗)》博文中的“步驟四:運行調試插件(點擊傳送門)”。

使用qt.conf

可以使用qt.conf文件覆蓋路徑或指定要傳遞給平臺插件的參數。

格式和位置

qt.conf文件是一個INI文本文件,如QSettings文檔中所述。

QLibraryInfo將從以下位置之一加載qt.conf:

  • 路徑一::/qt/etc/qt.conf使用資源系統
  • 路徑二:在macOS上,在應用程序包內的資源目錄中,例如assistant.app/Contents/Resources/qt.conf
  • 路徑三:在包含應用程序可執行文件的目錄中,即QCoreApplication::applicationDirPath()+QDir::separator()+"qt.conf"

重寫路徑

conf文件可用於重寫編譯到qt庫中的硬編碼路徑。可以使用QLibraryInfo類訪問這些路徑。如果沒有qt.conf,QLibraryInfo中的函數將返回這些硬編碼的路徑;否則,它們將返回qt.conf中指定的路徑。

如果沒有qt.conf,qt庫將使用硬編碼路徑來查找插件、翻譯等。目標系統上可能不存在這些路徑,或者它們不可訪問。因此,您可能需要qt.conf來使qt庫在其他地方查找。

文件應該有一個路徑組,其中包含與QLibraryInfo::LibraryLocation枚舉的每個值對應的條目。有關各個位置含義的詳細信息,請參閱QLibraryInfo文檔。

序號

關鍵字

默認值

1

Prefix

QCoreApplication::applicationDirPath()

2

Documentation

doc

3

Headers

include

4

Libraries

lib

5

LibraryExecutables

libexec

6

Binaries

bin

7

Plugins

plugins

8

Imports

imports

9

Qml2Imports

qml

10

ArchData

.

11

Data

.

12

Translations

translations

13

Examples

examples

14

Tests

tests

15

Settings

.

絕對路徑按qt.conf文件中的指定使用。所有路徑都與前綴相關。在Windows和X11上,前綴是相對於包含應用程序可執行文件QCoreApplication::applicationDirPath()的目錄的。在macOS上,前綴與應用程序包中的內容相關。例如,application.app/Contents/plugins/是加載Qt插件的默認位置。

注意,插件需要放在plugins目錄下的特定子目錄中(詳細信息請參見“創建Qt插件”->“部署插件位置””)。

例如,qt.conf文件可以包含以下內容:

注意:在INI文件中,反斜槓字符被視爲特殊字符(請參見QSettings)。因此,建議對Windows上的路徑也使用正斜槓。否則,需要轉義字符:

[Paths]
Prefix = /some/path
Translations = i18n

注意:在INI文件中,反斜槓字符被視爲特殊字符(請參見QSettings)。因此,建議對Windows上的路徑也使用正斜槓。否則,需要轉義字符:

爲平臺插件配置參數

conf可以包含一個平臺組,其鍵是要傳遞給平臺插件的以逗號分隔的參數列表。密鑰名是平臺插件的名稱,第一個字母大寫,後跟參數。

例如:

[Platforms]
WindowsArguments = fontengine=freetype

 

Demo:擴展Qt應用程序動態插件

Demo運行效果

動態插件的pro文件:sharePlugin.pro

TEMPLATE      = lib
# 動態庫的
CONFIG       += plugin
# 靜態庫的
#CONFIG       += plugin static
QT           += widgets
TARGET        = $$qtLibraryTarget(sharePlugin)
DESTDIR       = ../../plugins

HEADERS += \
    Interface.h \
    SharePlugin.h

SOURCES += \
    SharePlugin.cpp

動態插件的接口頭文件(只有頭文件):Interface.h

#ifndef INTERFACE_H
#define INTERFACE_H

#include <QString>
#include <QtPlugin>

class Interface
{
public:
    virtual ~Interface(){}

    virtual QString getSerailData() = 0;                // 序列化
    virtual void setSerailData(const QString data) = 0; // 序列化設置

    virtual void show() = 0;                            // 顯示窗口
};

#define Interface_iid "MyPlugin.Interface"

Q_DECLARE_INTERFACE(Interface, Interface_iid)

#endif // INTERFACE_H

動態插件的具體實現頭文件:SharePlugin.h

#ifndef SHAREPLUGIN_H
#define SHAREPLUGIN_H

/************************************************************\
 * 控件名稱:SharePlugin
 * 控件描述:動態插件模板,生成插件部分
 * 作者:紅模仿    聯繫方式:QQ21497936
 * 博客地址:https://blog.csdn.net/qq21497936
 *       日期             版本               描述
 *   2020年04月10日      v1.0.0            基礎模板
\************************************************************/

#include <QObject>
#include <QtPlugin>
#include "Interface.h"

class SharePlugin : public QObject, public Interface
{
    Q_OBJECT

    Q_INTERFACES(Interface)                         // Q_INTERFACES宏用於告訴Qt該類實現的接口。
    Q_PLUGIN_METADATA(IID Interface_iid)            // Q_PLUGIN_METADATA宏用於描述插件元數據

public:
    explicit SharePlugin(QObject *parent = 0);

    virtual QString getSerailData();                // 序列化
    virtual void setSerailData(const QString data); // 序列化設置

    virtual void show();                            // 顯示窗口

private:
    QString _data;                                  // 序列化
};


#endif // SHAREPLUGIN_H

動態插件的具體實現源文件:SharePlugin.cpp

#include "SharePlugin.h"
#include <QDebug>
#include <QWidget>

SharePlugin::SharePlugin(QObject *parent)
    : QObject(parent),
    _data("null")
{

}

void SharePlugin::setSerailData(const QString data)
{
    _data = data;
    qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << _data;
}

QString SharePlugin::getSerailData()
{
    qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << _data;
    return _data;
}

void SharePlugin::show()
{
    QWidget *pWidget = new QWidget;
    pWidget->setWindowTitle("test SharePlugin");
    pWidget->show();
}

測試Demo熱加載關鍵代碼

bool Widget::loadPlugin()
{
    // 熱加載插件
    // 獲取當前應用程序所在的路徑
    QDir pluginsDir(qApp->applicationDirPath());
    // 切換到插件路徑(判斷是否輸出到debug和release,目前是在pro中直接輸出到../../app目錄)
    if(pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
    {
        // 返回上一級目錄
        pluginsDir.cdUp();
    }
    // 切換到插件目錄
    pluginsDir.cdUp();
    pluginsDir.cdUp();
    pluginsDir.cd("plugins");
    // 打開對應的插件
    // 引入靜態插件需要將靜態編譯的庫,編譯的時候就要引入進去
    // 實測靜態插件,debug調用release版本插件可行,反過來也可行
    // 實測動態插件,debug調用release版本插件不可行,反過來也不可行
    QString pluginDllName = "sharePlugin.dll";    // release版本插件(注意:release下只能加載release版本的插件)
//    QString pluginDllName = "sharePlugind.dll";     // debug版本插件(注意:debug下只能加載debug版本的插件)
    QString dllPath = pluginsDir.absolutePath() + QDir::separator() + pluginDllName;
    if(!QFile::exists(dllPath))
    {
        qDebug() << __FILE__ << __LINE__
                 << "Not exist file:" << dllPath;
        return false;
    }
    // 加載插件
    QPluginLoader pluginLoader(dllPath);
    pluginLoader.load();
    qDebug() << __FILE__ << __LINE__ << pluginLoader.isLoaded();
    QObject *pPlugin = pluginLoader.instance();

    if(pPlugin)
    {
        QString pluginName = pPlugin->metaObject()->className();
        qDebug() << __FILE__ << __LINE__ << "Succeed to get plugin class Name:" << pluginName;
        //對插件初始化
        if(pluginName == "SharePlugin")
        {
           _pInterface = qobject_cast<Interface *>(pPlugin);
           if (_pInterface)
           {
                qDebug() << __FILE__ << __LINE__ << "Succeed to load SharePlugin";
           }else{
                qDebug() << __FILE__ << __LINE__ << "Failed to load SharePlugin";
           }
        }
    }else{
        qDebug() << __FILE__ << __LINE__ << "Failed to load plugin:" << dllPath;
        return false;
    }
    return true;
}

測試Demo測試代碼

void Widget::on_pushButton_testSharePlugin_clicked()
{
    if(_pInterface)
    {
        qDebug() << __FILE__ << __LINE__ << _pInterface->getSerailData();
        _pInterface->setSerailData("hello world!!!");
        qDebug() << __FILE__ << __LINE__ << _pInterface->getSerailData();
        _pInterface->show();
    }
}

 

Demo:擴展Qt應用程序靜態插件

Demo運行效果

靜態插件的pro文件:staticPlugin.pro

TEMPLATE      = lib
# 動態庫的
#CONFIG       += plugin
# 靜態庫的
CONFIG       += plugin static
QT           += widgets
TARGET        = $$qtLibraryTarget(staticPlugin)
DESTDIR       = ../../plugins

HEADERS += \
    StaticInterface.h \
    StaticPlugin.h

SOURCES += \
    StaticPlugin.cpp

靜態插件的接口頭文件(只有頭文件):StaticInterface.h

#ifndef STATICINTERFACE_H
#define STATICINTERFACE_H

#include <QString>
#include <QtPlugin>

class StaticInterface
{
public:
    virtual ~StaticInterface(){}

    virtual QString getSerailData() = 0;                // 序列化
    virtual void setSerailData(const QString data) = 0; // 序列化設置

    virtual void show() = 0;                            // 顯示窗口
};

#define StaticInterface_idd "MyPlugin.StaticInterface"

Q_DECLARE_INTERFACE(StaticInterface, StaticInterface_idd)

#endif // STATICINTERFACE_H

靜態插件的具體實現頭文件:StaticPlugin.h

#ifndef STATICPLUGIN_H
#define STATICPLUGIN_H

/************************************************************\
 * 控件名稱:StaticPlugin
 * 控件描述:靜態插件模板,生成插件部分
 * 作者:紅模仿    聯繫方式:QQ21497936
 * 博客地址:https://blog.csdn.net/qq21497936
 *       日期             版本               描述
 *   2020年04月10日      v1.0.0            基礎模板
\************************************************************/

#include <QObject>
#include <QtPlugin>
#include <StaticInterface.h>

class StaticPlugin : public QObject, public StaticInterface
{
    Q_OBJECT


    Q_INTERFACES(StaticInterface)                   // Q_INTERFACES 宏用於告訴Qt該類實現的接口。
    Q_PLUGIN_METADATA(IID StaticInterface_idd)      // Q_PLUGIN_METADATA宏用於描述插件元數據

public:
    explicit StaticPlugin(QObject *parent = 0);

    virtual QString getSerailData();                // 序列化
    virtual void setSerailData(const QString data); // 序列化設置

    virtual void show();                            // 顯示窗口

private:
    QString _data;                                  // 序列化
};

#endif // STATICPLUGIN_H

靜態插件的具體實現源文件:StaticPlugin.cpp

#include "StaticPlugin.h"
#include <QDebug>
#include <QWidget>

StaticPlugin::StaticPlugin(QObject *parent)
    : QObject(parent),
      _data("null")
{

}

void StaticPlugin::setSerailData(const QString data)
{
    _data = data;
    qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << _data;
}

QString StaticPlugin::getSerailData()
{
    qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << _data;
    return _data;
}

void StaticPlugin::show()
{
    QWidget *pWidget = new QWidget;
    pWidget->setWindowTitle("test StaticPlugin");
    pWidget->show();
}

測試Demo的pro工程文件:testStaticDemo.pro
#-------------------------------------------------
#
# Project created by QtCreator 2020-04-09T15:36:39
#
#-------------------------------------------------

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = testDemo
TEMPLATE = app

#DESTDIR  = ../../app

# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0


SOURCES += \
        main.cpp \
        StaticWidget.cpp

HEADERS += \
        StaticWidget.h

FORMS += \
        StaticWidget.ui

# 引入靜態插件需要將靜態編譯的庫,編譯的時候就要引入進去
# 實測靜態插件,debug調用release版本插件可行,反過來也可行
# 實測動態插件,debug調用release版本插件不可行,反過來也不可行
CONFIG(debug, debug|release) {
    LIBS += -L../../plugins
    LIBS += -lstaticPlugind
} else {
    LIBS += -L../../plugins
    LIBS += -lstaticPlugin
}

測試Demo加載關鍵代碼

bool StaticWidget::loadStaticPlugin()
{
    bool ret = false;
    for(int index = 0; index < QPluginLoader::staticInstances().size(); index++)
    {
        QObject *pStaticPlugin = QPluginLoader::staticInstances().at(index);
        _pStaticInterface = qobject_cast<StaticInterface *>(pStaticPlugin);
       if (_pStaticInterface)
       {
            qDebug() << __FILE__ << __LINE__ << "Succeed to load StaticInterface:" << pStaticPlugin->metaObject()->className();
            ret = true;
       }
    }
    if(!ret)
    {
        qDebug() << __FILE__ << __LINE__ << "Failed to load StaticInterface";
        return false;
    }
    return true;
}

測試Demo測試代碼

void StaticWidget::on_pushButton_testStaticPlugin_clicked()
{
    if(_pStaticInterface)
    {
        qDebug() << __FILE__ << __LINE__ << _pStaticInterface->getSerailData();
        _pStaticInterface->setSerailData("hello world!!!");
        qDebug() << __FILE__ << __LINE__ << _pStaticInterface->getSerailData();
        _pStaticInterface->show();
    }
}

 

工程模板v1.0.0

插件的工程模板v1.0.0,包含動態q插件模板和靜態插件模板以及各自的測試Demo代碼

 

下載工程模板

CSDN:https://download.csdn.net/download/qq21497936/12324131

QQ羣:1047134658(點擊“文件”搜索“qtPluginDemo”,羣內與博文同步更新所有可開源的源碼模板)

 

原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客導航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105481285

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