如何创建 Qt 插件

Qt 为创建插件提供了 2 个 API:

  • 用于编写 Qt 自身扩展的高级 API:自定义数据库驱动程序、图像格式、文本编解码器、自定义风格、等。
  • 用于扩展 Qt 应用程序的低级 API。

例如:若想要编写自定义 QStyle 子类并让 Qt 应用程序动态加载它,将使用更高级 API。

由于更高级 API 建立在更低级 API 之上,因此有一些问题是两者公共的。

若想要提供用于 Qt Designer 的插件,见 Qt Designer 模块文档编制。

话题:

高级 API:编写 Qt 扩展

编写扩展 Qt 本身的插件是通过子类化适当插件基类、实现一些函数、及添加宏达成的。

有几个插件基类。默认情况下,派生插件存储在标准插件目录的子目录下。Qt 将找不到插件,若未将它们存储在适当目录下。

下表汇总了插件基类。某些类是私有的,因此未文档化。可以使用它们,但不承诺兼容更高 Qt 版本。

基类 目录名称 Qt 模块 键区分大小写
QAccessibleBridgePlugin accessiblebridge Qt GUI 区分大小写
QImageIOPlugin imageformats Qt GUI 区分大小写
QPictureFormatPlugin (obsolete) pictureformats Qt GUI 区分大小写
QAudioSystemPlugin audio Qt Multimedia 不区分大小写
QDeclarativeVideoBackendFactoryInterface video/declarativevideobackend Qt Multimedia 不区分大小写
QGstBufferPoolPlugin video/bufferpool Qt Multimedia 不区分大小写
QMediaPlaylistIOPlugin playlistformats Qt Multimedia 不区分大小写
QMediaResourcePolicyPlugin resourcepolicy Qt Multimedia 不区分大小写
QMediaServiceProviderPlugin mediaservice Qt Multimedia 不区分大小写
QSGVideoNodeFactoryPlugin video/videonode Qt Multimedia 不区分大小写
QBearerEnginePlugin bearer Qt Network 区分大小写
QPlatformInputContextPlugin platforminputcontexts Qt Platform Abstraction 不区分大小写
QPlatformIntegrationPlugin platforms Qt Platform Abstraction 不区分大小写
QPlatformThemePlugin platformthemes Qt Platform Abstraction 不区分大小写
QGeoPositionInfoSourceFactory 位置 Qt Positioning 区分大小写
QPlatformPrinterSupportPlugin printsupport Qt Print Support 不区分大小写
QSGContextPlugin scenegraph Qt Quick 区分大小写
QScriptExtensionPlugin script Qt Script 区分大小写
QSensorGesturePluginInterface sensorgestures Qt Sensors 区分大小写
QSensorPluginInterface sensors Qt Sensors 区分大小写
QSqlDriverPlugin sqldrivers Qt SQL 区分大小写
QIconEnginePlugin iconengines Qt SVG 不区分大小写
QAccessiblePlugin accessible Qt Widgets 区分大小写
QStylePlugin styles Qt Widgets 不区分大小写

若有新样式类称为 MyStyle 想要将其用作插件,则需要按以下方式定义类 ( mystyleplugin.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 ) 包含大多数插件所需的插件描述元数据。对于样式插件,它仅仅包含可以由插件创建的样式列表:

{ "Keys": [ "mystyleplugin" ] }
					

在 JSON 文件中需要提供的信息类型取决于插件,请参阅类文档编制了解在文件中需要包含信息的有关细节。

对于数据库驱动程序、图像格式、文本编解码器及大多数其它插件类型,明确创建对象不是必需的。Qt 将根据需要查找并创建它们。样式例外,由于可能想要在代码中明确设置样式。要应用样式,使用代码像这样:

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

某些插件类要求实现其它功能。见类文档编制,了解各插件类型必须重实现的虚函数的有关细节。

样式插件范例 shows如何实现插件以扩展 QStylePlugin 基类。

低级 API:扩展 Qt 应用程序

不只 Qt 本身,Qt 应用程序还可以被扩展透过插件。这要求应用程序检测并加载插件,使用 QPluginLoader 。在这种情况下,插件可以提供任意功能,不限于数据库驱动程序、图像格式、文本编解码器、样式及扩展 Qt 功能的其它类型插件。

透过插件使应用程序可扩展,涉及以下步骤:

  1. 定义一组用于对话插件的接口 (仅具有纯虚函数的类)。
  2. 使用 Q_DECLARE_INTERFACE () 宏告诉 Qt 的 元对象系统 关于接口。
  3. 使用 QPluginLoader 在应用程序中加载插件。
  4. Use qobject_cast() to test whether a plugin implements a given interface.

