How to Create Qt Plugins

Qt 提供两种创建插件的API:

  • 一种高级API, 扩充Qt功能: 自定义数据库驱动, 图像格式, 文本编解码器, 自定义样式等.
  • 一种低级API, 扩充Qt编写的应用程序功能.

例如, 如果你想自定义一个 QStyle 派生类, 让Qt动态加载, 你可以使用高级API.

高级API是以低级API为基础, 因此, 一些问题对二者是相同的.

如果你想为Qt Designer提供插件, 参考 Qt Designer 模块文档.

Topics:

高级 API: 扩充 Qt 功能

如果你想编写一个扩充Qt功能的插件, 你可以子类化合适的插件基类, 实现少数函数, 并添加一个宏.

派生的插件默认存储在标准的插件目录及子目录. 如果插件不存储在标准目录中, Qt无法加载插件.

下表是Qt的插件基类. 其中一些类是私有的, 没有描述文档. 你可以使用它们, 但是Qt的后续版本不一定兼容.

基类目录所属模块区分大小写
QAccessibleBridgePluginaccessiblebridgeQt GUICase Sensitive
QImageIOPluginimageformatsQt GUICase Sensitive
QPictureFormatPlugin (obsolete)pictureformatsQt GUICase Sensitive
QAudioSystemPluginaudioQt MultimediaCase Insensitive
QDeclarativeVideoBackendFactoryInterfacevideo/declarativevideobackendQt MultimediaCase Insensitive
QGstBufferPoolPluginvideo/bufferpoolQt MultimediaCase Insensitive
QMediaPlaylistIOPluginplaylistformatsQt MultimediaCase Insensitive
QMediaResourcePolicyPluginresourcepolicyQt MultimediaCase Insensitive
QMediaServiceProviderPluginmediaserviceQt MultimediaCase Insensitive
QSGVideoNodeFactoryPluginvideo/videonodeQt MultimediaCase Insensitive
QBearerEnginePluginbearerQt NetworkCase Sensitive
QPlatformInputContextPluginplatforminputcontextsQt Platform AbstractionCase Insensitive
QPlatformIntegrationPluginplatformsQt Platform AbstractionCase Insensitive
QPlatformThemePluginplatformthemesQt Platform AbstractionCase Insensitive
QGeoPositionInfoSourceFactorypositionQt PositioningCase Sensitive
QPlatformPrinterSupportPluginprintsupportQt Print SupportCase Insensitive
QSGContextPluginscenegraphQt QuickCase Sensitive
QScriptExtensionPluginscriptQt ScriptCase Sensitive
QSensorGesturePluginInterfacesensorgesturesQt SensorsCase Sensitive
QSensorPluginInterfacesensorsQt SensorsCase Sensitive
QSqlDriverPluginsqldriversQt SQLCase Sensitive
QIconEnginePluginiconenginesQt SVGCase Insensitive
QAccessiblePluginaccessibleQt WidgetsCase Sensitive
QStylePluginstylesQt WidgetsCase Insensitive

如果你想自定义一个Qt界面样式插件, 那么你可以创建一个头文件(mystyleplugin.h), 以如下方式声明类 MyStyle :


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

一些插件类需要实现额外函数. 不同类型的插件需要重新实现的虚函数参见插件类文档.

样式插件示例 展示如何基于 QStylePlugin 基类自定义样式插件.

低级API: 扩展Qt应用程序

除Qt框架外, Qt编写的应用程序也可通过插件扩展. 这种情况下, 应用程序可以使用QPluginLoader检测和加载插件, 这类插件可以提供任意功能, 不限于数据库驱动, 图像格式, 文本编解码器, 样式等.

应用程序使用插件的具体步骤如下:

  1. 定义一组插件接口(纯虚类).
  2. 使用 Q_DECLARE_INTERFACE() 宏告诉Qt的 元对象系统 有关接口信息.
  3. 在应用程序中使用 QPluginLoader 加载插件.
  4. 使用 qobject_cast() 检测插件是否提供相关接口.

