Qt wiki will be updated on October 12th 2023 starting at 11:30 AM (EEST) and the maintenance will last around 2-3 hours. During the maintenance the site will be unavailable.
Plugins/zh: Difference between revisions
AutoSpider (talk | contribs) (Simplify punctuation) |
No edit summary |
||
Line 5: | Line 5: | ||
{{LangSwitch}} | {{LangSwitch}} | ||
== | ==ry== | ||
<br /> | |||
<code> | <code> | ||
Line 27: | Line 22: | ||
// hammer.h 中的 Hammer(我们的 Hammer 插件) | // hammer.h 中的 Hammer(我们的 Hammer 插件) | ||
#include "toolinterface.h" | #include "toolinterface.h" | ||
class Hammer : public QObject, public ToolInterface | class Hammer : public QObject, public ToolInterface | ||
{ | { | ||
Line 34: | Line 31: | ||
public: | public: | ||
QString toolName() const { return "hammer"; } | QString toolName() const { return "hammer"; } | ||
}; | }; | ||
</code> | </code> | ||
不可以。 | |||
了。它通过检查头文件和查找Q_DECLARE_INTERFACE来实现这点。在我们这个例子中,toolinterface.h 文件内有一个Q_DECLARE_INTERFACE 。 | |||
粗糙的伪代插件,必须将其转换(cast)到所有被支持的接口来确定这个插件究竟实现了什么。为了避免这种开销,Qt的定义了插件应当被放置的标准路径。举例来说,样式插件必须在 plugins/styles | |||
if (strcmp(iname, "Hammer") 0) return this; | if (strcmp(iname, "Hammer") 0) return this; | ||
if (strcmp(iname, "ToolInterface") 0) return static_cast<ToolInterface'''>(this); | if (strcmp(iname, "ToolInterface") 0) return static_cast<ToolInterface'''>(this);''' | ||
// .. additional comparisons if you had more than one interface in Hammer | // .. additional comparisons if you had more than one interface in Hammer | ||
if (strcmp(iname, "in.forwardbias.tool/1.0") == 0) return static_cast<ToolInterface '''>(this); // also responds to the string in Q_DECLARE_INTERFACE | if (strcmp(iname, "in.forwardbias.tool/1.0") == 0) return static_cast<ToolInterface '''>(this); // also responds to the string in Q_DECLARE_INTERFACE''' | ||
} | } | ||
一个需要铭记的注意事项是,moc 不懂得接口的继承。举例来说,如果 ToolInterface 继承自 GenericInterface,它将不可能使用 qt_metacast 转换成 GenericInterface。moc 没有 C++ 语法的解析器,因此它不能在前面生成的 qt_metacast 代码中添加 GenericInterfa | |||
<br /> | |||
这是一个在共享对象中被导出的 C 函数。它看起来像这样: | 这是一个在共享对象中被导出的 C 函数。它看起来像这样: | ||
Line 67: | Line 56: | ||
// pluginName 没有被使用 (看下一节静态插件来理解它的用途) | // pluginName 没有被使用 (看下一节静态插件来理解它的用途) | ||
#define Q_EXPORT_PLUGIN2(pluginName, PluginClass) extern "C" PluginClass *qt_plugin_instance() { return new pluginClass; } | #define Q_EXPORT_PLUGIN2(pluginName, PluginClass) extern "C" PluginClass *qt_plugin_instance() { return new pluginClass; } | ||
extern "C" const char *qt_plugin_verification_data() { return "pattern=QT_PLUGIN_VERIFICATION_DATA\nversion=4.5.3\ndebug=false\nbuildkey=x86_64 linux g+''-4 full-config"; | |||
extern "C" const char *qt_plugin_verification_data() { return "pattern=QT_PLUGIN_VERIFICATION_DATA\nversion=4.5.3\ndebug=false\nbuildkey=x86_64 linux g+''-4 full-config";'' | |||
</code> | </code> | ||
Line 74: | Line 66: | ||
Q_IMPORT_PLUGIN2 定义了创建一个该插件实例的C函数并包含其他额外的函数来返回插件编译时使用的Qt的配置信息。 | Q_IMPORT_PLUGIN2 定义了创建一个该插件实例的C函数并包含其他额外的函数来返回插件编译时使用的Qt的配置信息。 | ||
== QPluginLoader == | ==QPluginLoader== | ||
QPluginLoader 使用作为 Q_EXPORT_PLUGIN2 的一部分被嵌入的校验数据(见上面的例子)来确认一个插件是否和应用程序兼容。在UNIX下一个有意思方面是,Qt将 mmap该库然后进行字符串的搜索(从文件的尾部,也即是反向搜索)而不是加载库然后解析函数。这样做的原因似乎是,它显然更快,而且可以避免加载不兼容的插件。 | QPluginLoader 使用作为 Q_EXPORT_PLUGIN2 的一部分被嵌入的校验数据(见上面的例子)来确认一个插件是否和应用程序兼容。在UNIX下一个有意思方面是,Qt将 mmap该库然后进行字符串的搜索(从文件的尾部,也即是反向搜索)而不是加载库然后解析函数。这样做的原因似乎是,它显然更快,而且可以避免加载不兼容的插件。 | ||
== Qt 标准插件 == | ==Qt 标准插件== | ||
Qt 的各个不同部分可以使用插件扩展- 编解码、样式、字体引擎等。对于给出的一个插件,必须将其转换(cast)到所有被支持的接口来确定这个插件究竟实现了什么。为了避免这种开销,Qt的定义了插件应当被放置的标准路径。举例来说,样式插件必须在 plugins/styles | Qt 的各个不同部分可以使用插件扩展- 编解码、样式、字体引擎等。对于给出的一个插件,必须将其转换(cast)到所有被支持的接口来确定这个插件究竟实现了什么。为了避免这种开销,Qt的定义了插件应当被放置的标准路径。举例来说,样式插件必须在 plugins/styles | ||
== 静态插件 == | ==静态插件== | ||
当Qt构建在静态模式下时,插件也必须是静态的。为什么呢?由于Qt是静态的,它根本无法加载动态链接到Qt的插件。唯一的办法是为所有的插件生成静态库并在目标程序中链接所有的静态库。 | 当Qt构建在静态模式下时,插件也必须是静态的。为什么呢?由于Qt是静态的,它根本无法加载动态链接到Qt的插件。唯一的办法是为所有的插件生成静态库并在目标程序中链接所有的静态库。 | ||
Line 88: | Line 80: | ||
1. 必须有人"注册(register)"这个插件。当静态构建Qt时,开发者决定哪些插件随程序发布。有人需要将这些被选择的插件需要到Qt系统。这是通过使用Q_IMPORT_PLUGIN(pluginName)实现的。它所做的是创建一个全局静态对象,其构造函数使用 qRegisterStaticPluginInstanceFunction 来注册插件的qt_plugin_instance_##pluginName。这样以来,Qt现在能知道这个插件的存在并且知道如何创建插件,但它不知道插件实现了什么! Qt只能通过遍历每个插件对象并尝试转换到每一个标准接口。 | 1. 必须有人"注册(register)"这个插件。当静态构建Qt时,开发者决定哪些插件随程序发布。有人需要将这些被选择的插件需要到Qt系统。这是通过使用Q_IMPORT_PLUGIN(pluginName)实现的。它所做的是创建一个全局静态对象,其构造函数使用 qRegisterStaticPluginInstanceFunction 来注册插件的qt_plugin_instance_##pluginName。这样以来,Qt现在能知道这个插件的存在并且知道如何创建插件,但它不知道插件实现了什么! Qt只能通过遍历每个插件对象并尝试转换到每一个标准接口。 | ||
2. 该插件本身是静态库。当我们的应用程序被链接时,我们需要链接这些静态库。这是通过. pro文件的 QTPLUGIN''=pluginName(这只是简单添加了 -l<plugin>.a 的链接选项) 实现的。 | 2. 该插件本身是静态库。当我们的应用程序被链接时,我们需要链接这些静态库。这是通过. pro文件的 QTPLUGIN''=pluginName(这只是简单添加了 -l<plugin>.a 的链接选项) 实现的。'' | ||
== FAQ == | ==FAQ== | ||
1. 多个插件可以共存在一个单一的 DLL/.so中么? 你可以将多个 '''相同''' 类型的插件放在一个DLL中但不能将不同类型的的插件放于一个单一的 .so 中。比如:你可以将两个图片插件放于同一个.so但是你不能将一个图片插件和一个字体引擎插件放于同一个.so文件。 | 1. 多个插件可以共存在一个单一的 DLL/.so中么? 你可以将多个 '''相同''' 类型的插件放在一个DLL中但不能将不同类型的的插件放于一个单一的 .so 中。比如:你可以将两个图片插件放于同一个.so但是你不能将一个图片插件和一个字体引擎插件放于同一个.so文件。 | ||
Line 97: | Line 89: | ||
3. 静态 Qt 可以加载动态插件么?不可以。 | 3. 静态 Qt 可以加载动态插件么?不可以。 | ||
__NOINDEX__ | |||
__NONEWSECTIONLINK__ |
Revision as of 02:36, 14 November 2020
This article may require cleanup to meet the Qt Wiki's quality standards. Reason: Auto-imported from ExpressionEngine. Please improve this article if you can. Remove the {{cleanup}} tag and add this page to Updated pages list after it's clean. |
ry
// toolinterface.h 中的 ToolInterface
class ToolInterface
{
public:
virtual QString toolName() const = 0;
};
Q_DECLARE_INTERFACE(ToolInterface, "in.forwardbias.tool/1.0");
// hammer.h 中的 Hammer(我们的 Hammer 插件)
#include "toolinterface.h"
class Hammer : public QObject, public ToolInterface
{
Q_OBJECT
Q_INTERFACES(ToolInterface)
public:
QString toolName() const { return "hammer"; }
};
不可以。
了。它通过检查头文件和查找Q_DECLARE_INTERFACE来实现这点。在我们这个例子中,toolinterface.h 文件内有一个Q_DECLARE_INTERFACE 。
粗糙的伪代插件,必须将其转换(cast)到所有被支持的接口来确定这个插件究竟实现了什么。为了避免这种开销,Qt的定义了插件应当被放置的标准路径。举例来说,样式插件必须在 plugins/styles
if (strcmp(iname, "Hammer") 0) return this; if (strcmp(iname, "ToolInterface") 0) return static_cast<ToolInterface>(this); // .. additional comparisons if you had more than one interface in Hammer if (strcmp(iname, "in.forwardbias.tool/1.0") == 0) return static_cast<ToolInterface >(this); // also responds to the string in Q_DECLARE_INTERFACE
}
一个需要铭记的注意事项是,moc 不懂得接口的继承。举例来说,如果 ToolInterface 继承自 GenericInterface,它将不可能使用 qt_metacast 转换成 GenericInterface。moc 没有 C++ 语法的解析器,因此它不能在前面生成的 qt_metacast 代码中添加 GenericInterfa
这是一个在共享对象中被导出的 C 函数。它看起来像这样:
// pluginName 没有被使用 (看下一节静态插件来理解它的用途)
#define Q_EXPORT_PLUGIN2(pluginName, PluginClass) extern "C" PluginClass *qt_plugin_instance() { return new pluginClass; }
extern "C" const char *qt_plugin_verification_data() { return "pattern=QT_PLUGIN_VERIFICATION_DATA\nversion=4.5.3\ndebug=false\nbuildkey=x86_64 linux g+''-4 full-config";''
注意:qt_plugin_instance 实际中使用了单例,为了易于理解上面进行了简化。
Q_IMPORT_PLUGIN2 定义了创建一个该插件实例的C函数并包含其他额外的函数来返回插件编译时使用的Qt的配置信息。
QPluginLoader
QPluginLoader 使用作为 Q_EXPORT_PLUGIN2 的一部分被嵌入的校验数据(见上面的例子)来确认一个插件是否和应用程序兼容。在UNIX下一个有意思方面是,Qt将 mmap该库然后进行字符串的搜索(从文件的尾部,也即是反向搜索)而不是加载库然后解析函数。这样做的原因似乎是,它显然更快,而且可以避免加载不兼容的插件。
Qt 标准插件
Qt 的各个不同部分可以使用插件扩展- 编解码、样式、字体引擎等。对于给出的一个插件,必须将其转换(cast)到所有被支持的接口来确定这个插件究竟实现了什么。为了避免这种开销,Qt的定义了插件应当被放置的标准路径。举例来说,样式插件必须在 plugins/styles
静态插件
当Qt构建在静态模式下时,插件也必须是静态的。为什么呢?由于Qt是静态的,它根本无法加载动态链接到Qt的插件。唯一的办法是为所有的插件生成静态库并在目标程序中链接所有的静态库。
当Qt是在静态模式下构建时,Q_EXPORT_PLUGIN2 宏扩展成一个C函数 qt_plugin_instance_##pluginName()。 pluginName有助于避免使用多个插件时的名称冲突 —— 记住这是静态链接时的代码。
Q_EXPORT_PLUGIN2 生成注册静态插件的代码。另外两点需要注意: 1. 必须有人"注册(register)"这个插件。当静态构建Qt时,开发者决定哪些插件随程序发布。有人需要将这些被选择的插件需要到Qt系统。这是通过使用Q_IMPORT_PLUGIN(pluginName)实现的。它所做的是创建一个全局静态对象,其构造函数使用 qRegisterStaticPluginInstanceFunction 来注册插件的qt_plugin_instance_##pluginName。这样以来,Qt现在能知道这个插件的存在并且知道如何创建插件,但它不知道插件实现了什么! Qt只能通过遍历每个插件对象并尝试转换到每一个标准接口。
2. 该插件本身是静态库。当我们的应用程序被链接时,我们需要链接这些静态库。这是通过. pro文件的 QTPLUGIN=pluginName(这只是简单添加了 -l<plugin>.a 的链接选项) 实现的。
FAQ
1. 多个插件可以共存在一个单一的 DLL/.so中么? 你可以将多个 相同 类型的插件放在一个DLL中但不能将不同类型的的插件放于一个单一的 .so 中。比如:你可以将两个图片插件放于同一个.so但是你不能将一个图片插件和一个字体引擎插件放于同一个.so文件。
2. 动态 Qt 可以加载静态插件么?不可以。
3. 静态 Qt 可以加载动态插件么?不可以。