如何创建 Qt 插件
Qt 为创建插件提供了两个 API:
- 高级 API 用于编写 Qt 本身的扩展,如自定义数据库驱动程序、图像 Formats、文本编解码器和自定义样式。
- 用于扩展 Qt 应用程序的低级 API。
例如,如果您想编写一个自定义的QStyle 子类,并让 Qt XML 应用程序动态加载它,您将使用高级 API。
由于高层 API 建立在低层 API 的基础上,因此两者都会遇到一些共同的问题。
如果您想提供与Qt Widgets Designer 一起使用的插件,请参阅创建自定义 Widget 插件。
高级 API:编写 Qt 扩展
编写一个扩展 Qt 本身的插件,只需对相应的插件基类进行子类化、实现几个函数并添加一个宏即可。
有多个插件基类。派生插件默认存储在标准插件目录的子目录中。如果插件没有存储在相应的目录中,Qt 将无法找到它们。
下表总结了插件基类。其中一些类是私有的,因此没有文档说明。您可以使用它们,但不保证与以后的 Qt 版本兼容。
基类 | 目录名称 | Qt 模块 | 关键字大小写敏感性 |
---|---|---|---|
QAccessibleBridgePlugin | accessiblebridge | Qt GUI | 大小写敏感 |
QImageIOPlugin | imageformats | Qt GUI | 大小写敏感 |
QPictureFormatPlugin(已过时) | pictureformats | Qt GUI | 对大小写敏感 |
QBearerEnginePlugin插件 | bearer | Qt Network | 对大小写敏感 |
QPlatformInputContextPlugin(平台输入上下文插件 | platforminputcontexts | Qt 平台抽象 | 大小写不敏感 |
QPlatformIntegrationPlugin 平台集成插件 | platforms | Qt 平台抽象 | 区分大小写 |
QPlatformThemePlugin | platformthemes | Qt 平台抽象 | 区分大小写 |
QPlatformPrinterSupportPlugin 平台打印机支持插件 | printsupport | Qt Print Support | 区分大小写 |
QSGContextPlugin | scenegraph | Qt Quick | 区分大小写 |
QSqlDriverPlugin | sqldrivers | Qt SQL | 区分大小写 |
QIconEnginePlugin | iconengines | Qt SVG | 大小写不敏感 |
QAccessiblePlugin | accessible | Qt Widgets | 大小写敏感 |
QStylePlugin | styles | Qt Widgets | 大小写不敏感 |
如果您有一个名为JsonViewer
的新文档查看器类,并希望将其作为插件提供,则该类需要定义如下 (jsonviewer.h
):
class JsonViewer : public ViewerInterface { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.DocumentViewer.ViewerInterface/1.0" FILE "jsonviewer.json") Q_INTERFACES(ViewerInterface) public: JsonViewer(); ~JsonViewer() override; private: bool openJsonFile(); QTreeView *m_tree; QListWidget *m_toplevel = nullptr; QJsonDocument m_root; QPointer<QLineEdit> m_searchKey; };
确保该类的实现位于.cpp
文件中:
JsonViewer::JsonViewer() { connect(this, &AbstractViewer::uiInitialized, this, &JsonViewer::setupJsonUi); } void JsonViewer::init(QFile *file, QWidget *parent, QMainWindow *mainWindow) { AbstractViewer::init(file, new QTreeView(parent), mainWindow); m_tree = qobject_cast<QTreeView *>(widget()); }
此外,大多数插件都需要一个 json 文件 (jsonviewer.json
) ,其中包含描述插件的元数据。对于文档查看器插件,它只包含查看器插件的名称。
{ "Keys": [ "jsonviewer" ] }
需要在 json 文件中提供的信息类型取决于插件。有关文件中需要包含的信息的详细信息,请参见类文档。
对于数据库驱动程序、图像格式、文本编解码器和大多数其他插件类型,不需要创建显式对象。Qt 将根据需要查找并创建它们。
插件类可能需要实现额外的功能。有关每种插件类型必须重新实现的虚拟函数的详细信息,请参阅类文档。
文档查看器演示展示了如何实现一个显示文件结构化内容的插件。因此,每个插件都要重新实现虚拟函数,这些函数包括
- 识别插件
- 返回所支持的 MIME 类型
- 告知是否有要显示的内容,以及
- 如何显示内容
QString viewerName() const override { return QLatin1StringView(staticMetaObject.className()); }; QStringList supportedMimeTypes() const override; bool hasContent() const override; bool supportsOverview() const override { return true; }
低级 API:扩展 Qt 应用程序
除了 Qt 本身,Qt 应用程序还可以通过插件进行扩展。这要求应用程序使用QPluginLoader 检测和加载插件。在这种情况下,插件可提供任意功能,并不局限于数据库驱动程序、图像格式、文本编解码器、样式和其他类型的扩展 Qt 功能的插件。
通过插件使应用程序具有可扩展性涉及以下步骤:
- 定义一组用于与插件对话的接口(仅包含纯虚拟函数的类)。
- 使用Q_DECLARE_INTERFACE() 宏告诉 Qt XML 的元对象系统有关接口的信息。
- 在应用程序中使用QPluginLoader 加载插件。
- 使用qobject_cast() 测试插件是否实现了给定接口。
编写插件包括以下步骤:
- 声明一个插件类,该类继承自QObject 和插件希望提供的接口。
- 使用Q_INTERFACES() 宏告诉 Qt XML 的元对象系统有关接口的信息。
- 使用Q_PLUGIN_METADATA() 宏导出插件。
例如,下面是一个接口类的定义:
class ViewerInterface : public AbstractViewer { public: virtual ~ViewerInterface() = default; };
下面是接口声明:
#define ViewerInterface_iid "org.qt-project.Qt.Examples.DocumentViewer.ViewerInterface/1.0" Q_DECLARE_INTERFACE(ViewerInterface, ViewerInterface_iid)
有关Qt Widgets Designer 特有问题的信息,另请参阅为Qt Widgets Designer 创建自定义部件。
查找插件
Qt 应用程序会自动知道哪些插件可用,因为插件存储在标准插件子目录中。因此,应用程序不需要任何代码来查找和加载插件,因为 Qt 会自动处理它们。
在开发过程中,存放插件的目录是QTDIR/plugins
(其中QTDIR
是安装 Qt 的目录),每种类型的插件都存放在该类型的子目录中,例如styles
。如果希望应用程序使用插件,而又不想使用标准插件路径,可让安装过程确定要使用的插件路径,并保存该路径,例如使用QSettings ,供应用程序运行时读取。然后,应用程序就可以使用此路径调用QCoreApplication::addLibraryPath() ,应用程序就可以使用插件了。请注意,路径的最后部分(例如styles
)不能更改。
如果希望插件可以加载,一种方法是在应用程序下创建一个子目录,并将插件放在该目录中。如果分发 Qt 自带的任何插件(位于plugins
目录中的插件),则必须将插件所在的plugins
下的子目录复制到应用程序根目录(即不包括plugins
目录)。
有关部署的更多信息,请参阅部署Qt 应用程序和部署插件文档。
静态插件
在应用程序中包含插件的最灵活、最常用的方法是将其编译成一个动态库,单独发布,并在运行时检测和加载。
插件可以静态链接到应用程序中。如果您构建的是静态版本的 Qt,这是包含 Qt 预定义插件的唯一选择。使用静态插件可减少部署过程中的错误,但其缺点是,如果不彻底重建和重新发布应用程序,就无法添加插件的任何功能。
CMake 和 qmake 会自动添加所使用的 Qt 模块通常需要的插件,而更专业的插件则需要手动添加。自动添加插件的默认列表可按类型覆盖。
默认值是为了获得最佳的开箱即用体验而调整的,但可能会不必要地增加应用程序的体积。建议检查链接器命令行,删除不必要的插件。
要使静态插件真正被链接和实例化,还需要在应用程序代码中使用Q_IMPORT_PLUGIN() 宏,但这些宏会由构建系统自动生成并添加到应用程序项目中。
在 CMake 中导入静态插件
要在 CMake 项目中静态链接插件,需要调用qt_import_plugins命令。
例如,Linuxlibinput
插件默认没有导入。下面的命令会导入它:
qt_import_plugins(myapp INCLUDE Qt::QLibInputPlugin)
要链接最小平台集成插件而不是默认的 Qt 平台适配插件,请使用:
qt_import_plugins(myapp INCLUDE_BY_TYPE platforms Qt::MinimalIntegrationPlugin )
另一个典型的用例是只链接一组imageformats
插件:
qt_import_plugins(myapp INCLUDE_BY_TYPE imageformats Qt::QJpegPlugin Qt::QGifPlugin )
如果您想阻止链接任何imageformats
插件,请使用
qt_import_plugins(myapp EXCLUDE_BY_TYPE imageformats )
如果要关闭添加任何默认插件,请使用qt_import_plugins 的NO_DEFAULT
选项。
在 qmake 中导入静态插件
在 qmake 项目中,你需要使用QTPLUGIN
将所需插件添加到联编中:
QTPLUGIN += qlibinputplugin
例如,要链接 minimal 插件而不是默认的 Qt Platform Adaptation 插件,请使用
QTPLUGIN.platforms = qminimal
如果既不希望自动链接默认插件,也不希望自动链接最小 QPA 插件,请使用
QTPLUGIN.platforms = -
如果不想自动链接所有添加到 QTPLUGIN 的插件,请从CONFIG
变量中删除import_plugins
:
CONFIG -= import_plugins
创建静态插件
您也可以按照以下步骤创建自己的静态插件:
- 在
CMakeLists.txt
的qt_add_plugin命令中加入STATIC
选项。对于 qmake 项目,在插件的.pro
文件中添加CONFIG += static
。 - 在应用程序中使用Q_IMPORT_PLUGIN() 宏。
- 如果插件提供 qrc 文件,请在应用程序中使用Q_INIT_RESOURCE() 宏。
- 使用
CMakeLists.txt
文件中的target_link_libraries或.pro
文件中的LIBS
将应用程序与插件库链接。
请参阅 "即插即用"示例和相关的 "基本工具"插件,了解如何进行链接。
注意: 如果不是使用 CMake 或 qmake 来构建插件,则需要确保已定义QT_STATICPLUGIN
预处理器宏。
加载插件
插件类型(静态或共享)和操作系统需要特定的方法来定位和加载插件。为加载插件实现一个抽象是很有用的。
void ViewerFactory::loadViewerPlugins() { if (!m_viewers.isEmpty()) return;
QPluginLoader::staticInstances() 返回一个QObjectList ,其中包含指向每个静态链接插件的指针
// Load static plugins const QObjectList &staticPlugins = QPluginLoader::staticInstances(); for (auto *plugin : staticPlugins) addViewer(plugin);
共享插件位于其部署目录中,可能需要操作系统特定的处理方式。
// 加载共享插件 QDir插件目录=QDir(QApplication::applicationDirPath());#if defined(Q_OS_WINDOWS) pluginsDir.cd("app"_L1);#elif defined(Q_OS_DARWIN) if(pluginsDir.dirName()== "MacOS"_L1) { pluginsDir.cdUp(); pluginsDir.cdUp(); pluginsDir.cdUp(); pluginsDir.cdUp(); }#endif const autoentryList=pluginsDir.entryList(QDir::Files);for(constQString文件名: entryList) { QPluginLoaderloader(pluginsDir.absoluteFilePath(fileName)); QObject*plugin =loader.instance();if(plugin) addViewer(plugin);#if 0 else qDebug() << loader.errorString(); #endif} }
部署和调试插件
部署插件"文档介绍了在应用程序中部署插件以及在出现问题时调试插件的过程。
另请参阅 QPluginLoader 和QLibrary 。
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.