Qt有两种与插件有关的API。一种用来扩展Qt本身的功能,如自定义数据库驱动,图像格式,文本编解码,自定义分格,等等,称为Higher-Level API。另一种用于应用程序的功能扩展,称为Lower-Level API。前一种是建立在后一种的基础之上的。这里讨论的是后一种,即用来扩展应用程序的Lower-level API。
让应用程序支持插件扩展的步骤:
1. 定义一个接口集(只有纯虚函数的类),用来与插件交流。
2. 用宏Q_DECLARE_INTERFACE()将该接口告诉Qt元对象系统。
- Q_DECLARE_INTERFACE(BrushInterface,"com.trolltech.PlugAndPaint.BrushInterface/1.0")
3. 应用程序中用QPluginLoader来装载插件。
4. 用宏qobject_cast()来确定一个插件是否实现了接口。
- QObject *obj = new QTimer;
- QTimer *timer = qobject_cast<QTimer *>(obj);
写一个插件的步骤:
1. 声明插件类,该类从QObject和该插件希望实现的接口继承而来。
2. 用宏Q_INTERFACES()将该接口告诉Qt元对象系统。
- class BasicToolsPlugin : public QObject,
- public BrushInterface,
- public ShapeInterface,
- public FilterInterface
- {
- Q_OBJECT
- Q_INTERFACES(BrushInterface ShapeInterface FilterInterface)
- public:
- ...
- };
3. 用宏Q_EXPORT_PLUGIN2()导出插件。
- 1 Q_EXPORT_PLUGIN2 ( PluginName, ClassName )
4. 用适当的.pro文件构建插件。下面的代码声明了一个接口类:
- class FilterInterface
- {
- public:
- virtual ~FilterInterface() {}
- virtual QStringList filters() const = 0;
- virtual QImage filterImage(const QString &filter, const QImage &image, QWidget* parent)=0;
- };
- Q_DECLARE_INTERFACE(FilterInterface, "com.trolltech.PlugAndPaint.FilterInterface/1.0")
这里是实现该接口的插件类的定义:
- #include <QObject>
- #include <QStringList>
- #include <QImage>
- #include <plugandpaint/interfaces.h>
- class ExtraFiltersPlugin : public QObject, public FilterInterface
- {
- Q_OBJECT
- 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应用程序的详细示例。
#p#
装载插件
装载插件时。Qt库有一些健全检查来确定插件能否被装载和使用。这就可以同时安装多个版本和Qt库配置。
与较高主版本和(或)次版本号的Qt库链接的插件不能被主版本和(或)次版本号较低的库装载。
原理: 一个使用新版Qt库的插件可能用了老版本没有的新特征。Trolltech有一个只在次版本号升级时添加新功能和API的政策,这就是为什么该测试只看主次版本号,而不看补丁号。
Qt库和所有插件用一个联编关键字来联编。Qt库中的联编关键字被与插件中的联编关键字对照,如果相符,插件就被装载。如果联编关键字不符,Qt库就拒绝装载该插件。
原理: 见下文对联编关键字的解释。
编译插件来扩展应用程序时,确保插件和应用程序用同样的配置这一点很重要。这意味着如果应用程序是release模式编译的,那么插件也要是release模式。
若将Qt配置为debug和release模式都编译,但只在release模式下编译应用程序,就要确保你的插件也是在release模式下编译的。缺省的,若Qt的debug编译可用,插件就只在debug模式下编译。要强制插件用release模式编译,要在工程中添加:
CONFIG += release这能确保插件兼容应用程序中所用的库版本。
关键字
装载插件时,Qt核对每一个插件的联编关键字要和自己的匹配,以保证所装载的是兼容的插件;任何不匹配的插件不会被装载。
联编关键字包含一下信息:
- Architecture, operating system and compiler.
原理: 在同一编译器的不同版本并不产生二进制兼容代码的场合,编译器的版本也体现在联编关键字里。
Qt库的配置。这个配置是库中所缺少特性的列表,因为这些功能对应的API在该库中不可用。
原理: 两个同一版本的Qt库的不同配置不是二进制兼容的。装载插件的Qt库使用这个(缺少的)特性列表来判断插件是不是二进制兼容的。
注意: 也存在这种情况,插件可以使用在两个不同配置里可用到的特性。但是,编写插件的开发者需要知道,哪些特性在他们的插件和Qt的公用工具类中都在被使用。Qt库在装载插件时会需要复杂的特性与依赖性的查询确认。这些需求给开发者添了一个不必要的负担,也增加了装载插件的系统开销。为了减少开发时间,降低应用的运行时消耗,可以使用对联编关键字的简单字符串比较。
可选地,可以在配置脚本命令行指定一个附加的字符串Optionally, an extra string may be specified on the configure script command line.
原理: 在发布带有应用程序的Qt库的二进制时,这给开发者提供了一个编写插件的办法,这样写出来的插件只能被插件链接的那个库所装载。
为了调试可能需要关闭联编关键字校验功能,这可以通过将你运行应用程序的环境的环境变量QT_NO_PLUGIN_CHECK设置为非零来实现。
#p#
静态类
插件能被静态地链接到应用程序。如果你创建了Qt的静态版本,这仅仅是用来包含Qt的预定义插件的一个选项。
当被作为静态库编译时,Qt提供下面这些静态插件:
要静态链接这些插件,你的应用程序中要用到宏Q_IMPORT_PLUGIN() 并且要用QTPLUGIN将需要的插件添加到你的编译中。例如,在main.cpp中:
- #include <QApplication>
- #include <QtPlugin>
- Q_IMPORT_PLUGIN(qjpeg)
- Q_IMPORT_PLUGIN(qgif)
- Q_IMPORT_PLUGIN(qkrcodecs)
- int main(int argc, char *argv[])
- {
- QApplication app(argc, argv);
- ...
- return app.exec();
- }
应用程序的.pro 文件中要用下列条目:
- QTPLUGIN += qjpeg \
- qgif \
- qkrcodecs
也可以创建自己的静态库,步骤如下:
在插件的 .pro 文件中添加 CONFIG += static 。
应用程序中用宏 Q_IMPORT_PLUGIN() 。
应用程序的.pro 文件中用 LIBS 将静态库链接进来。
参见示例 Plug & Paint 和相关的插件 Basic Tools 来获得详情。
The Plugin Cache
为了加速插件的装载和确认,装载插件时收集的信息被缓存到QSettings中。这包括插件是否被成功装载的信息,以使后面的装载操作不用再尝试装载无效的插件。但是,若一个插件的 last modified 时间戳被修改,插件的缓存条目是无效的并且插件会不管缓存条目中的值而被重新装载,同时缓存条目本身也会被新的值替代。
这也意味着每一次插件或任何依赖资源(如共享库)被更新之后时间戳也必须被更新,因为依赖资源可能影响一个插件装载的结果。
有时,开发插件时,需要从插件缓存中移除条目。因为Qt用QSettings来管理插件缓存,插件的位置是依赖于平台的;更多关于每一个平台的信息请参看the QSettings documentation。
例如,Windows中这些条目存储在注册表中,每个插件的路径是以下面两个字串中的一个开始的:
- HKEY_CURRENT_USER\Software\Trolltech\OrganizationDefaults\Qt Plugin Cache 4.2.debug
- HKEY_CURRENT_USER\Software\Trolltech\OrganizationDefaults\Qt Plugin Cache 4.2.falseDebugging Plugins
有许多问题可能影响到插件在应用程序中的正常运转。 许多与插件和应用程序的创建方法不同有关, 通常发生在不同的创建系统和过程中。
下表描述的是开发者创建插件时遇到的问题的常见原因:
也可以用QT_DEBUG_PLUGINS环境变量来从Qt中获得尝试去装载的每一个插件的诊断信息。在应用程序的运行环境中把该变量的值设置为非零。
【编辑推荐】