编写插件涉及这些步骤:

  1. 声明插件类继承 QObject 及插件想要提供的接口。
  2. 使用 Q_INTERFACES () 宏告诉 Qt 的 元对象系统 关于接口。
  3. 导出插件使用 Q_PLUGIN_METADATA () 宏。
  4. 构建插件使用合适 .pro 文件。

例如,这里是接口类的定义:

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);
};
					

插件和描绘 范例文档编制会详细阐述此过程。另请参阅 创建自定义 Widget 为 Qt Designer 了解 Qt Designer 特定问题的有关信息。还可以查看 回显插件范例 ,这是有关如何实现扩展 Qt 应用程序插件的更通俗范例。请注意: QCoreApplication 必须已初始化,在可以加载插件之前。

定位插件

Qt 应用程序自动知道哪些插件可用,因为插件存储在标准插件子目录下。应用程序不要求采用任何代码查找和加载插件,由于 Qt 会自动处理它们。

在开发期间,插件目录为 QTDIR/plugins (在哪里 QTDIR 是 Qt 的安装目录),各种类型的插件在该类型的子目录下,例如, styles 。若想要应用程序使用插件但又不想使用标准插件路径,让安装进程确定想要使用的插件路径并保存路径,例如,通过使用 QSettings ,供应用程序运行时读取。然后,应用程序可以调用 QCoreApplication::addLibraryPath () 采用此路径,您的插件将可用于应用程序。注意,最后部分的路径 (例如, styles ) 无法更改。

若想要插件可加载,一种途径是在应用程序下创建子目录,并将插件放置于该目录下。若分发 Qt 自带的任何插件 (其位于 plugins 目录),必须拷贝其子目录在 plugins 插件,位于应用程序根文件夹下 (即:不包括 plugins 目录)。

有关部署的更多信息,见 部署 Qt 应用程序 and 部署插件 文档编制。

静态插件

将插件包括在应用程序中的正常且最灵活方式,是将其编译成单独随附的动态库,并在运行时检测并加载。

可以将插件静态链接到应用程序。若构建静态版本的 Qt,这是包括 Qt 预定义插件的唯一选项。使用静态插件可使部署不易出错,但有缺点:无法添加插件功能,当不完整重新构建和重新分发应用程序时。

要静态链接插件,需要将所需插件添加到构建使用 QTPLUGIN .

.pro 文件对于应用程序而言,需要以下条目:

QTPLUGIN     += qjpeg \
                qgif \
                qkrcodecs
					

通常,qmake 将使用 Qt 模块所需的插件自动添加到 QTPLUGIN (见 QT ),而更专用的插件则需要手动添加。可以按类型覆盖自动添加的默认插件列表。例如,要链接 minimal 插件而不是默认 Qt 平台适配插件,使用:

QTPLUGIN.platforms = qminimal
					

若不想链接默认,也不想自动链接 minimal QPA 插件,使用:

QTPLUGIN.platforms = -
					

默认微调至最佳开箱即用体验,但可能不必要膨胀应用程序。推荐通过 qmake 构建审查链接器命令行,并消除不必要插件。

链接静态插件的细节

为促使实际链接并实例化静态插件, Q_IMPORT_PLUGIN () 宏还需要在应用程序代码中,但这些是由 qmake 自动生成的,并已添加到应用程序工程中。

若不想要添加到 QTPLUGIN 的所有插件被自动链接,移除 import_plugins CONFIG 变量:

CONFIG -= import_plugins
					

创建静态插件

创建自己的静态插件也是可能的,通过以下这些步骤:

  1. Add CONFIG += static 到插件的 .pro 文件。
  2. 使用 Q_IMPORT_PLUGIN () 宏在应用程序中。
  3. 使用 Q_INIT_RESOURCE () 宏在应用程序中,若插件随附 qrc 文件。
  4. 链接应用程序与插件库,使用 LIBS .pro 文件。

插件和描绘 范例和关联的 基本工具 插件,了解如何做到这的有关细节。

注意: 若不使用 qmake 构建插件,需要确保 QT_STATICPLUGIN 预处理器宏有定义。

部署和调试插件

部署插件 文档涵盖采用应用程序部署插件和调试它们 (当出现问题时) 的过程。

另请参阅 QPluginLoader , QLibrary ,和 插件和描绘范例 .