Qt开发技术:Qt的动态静态插件框架介绍和Demo

简介: 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代码




相关文章
|
4天前
|
XML 存储 JSON
技术笔记:Qt基础之配置文件(QSettings)
技术笔记:Qt基础之配置文件(QSettings)
|
4天前
|
调度
技术笔记:QT之深入理解QThread
技术笔记:QT之深入理解QThread
|
12天前
|
关系型数据库 MySQL 项目管理
数据库大作业——基于qt开发的图书管理系统(四)项目目录的整理与绘制登录页面
数据库大作业——基于qt开发的图书管理系统(四)项目目录的整理与绘制登录页面
|
12天前
|
SQL 关系型数据库 MySQL
数据库大作业——基于qt开发的图书管理系统(三)Qt连接Mysql数据库
数据库大作业——基于qt开发的图书管理系统(三)Qt连接Mysql数据库
|
12天前
|
SQL 数据可视化 关系型数据库
数据库大作业——基于qt开发的图书管理系统(二) 相关表结构的设计
数据库大作业——基于qt开发的图书管理系统(二) 相关表结构的设计
|
12天前
|
安全 BI 数据库
数据库大作业——基于qt开发的图书管理系统 (一)环境的配置与项目需求的分析
数据库大作业——基于qt开发的图书管理系统 (一)环境的配置与项目需求的分析
|
2月前
|
区块链
【qt】最快的开发界面效率——混合编程3
【qt】最快的开发界面效率——混合编程
45 1
|
2月前
【qt】最快的开发界面效率——混合编程2
【qt】最快的开发界面效率——混合编程
41 1
|
2月前
|
网络协议 C++
C++ Qt开发:QTcpSocket网络通信组件
`QTcpSocket`和`QTcpServer`是Qt中用于实现基于TCP(Transmission Control Protocol)通信的两个关键类。TCP是一种面向连接的协议,它提供可靠的、双向的、面向字节流的通信。这两个类允许Qt应用程序在网络上建立客户端和服务器之间的连接。Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍如何运用`QTcpSocket`组件实现基于TCP的网络通信功能。
62 8
C++ Qt开发:QTcpSocket网络通信组件
|
2月前
|
算法 编译器 Linux
【Qt4 部署】ARM系统上使用Qt 4 进行开发的QWS 等环境变量部署
【Qt4 部署】ARM系统上使用Qt 4 进行开发的QWS 等环境变量部署
48 0

相关实验场景

更多

推荐镜像

更多