Qt5的插件机制(1)--Qt 框架中的插件载入机制概述
概述
Qt的源代码中通过 Q<pluginType>Factory、Q<pluginType>Plugin 和 Q<pluginType> 这三个类实现了Qt的插件载入机制,
这个机制可用于载入特定种类的插件。
比方通过 QPlatformIntegrationFactory\QPlatformIntegrationPlugin\QPlatformIntegration
三个类能够实现平台类QPA插件(PlatformIntegration)的载入,通过QPlatformInputContextFactory\QPlatformInputContextPlugin\
QPlatformInputContext三个类能够实现输入法类插件(InputContext)的载入。
以下自底向上介绍Qt的插件载入机制
实现插件:Q<pluginType> 类和Q<pluginType>Plugin 类
首先,Q<pluginType> 类(或其子类)实现详细的功能,他是插件的主体,不同类别的插件要实现不同的功能;
Q<pluginType>Plugin 类(或其子类)是该插件的接口类, 一般仅仅须要实现一个方法,creat,
- class Q<pluginType>Plugin
- {
- ...
- Q<pluginType> * creat(...) ; // 返回一个Q<pluginType>类型的指针。这个函数的功能一般都很easy,
- // 其内部仅仅须要 new 一个 Q<pluginName> 类的对象,并返回其指针
- }
Q<pluginType>Plugin 类主要被 Qt 框架自身用来载入插件
载入插件:QFactoryLoader 类
此外另一个类,与插件的载入息息相关,这个类是 QFactoryLoader, 我们如今仅仅须要关心这个类的 instance() 方法:
- QFactoryLoader::QObject *instance(int index)
QFactoryLoader 类会维护一个 库列表, index 就是要载入的插件所属的库在库列表中的索引。
instance 返回一个
QOjbect类或其子类的指针,而这个指针通常会被映射成 Q<pluginType>Plugin 类型。
这样一来。Qt 框架要载入某个插件时,仅仅须要先调用 QFactoryLoader::instance(index) 返回一个该插件相应的
Q<pluginType>Plugin指针。再通过这个指针调用 Q<pluginType>Plugin::creat(...) 方法,就能够获得插件主体
类Q<pluginName>的一个实例。
载入插件的快捷函数
接着再来看下 qLoadPlugin 和 qLoadPlugin1 这两个模板函数的实现,二者功能上的主要差别是后者能够设置载入插件时的參数。
使用这两个模板函数能够快捷的载入插件并获取事实上例。
在使用这两个模板时,模板类型參数 PluginInterface 应被填充为 Q<pluginType>(插件主体类),类型參数FactoryInterface
应被填充为 Q<pluginType>Plugin类。
- 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;
- }
插件生产者:Q<pluginType>Factory 类
最后来看Q<pluginType>Factory 类,它是Qt的插件载入机制中位于最上层的类,这个类一般主要实现两
个静态的方法(我们更关心 creat),并且都是静态的。
其定义大致例如以下:
- class Q<pluginType>Factory
- {
- public:
- static QStringList keys(...) ; // 获得与 Q<pluginType> 类型的插件相关的keyword列表,
- // keyword一般用于描写叙述插件的名称等属性,这个keyword列表中的每一个
- // 元素都相应一个实际的插件。如 QPlatformInputContextFactory::keys(...)
- // 返回的就是输入法插件的名称列表。
- static Q<pluginType> * creat(...) ; // 返回一个Q<pluginType>类型的指针(应用程序所需的插件)
- }
Q<pluginType>Factory::creat(...) 函数中,会通过检測环境变量等手段来获取到与对应的插件相关的keyword。然后再用该keyword
去调用 qLoadPlugin/qLoadPlugin1 或相似的方法来载入插件。最后返回一个插件实体类。
一个Q<pluginType>Factory 类往往相应于某一类别、或某种特定功能的插件。
比方QPlatformIntegrationFactory用于“生产”平台类插件。
QPlatformInputContextFactory用于“生产”输入法类插件。普通情况下,Q<pluginType>Factory 类并没有实际的成员变量。而仅仅有几个
静态的方法,因此一个Qt应用程序中不须要将这个类实例化。而是直接使用这个类的静态方法。另外,Qt还会为每一个Q<pluginType>Factory 类
绑定一个 QFactoryLoader 对象,这个对象专门负责载入这一类别的插件。
Qt 框架的顶层要载入某一类插件时,仅仅需与相应的Q<pluginType>Factory 类打交道就可以。
比方。在Qt应用程序初始化过程中,它发现自己
须要一个输入法插件了,就会直接调用 QPlatformInputContextFactory::creat(...) 来生产一个输入法类插件。而不用再管这个插件载入过程中的细节。
返回的这个输入法类插件究竟是什么。是ibus? 还是fcitx?
这些全然由用户通过环境变量QT_IM_MODULE指定,
QPlatformInputContextFactory::create()方法中会去检測这个环境变量。
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
附 1 : Qt应用程序的平台类插件QPlatformIntegration的载入过程
如今临时先把焦点转移到QGuiApplicationPrivate::platformIntegration()上,这种方法的定义为:
- static QPlatformIntegration *platform_integration;
- static QPlatformIntegration *platformIntegration()
- { return platform_integration; }
可见 platform_integration 指向了当前的执行平台,那这个代表平台的成员在何处被初始化呢?
在QGuiApplication类(每一个GUI应用程序都有一个它的实例)的构造函数中。会调用QGuiApplicationPrivate类的init()方法,
而 QGuiApplicationPrivate::init 又会调用 QGuiApplicationPrivate::createPlatformIntegration(), 后者会读取
QT_QPA_PLATFORM_PLUGIN_PATH \ QT_QPA_PLATFORM \ QT_QPA_PLATFORMTHEME 等环境变量,接着将他们传递给
init_platform(...)函数 ,这个函数定义于 qtbase/src/gui/kernel/qguiapplication.cpp 文件里,它内部有一句:
- GuiApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name,
- arguments, argc, argv, platformPluginPath);
- ....
QPlatformIntegrationFactory::create 里又通过以下这句载入平台插件
- QPlatformIntegration *ret = loadIntegration(directLoader(), platform, paramList, argc, argv))
loadIntegration函数定义在 qtbase/src/gui/kernel/QPlatformIntegrationFactory.cpp中, 这个函数的作用和实现方法都与模板函数 qLoadPlugin 的实现类似。
- 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;
- }
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
附 2 : Qt应用程序的输入法类插件QPlatformInputContext的载入过程
Linux xcb平台或linuxfb平台都是在平台插件 (QXcbIntegration或QLinuxFbIntegration类 ) 的initialize() 函数中 通过
调用 QPlatformInputContextFactory::create() 来初始化 平台的输入法插件(QPlatformInputContext类)的。
QPlatformInputContextFactory::create() 的实现例如以下:
- 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;
- }
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
附 3 : 几点提示
<1>
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文件里使用)
Qt5的插件机制(1)--Qt 框架中的插件载入机制概述的更多相关文章
- Qt Creator 中的插件Plugin, 区分说明。。。
Qt Creator 中可以创建 三中类型的插件Plugin: 1.用的最多的,派生自QGenericPlugin类: 在新建Library, Plugin类型工程中,新建. 调用使用QPlugi ...
- MVC框架中的值提供机制(三)
在MVC框架中NameValueCollectionValueProvider采用一个NameValueCollection作为数据源,DictionnaryValueProvider的数据源类型自然 ...
- MVC框架中的值提供机制(二)
在MVC框架中存在一些默认的值提供程序模板,这些值提供程序都是通过工厂模式类创建;在MVC框架中存在需要已Factory结尾的工厂类,在值提供程序中也存在ValueProviderFactories工 ...
- MVC框架中的值提供机制(一)
在MVC框架中action方法中的Model数据的绑定的来源有很多个,可能是http请求中的get参数或是post提交的表单数据,会是json字符串或是路径中的相关数据;MVC框架中针对这些不同的数据 ...
- android插件化-获取apkplug框架已安装插件-03
上一篇文章成功的将apkplug框架嵌入了应用中而且启动 链接http://www.apkplug.com/blog/?post=10 这一篇文章实现怎样获取全部已安装插件 一 获取框架的System ...
- Java回调机制在RPC框架中的应用示例
完整源码: https://gitee.com/shiyanjun/x-callback-demo 应用场景描述: 服务提供者在项目启动时,创建并启动一个TCP服务器,然后将自己提供的所有服务注册到注 ...
- QT5.8+vs2015配置以及qt creater中出现中文乱码解决办法之一
1.参考此文档:QT5.6+vs2015配置: 2.出现乱码问题时候 在头文件上加入: #pragma execution_character_set("utf-8") //加入这 ...
- C++反射机制:可变参数模板实现C++反射(使用C++11的新特性--可变模版参数,只根据类的名字(字符串)创建类的实例。在Nebula高性能网络框架中大量应用)
1. 概要 本文描述一个通过C++可变参数模板实现C++反射机制的方法.该方法非常实用,在Nebula高性能网络框架中大量应用,实现了非常强大的动态加载动态创建功能.Nebula框架在码云的仓库地 ...
- 【转】【译】JavaScript魔法揭秘--探索当前流行框架中部分功能的处理机制
推荐语: 今天推荐一篇华为同事的同事翻译的一篇文章,推荐的主要原因是作为一个华为员工居然晚上还能写文章,由不得小钗不佩服!!! 其中的jQuery.angular.react皆是十分优秀的框架,各有特 ...
随机推荐
- Redis字符串(STRING)中BIT相关命令
上篇文章我们对STRING数据类型中一些基本的命令进行了介绍,但是没有涉及到BIT相关的命令,本文我们就来看看几个和BIT相关的命令. 本文是Redis系列的第四篇文章,了解前面的文章有助于更好的理解 ...
- python_购物车程序
#需求1.启动程序后,让用户输入工资,然后打印商品列表2.允许用户根据商品编号购买商品3.用户选择商品后,检测余额是否够,够就直接扣款,不够就提醒4.可随时退出,退出时,打印已购买商品和余额 #先定义 ...
- java application指的是什么
在Java语言中,能够独立运行的程序称为Java应用程序(Application).Java语言还有另外一种程序——Applet程序.Applet程序(也称Java小程序)是运行于各种网页文件中,用于 ...
- linux文件与用户和群组
文件基本属性 在图片中alogrithm的文件属性为drwxrwxr-x,其中d代表此文件为目录. 后面rwx,rwx,r-x分别代表文件所属者(ower),组(group),其他用户(other)的 ...
- JAVA学习之 异常处理机制
今天就来说说java的异常处理机制,异常处理不是第一接触,尤其是写过非常多c#的代码,基本都会写到异常处理的代码,事实上c#的异常处理与java的异常处理基本都是一样的,仅仅是在一些细节上不是非常一样 ...
- hdu Swipe Bo(bfs+状态压缩)错了多次的题
Swipe Bo Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total S ...
- bzoj 1600 & Usaco 月赛 2008 建造栅栏 题解
[原题] 1600: [Usaco2008 Oct]建造栅栏 Time Limit: 5 Sec Memory Limit: 64 MB Submit: 785 Solved: 443 [Subm ...
- hdu 4544 湫湫系列故事——消灭兔子 优先队列+贪心
将兔子的血量从小到大排序,箭的威力也从小到大排序, 对于每仅仅兔子将威力大于血量的箭增加队列,写个优先队列使得出来数位价钱最少.. #include<stdio.h> #include&l ...
- 每一个JavaScript开发者都应该知道的10道面试题
JavaScript十分特别.而且差点儿在每一个大型应用中起着至关关键的数据.那么,究竟是什么使JavaScript显得与众不同,意义非凡? 这里有一些问题将帮助你了解其真正的奥妙所在: 1.你能 ...
- 【LeetCode-面试算法经典-Java实现】【058-Length of Last Word (最后一个单词的长度)】
[058-Length of Last Word (最后一个单词的长度)] [LeetCode-面试算法经典-Java实现][全部题目文件夹索引] 原题 Given a string s consis ...