如何创建 Qt 插件
Qt 提供了两种用于创建插件的 API:
- 用于编写 Qt 自身扩展的高级 API,例如自定义数据库驱动程序、图像格式、文本编解码器和样式。
- 一个用于扩展 Qt 应用程序的低级 API。
例如,若要编写自定义的 `QStyle ` 子类,并让 Qt 应用程序动态加载它,则应使用高级 API。
由于高级 API 是构建在低级 API 之上的,因此两者存在一些共同的问题。
如果你想提供可与Qt Widgets Designer 配合使用的插件,请参阅《创建自定义控件插件》。
高级 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:
void retranslate() override;
bool openJsonFile();
QTreeView *m_tree = nullptr;
QListWidget *m_toplevel = nullptr;
QJsonDocument m_root;
QAction *m_expandAllAction = nullptr;
QAction *m_collapseAllAction = nullptr;
};请确保该类的实现位于名为.cpp 的文件中:
JsonViewer::JsonViewer()
: m_expandAllAction(new QAction(this)),
m_collapseAllAction(new QAction(this))
{
connect(this, &AbstractViewer::uiInitialized, this, &JsonViewer::setupJsonUi);
m_expandAllAction->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::ZoomIn));
m_collapseAllAction->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::ZoomOut));
}
void JsonViewer::init(QFile *file, QWidget *parent, QMainWindow *mainWindow)
{
AbstractViewer::init(file, new QTreeView(parent), mainWindow);
setTranslationBaseName("jsonviewer"_L1);
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的元对象系统。
- 在应用程序中使用QPluginLoader 加载插件。
- 使用 `qobject_cast()` 来测试某个插件是否实现了给定的接口。
编写插件包括以下步骤:
- 声明一个从QObject 以及插件要提供的接口继承而来的插件类。
- 使用Q_INTERFACES() 宏向 Qt的元对象系统声明这些接口。
- 使用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 Platform Adaptation 插件),请使用:
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 插件,也不希望自动链接精简版 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,将您的应用程序与插件库链接起来。
有关具体操作方法的详细信息,请参阅“Plug & Paint”示例及其相关的Basic Tools插件。
注意:如果您 未使用 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 pluginsDir=QDir(QApplication::applicationDirPath());
#if defined(Q_OS_DARWIN)
if(pluginsDir.exists("../PlugIns"_L1)) {// 已安装的构建
pluginsDir.cd("../PlugIns"_L1);
}else{
pluginsDir.cd("../../../../plugins"_L1);// 未安装构建
}
#elif defined(Q_OS_WIN)
if(pluginsDir.exists("plugins"_L1)) {// 未安装的构建
pluginsDir.cd("plugins"_L1);
}else{
pluginsDir.cd("../plugins"_L1);// 已安装构建
}
#else
pluginsDir.cd("../plugins"_L1);// 已安装和未安装构建
#endif
// qDebug("正在从 %s 加载插件...", qUtf8Printable(pluginsDir.path()));
const autoentryList=pluginsDir.entryList(QDir::Files);
for(constQString&fileName: entryList) {
QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
QObject*plugin =loader.instance();
if(plugin)
addViewer(plugin);
#if 0
else
qDebug() << loader.errorString();
#endif
}
}插件的部署与调试
《插件部署》文档介绍了将插件与应用程序一起部署的过程,以及在出现问题时如何对其进行调试。
另请参阅 QPluginLoader 和QLibrary 。
© 2026 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.