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的后续版本不一定兼容.
基类 | 目录 | 所属模块 | 区分大小写 |
---|---|---|---|
QAccessibleBridgePlugin | accessiblebridge | Qt GUI | Case Sensitive |
QImageIOPlugin | imageformats | Qt GUI | Case Sensitive |
QPictureFormatPlugin (obsolete) | pictureformats | Qt GUI | Case Sensitive |
QAudioSystemPlugin | audio | Qt Multimedia | Case Insensitive |
QDeclarativeVideoBackendFactoryInterface | video/declarativevideobackend | Qt Multimedia | Case Insensitive |
QGstBufferPoolPlugin | video/bufferpool | Qt Multimedia | Case Insensitive |
QMediaPlaylistIOPlugin | playlistformats | Qt Multimedia | Case Insensitive |
QMediaResourcePolicyPlugin | resourcepolicy | Qt Multimedia | Case Insensitive |
QMediaServiceProviderPlugin | mediaservice | Qt Multimedia | Case Insensitive |
QSGVideoNodeFactoryPlugin | video/videonode | Qt Multimedia | Case Insensitive |
QBearerEnginePlugin | bearer | Qt Network | Case Sensitive |
QPlatformInputContextPlugin | platforminputcontexts | Qt Platform Abstraction | Case Insensitive |
QPlatformIntegrationPlugin | platforms | Qt Platform Abstraction | Case Insensitive |
QPlatformThemePlugin | platformthemes | Qt Platform Abstraction | Case Insensitive |
QGeoPositionInfoSourceFactory | position | Qt Positioning | Case Sensitive |
QPlatformPrinterSupportPlugin | printsupport | Qt Print Support | Case Insensitive |
QSGContextPlugin | scenegraph | Qt Quick | Case Sensitive |
QScriptExtensionPlugin | script | Qt Script | Case Sensitive |
QSensorGesturePluginInterface | sensorgestures | Qt Sensors | Case Sensitive |
QSensorPluginInterface | sensors | Qt Sensors | Case Sensitive |
QSqlDriverPlugin | sqldrivers | Qt SQL | Case Sensitive |
QIconEnginePlugin | iconengines | Qt SVG | Case Insensitive |
QAccessiblePlugin | accessible | Qt Widgets | Case Sensitive |
QStylePlugin | styles | Qt Widgets | Case 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检测和加载插件, 这类插件可以提供任意功能, 不限于数据库驱动, 图像格式, 文本编解码器, 样式等.
应用程序使用插件的具体步骤如下:
- 定义一组插件接口(纯虚类).
- 使用 Q_DECLARE_INTERFACE() 宏告诉Qt的 元对象系统 有关接口信息.
- 在应用程序中使用 QPluginLoader 加载插件.
- 使用 qobject_cast() 检测插件是否提供相关接口.
编写插件的步骤:
- 声明一个插件类, 继承自 QObject 及插件接口类.
- 使用 Q_INTERFACES() 宏告诉Qt的 元对象系统 有关接口信息.
- 使用 Q_PLUGIN_METADATA() 宏导出插件.
- 使用合适的
.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 Applications 和 Deploying 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
创建静态插件
你可以通过如下步骤, 创建自己的静态插件:
- 在插件的
.pro
文件添加CONFIG += static
. - 应用程序中使用 Q_IMPORT_PLUGIN() 宏.
- 如果插件有qrc文件, 应用程序还需要使用 Q_INIT_RESOURCE() 宏.
- 在应用程序的
.pro
文件中, 使用LIBS链接插件.
详细信息参见 Plug & Paint示例和 Basic Tools 插件.
注意: 如果你不是用qmake构建插件, 你必须确保已定义预处理宏 QT_STATICPLUGIN
.
部署和调试插件
Deploying Plugins 文档介绍如何部署插件, 并在出问题时调试.