编写插件的步骤:

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

示例 Plug & Paint 文档详细解释这一过程. 有关如何为Qt Designer添加自定义工具参见 Creating Custom Widgets for Qt Designer. 你也可以查看示例 Echo Plugin Example, 这个示例更详细的展示如何为Qt应用程序编写插件. 注意: 加载插件之前, 必须初始化 QCoreApplication .

动态加载插件

Qt应用程序可以自动加载插件, 因为插件存储在标准的插件子目录. 因此, 应用程序不需要编写任何代码查找及加载插件, Qt自动处理它们.

开发过程中, 插件目录是 QTDIR/plugins ( QTDIR 是Qt的安装目录), 不同类型的插件对应不同的子目录, 例如, styles. 如果你想自定义插件路径, 而不使用Qt的标准插件路径, 或者你想在安装过程确定并保存插件路径(例如, 使用QSettings让程序运行时读取). 那么, 你可以调用 QCoreApplication::addLibraryPath() 添加自定义路径, 应用程序就可以加载你的插件. 注意, 路径的最后一部分不能修改 (如, styles).

如果你希望插件可以加载, 一种方式是在应用程序的同级目录下创建子目录, 并将插件放入目录中. 如果你发布Qt程序, 并需要加载Qt的插件 (位于 plugins 目录), 你必须将 plugins 目录下的插件子目录复制到应用程序的根目录下 (即., 不包含 plugins 目录).

有关部署的更多信息参见 Deploying Qt ApplicationsDeploying Plugins 文档.

静态加载插件

在应用程序中使用插件, 最常用且灵活的方式是将插件编译成动态库, 程序运行时检测和加载.

插件也可以静态链接到你的应用程序. 如果你静态编译Qt, Qt的预定义插件静态编译是唯一选项. 使用静态插件的优势是软件部署时不易出错, 但是如果不完全重新构建应用程序, 无法在插件中添加任何功能.

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

在应用程序的 .pro 文件, 你需要加入如下代码:


  QTPLUGIN     += qjpeg \
                  qgif \
                  qkrcodecs

对于Qt模块使用的插件, qmake 自动将其所需的插件加入QTPLUGIN中 (参见 QT), 其他插件需要手动添加. 对于自动加载的插件, 默认列表可以按类型覆盖. 例如, 如果你想链接最小插件, 而不是Qt默认的平台自适应插件, 你可以使用:


  QTPLUGIN.platforms = qminimal

如果你既不想使用默认插件, 又不想自动链接最小插件, 使用:


  QTPLUGIN.platforms = -

默认加载插件提供一种"开箱即用"的体验, 但是加载不必要的插件, 增大应用程序体积. 建议检查qmake的链接器命令行, 消除不必要的插件.

链接静态插件的详细信息

为了链接和初始化静态插件, 应用程序代码也需要使用Q_IMPORT_PLUGIN() 宏, 然而, qmake会自动生成这些宏, 并将其加入应用程序项目中.

如果你不想自动链接QTPLUGIN所有的Qt插件, 你可以在pro文件中增加如下代码:


  CONFIG -= import_plugins

创建静态插件

你可以通过如下步骤, 创建自己的静态插件:

  1. 在插件的.pro文件添加 CONFIG += static .
  2. 应用程序中使用 Q_IMPORT_PLUGIN() 宏.
  3. 如果插件有qrc文件, 应用程序还需要使用 Q_INIT_RESOURCE() 宏.
  4. 在应用程序的.pro文件中, 使用LIBS链接插件.

详细信息参见 Plug & Paint示例和 Basic Tools 插件.

注意: 如果你不是用qmake构建插件, 你必须确保已定义预处理宏 QT_STATICPLUGIN.

部署和调试插件

Deploying Plugins 文档介绍如何部署插件, 并在出问题时调试.

参见 QPluginLoader, QLibrary, Plug & Paint Example.