Qt的源代码中经过 Q<pluginType>Factory、Q<pluginType>Plugin 和 Q<pluginType> 这三个类实现了Qt的插件载入机制,linux
这个机制可用于载入特定种类的插件。比方经过 QPlatformIntegrationFactory\QPlatformIntegrationPlugin\QPlatformIntegration
三个类可以实现平台类QPA插件(PlatformIntegration)的载入,经过QPlatformInputContextFactory\QPlatformInputContextPlugin\
QPlatformInputContext三个类可以实现输入法类插件(InputContext)的载入。
如下自底向上介绍Qt的插件载入机制
app
class Q<pluginType>Plugin { ... Q<pluginType> * creat(...) ; // 返回一个Q<pluginType>类型的指针。这个函数的功能通常都很easy, // 其内部仅仅需要 new 一个 Q<pluginName> 类的对象,并返回其指针 }
QFactoryLoader::QObject *instance(int index)
instance 返回一个
QOjbect类或其子类的指针,而这个指针一般会被映射成 Q<pluginType>Plugin 类型。
这样一来。Qt 框架要载入某个插件时,仅仅需要先调用 QFactoryLoader::instance(index) 返回一个该插件相应的
Q<pluginType>Plugin指针。再经过这个指针调用 Q<pluginType>Plugin::creat(...) 方法,就可以得到插件主体
类Q<pluginName>的一个实例。
框架
template <class PluginInterface, class FactoryInterface> PluginInterface *qLoadPlugin(const QFactoryLoader *loader, const QString &key) { const int index = loader->indexOf(key); // 依据插件的keyword查找该插件所属的库在库列表中的索引 if (index != -1) { QObject *factoryObject = loader->instance(index); // 载入插件所属的库 if (FactoryInterface *factory = qobject_cast<FactoryInterface *>(factoryObject)) if (PluginInterface *result = factory->create(key)) return result; // 返回插件的实体类 } return 0; } template <class PluginInterface, class FactoryInterface, class Parameter1> PluginInterface *qLoadPlugin1(const QFactoryLoader *loader, const QString &key, const Parameter1 ¶meter1) { const int index = loader->indexOf(key); if (index != -1) { QObject *factoryObject = loader->instance(index); if (FactoryInterface *factory = qobject_cast<FactoryInterface *>(factoryObject)) if (PluginInterface *result = factory->create(key, parameter1)) return result; } return 0; }
其定义大体例如如下:
函数
class Q<pluginType>Factory { public: static QStringList keys(...) ; // 得到与 Q<pluginType> 类型的插件相关的keyword列表, // keyword通常用于描写叙述插件的名称等属性,这个keyword列表中的每个 // 元素都相应一个实际的插件。如 QPlatformInputContextFactory::keys(...) // 返回的就是输入法插件的名称列表。 static Q<pluginType> * creat(...) ; // 返回一个Q<pluginType>类型的指针(应用程序所需的插件) }
比方QPlatformIntegrationFactory用于“生产”平台类插件。
QPlatformInputContextFactory用于“生产”输入法类插件。普通状况下,Q<pluginType>Factory 类并无实际的成员变量。而仅仅有几个
静态的方法,所以一个Qt应用程序中不需要将这个类实例化。而是直接使用这个类的静态方法。另外,Qt还会为每个Q<pluginType>Factory 类
绑定一个 QFactoryLoader 对象,这个对象专门负责载入这一类别的插件。
Qt 框架的顶层要载入某一类插件时,仅仅需与相应的Q<pluginType>Factory 类打交道就能够。post
比方。在Qt应用程序初始化过程当中,它发现本身
ui
需要一个输入法插件了,就会直接调用 QPlatformInputContextFactory::creat(...) 来生产一个输入法类插件。而不用再管这个插件载入过程当中的细节。spa
返回的这个输入法类插件到底是什么。是ibus? 仍是fcitx?插件
这些全然由用户经过环境变量QT_IM_MODULE指定,指针
QPlatformInputContextFactory::create()方法中会去检測这个环境变量。code
static QPlatformIntegration *platform_integration; static QPlatformIntegration *platformIntegration() { return platform_integration; }
GuiApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name, arguments, argc, argv, platformPluginPath); ....
QPlatformIntegration *ret = loadIntegration(directLoader(), platform, paramList, argc, argv))
static inline QPlatformIntegration *loadIntegration(QFactoryLoader *loader, const QString &key, const QStringList ¶meters, int &argc, char ** argv) { const int index = loader->indexOf(key); if (index != -1) { // factory 指向相应的平台插件类的实例。如QLinuxFbIntegrationPlugin类; // 接着调用其creat方法生成并返回一个 QPlatformIntegration 类的实例的指针, // 这个指针将终于赋值给 QGuiApplicationPrivate::platform_integration, // 应用程序就获得了本身的执行平台. // 同一时候由此可知。假设想本身写一个QPA平台插件,仅仅需派生一个QPlatformIntegrationPlugin类和一个QPlatformIntegration类就能够。 if (QPlatformIntegrationPlugin *factory = qobject_cast<QPlatformIntegrationPlugin *>(loader->instance(index))) if (QPlatformIntegration *result = factory->create(key, parameters, argc, argv)) return result; } return 0; }
QPlatformInputContext *QPlatformInputContextFactory::create() { QPlatformInputContext *ic = 0; QString icString = QString::fromLatin1(qgetenv("QT_IM_MODULE")); // 检測环境变量QT_IM_MODULE。依据它选择要载入的输入法插件 if (icString == QLatin1String("none")) return 0; ic = create(icString); // 调用还有一个create函数载入输入法插件 if (ic && ic->isValid()) return ic; // 如下的代码暂不理会 delete ic; ic = 0; QStringList k = keys(); for (int i = 0; i < k.size(); ++i) { if (k.at(i) == icString) continue; ic = create(k.at(i)); if (ic && ic->isValid()) return ic; delete ic; ic = 0; } return 0; } QPlatformInputContext *QPlatformInputContextFactory::create(const QString& key) { QStringList paramList = key.split(QLatin1Char(':')); const QString platform = paramList.takeFirst().toLower(); #if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) if (QPlatformInputContext *ret = qLoadPlugin1<QPlatformInputContext, QPlatformInputContextPlugin>(loader(), platform, paramList)) // 依据key(插件名称)来载入相应的库并实例化(产生一个QPlatformInputContext类型的指针)。 return ret; #endif return 0; }
Qt中能载入库或插件的几个类:
QLibrary ,
QPluginLoader ,
QFactoryLoader ,
QStaticPlugin (临时不研究这个)
QLibrary 和 QPluginLoader 依赖的'私有数据类'都是 QLibraryPrivate。 一个QLibrary或QPluginLoader的对象都有一个QLibraryPrivate对象,相应一个库或插件;
QFactoryLoader 依赖的'私有数据类'是 QFactoryLoaderPrivate , 但 QFactoryLoaderPrivate 类中又包括了一个QLibraryPrivate列表。这个列表中有多个
QLibraryPrivate类型的元素。相应一系列的库或插件;
因此可见,QLibraryPrivate是Qt中与库或插件相关的核心数据类,每个库都相应一个QLibraryPrivate对象。
<2>
1. Qt Assistant 中搜索 How to Create Qt Plugins ,这一段具体说明了建立插件的方法。
主要有高级API(Higher-level API)和低级API(Lower-level API)两种API可以用来写插件。
高级API的用法可以在Qt源代码中看到很是多实例。低级API的使用演示样例在本系列文章的最后给出。
2. 假设不会写插件的 .pro 文件,可以在Qt Assistant 中搜索 qmake Manual , 这一页里有很是多连接是与编写project文件相关的,如 qmake Language 连接讲 .pro 文件的语法,Variables 连接讲.pro 文件里的变量(如QT、CONFIG、TEMPLATE等变量), Replace Functions 和 Replace Functions 连接讲一些内建的函数(可以在.pro文件里使用)