如何创建一个插件
Qt提供了两个API来创建插件:
- 高层级API,用于扩展Qt自身:自定义数据库驱动,图像格式,文本编码,自定义样式等。
- 低层级API,用于扩展Qt应用程序。
例如,如果你想写一个定制的QStyle子类,并让Qt应用程序动态加载,那么你将使用高层级的API。
因为高层级API是建立在低层级API之上,一些问题是相同的。
如果你想提供一个插件用于Qt Designer,可参考Qt Designer模块的文档。
高层级API:编写Qt扩展
通过子类化合适的插件基类,实现少数函数并添加一个宏可以编写插件来扩展Qt自身的。
这里有数个插件基类。派生的插件默认在标准插件目录的子目录中存储。如果没有存储在适当的目录中,Qt将找到插件。
下表概述了插件基类,一些类是私有的,因此没有记录在文档中。你可以使用他们,但在后续的Qt版本中不能提供兼容性保证。
基类 | 目录名 | Qt模块 | 关键字是否大小写敏感 |
---|---|---|---|
QAccessibleBridgePlugin | accessiblebridge | Qt GUI | Sensitive |
QImageIOPlugin | imageformats | Qt GUI | Sensitive |
QAudioSystemPlugin | audio | Qt Multimedia | Insensitive |
QMediaServiceProviderPlugin | mediaservice | Qt Multimedia | Insensitive |
QSqlDriverPlugin | sqldrivers | Qt SQL | Sensitive |
QStylePlugin | styles | Qt Widgets | Insensitive |
如果你有一个MyStyle的新样式想在作为一个插件的情况下可用,该类需要定义如下(mystyleplugin.h)
Class MyStylePlugin : public QStylePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID “org.qt-project.Qt.QStyleFactoryInterface”)
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()实现中使用了key的小写版本;大多数其他插件都是区分大小写)
此外,有一个包含元数据的json文件(mystyleplugin.json),这是大多数插件用于描述插件的所需要的。对于样式插件,它只包含一个可以由插件创建的样式列表。
{“keys” : [ “ mystyleplugin” ]}
需要由json文件提供的此类信息是插件独立的,需要在文件中包含的信息内容,请参考类文件中的详细说明。
对于数据库驱动程序、图像格式、文本编解码器和大多数其他类型的插件,不需要显示的对象创建。Qt将查找并根据需要创建它们。Styles是个例外,因为你可能需要在代码中显示的设置一个style。为了应用Style,使用类似于以下的代码:
QApplication::setStyle(QStyleFactory::create(“MyStyle”));
一些插件类需要实现额外的功能。每种插件类型必须重新实现的虚函数,请参考类文档获取详细说明。
Style Plugin Example展示了如何实现一个插件来扩展QStylePlugin基类。
低层级API:扩展Qt应用程序
除了Qt自身,Qt应用程序也能通过插件扩展。这需要应用程序使用QPluginLoader来检测和加载插件。在这种情况下,插件可能提供专一的功能,不限于数据库驱动,图片格式,文本编码,和其他扩展Qt功能的插件类型。
使一个应用程序通过插件扩展,包括以下步骤:
- 1.定义一套接口用于插件交流(只包含纯虚函数的类)
- 2.使用Q_DECLARE_INTERFACE()宏将这些接口告知Qt元对象系统
- 3.在应用程序中使用QPluginLoader加载插件
- 4.使用qobject_cast测试一个插件是否实现了给定接口
编写一个插件包含以下步骤:
- 1.声明一个插件类,该类继承于QObject和插件想要提供的接口类
- 2.使用Q_INTERFACES()宏告知Qt元对象系统关于该插件
- 3.使用Q_PLUGIN_METADATA()宏导出插件
- 4.使用合适的pro文件构建插件
e.g 接口类的定义
class FilterInterface
{
public:
virtual ~FilterInterface(){}
virtual QStringList filters() const = 0;
virtual QImage filterImage(const QString &filter, const QImage &image, QWidget *parent) = 0;
};
以下是实现接口的插件类定义
class ExtraFiltersPlugin : public QObject, public FilterInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qtproject.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示例文档详细解释了该过程。也可以参考Creating Custom Widgets for Qt Designer获取Qt Designer特定的一些信息。可以参考Echo Plugin Example,那是一个更加琐碎的示例如何去实现一个插件来扩展Qt应用程序。请注意在插件能加载之前QCoreApplication必须要初始化。
定位插件
Qt应用程序自动知道哪些插件是可用的,因为插件存储在标准插件子目录中。因此应用程序不需要任何代码来查找和加载插件,因为Qt会自动处理它们。
在开发的过程中,插件的目录是QTDIR/plugins(QTDIR是Qt的安装目录),每一个子目录表示一种插件类型,如styles。如果你想让你的应用程序使用插件但并不想使用标准的插件路径,可以用安装进程决定你想要使用的插件路径,并保存此路径。E.g 通过使用QSetting,让应用程序在运行中读取。应用程序也能调用QCoreApplication::addLibraryPath()使插件变得可用。但注意路径的最后一部分不能被改变(e.g 如 styles)
如果你想让插件能够加载,一种方法是在应用程序下面创建一个子目录,并把插件放置其中。如果你分类了任何与qt相关的插件(这些插件位于plugins目录中),必须复制子目录于plugins下,plugins目录是你的应用程序加载插件的根文件夹(i.e. 不用包括插件目录)
。
更多关于部署的信息,见Deploying Qt Applications和Deploying Plugins文档。
静态插件
将插件包含到一个应用程序通常和最灵活的方法是将其编译成一个动态库,该库单独发布,并在运行时检测和加载。
插件可以静态地链接到你的应用程序中。如果你构建了Qt的静态版本,那么包含Qt预定义插件的唯一选项。使用静态插件使部署不容易出错,但是如果没有完整的重新构建和重新分配应用程序,就不能添加插件的功能。
为了静态地链接插件,需要使用QTPLUGIN将所需的插件添加到构建中。
在应用程序的pro文件中,你需要添加下述入口:
QTPLUGIN += qjpeg\
qgif\
qkrcodecs
qmake自动添加插件到QTPLUGIN,通常是Qt模块需要使用的,然而,更详细的插件需要手动添加。自动添加的插件默认列表可以覆盖每个类型。
E.g 为了链接最小的插件而不是默认的Qt平台适配插件,使用:
QTPLUGIN.platforms = qminimal
如果不想要默认和最小QPA插件自动链接,使用:
QTPLUGIN.platforms =-
默认情况下,这些缺省值将被调整为最优的开箱即用体验,但可能造成不必要的应用代码膨胀。建议检查由qmake构建的链接器命令行,并消除不必要的插件。
静态插件链接详述
为了使静态插件实际链接和实例化,在应用程序代码使用Q_IMPORT_PLUGIN()是必要的,但是这些都是由qmake自动生成的并添加到你的应用程序项目中。
如果你不希望所有插件自动链接到QTPLUGIN,从CONFIG变量中移除import_plugins。
CONFIG -= import_plugins
创建静态插件
通过以下步骤创建你自己的静态插件:
- 1.在pro文件中添加CONFIG += static
- 2.在应用程序中使用Q_IMPORT_PLUGIN宏
- 3.如果插件使用了qrc文件,使用Q_INIT_RESOURCE宏
- 4.在pro文件中使用LIBS链接应用程序和插件库
具体使用可参考Plugin & Paint示例代码和Basic Tools相关的插件。
注意:如果没有使用qmake来构建插件,那么需要确保qtstaticplugin预处理器宏是定义的。
部署和调试插件
Deploying Plugins文档覆盖了应用部署插件的过程及问题出现时如何调试。
也可参考QPluginLoader,QLibrary和Plugin & Paint例子。