概述

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,

  1. class Q<pluginType>Plugin
  2. {
  3. ...
  4. Q<pluginType> * creat(...) ; // 返回一个Q<pluginType>类型的指针。这个函数的功能一般都很easy,
  5. // 其内部仅仅须要 new 一个 Q<pluginName> 类的对象,并返回其指针
  6. }

Q<pluginType>Plugin 类主要被 Qt 框架自身用来载入插件

载入插件:QFactoryLoader 类

此外另一个类,与插件的载入息息相关,这个类是 QFactoryLoader, 我们如今仅仅须要关心这个类的 instance() 方法:

  1. 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类。

  1. template <class PluginInterface, class FactoryInterface>
  2. PluginInterface *qLoadPlugin(const QFactoryLoader *loader, const QString &key)
  3. {
  4. const int index = loader->indexOf(key); // 依据插件的keyword查找该插件所属的库在库列表中的索引
  5. if (index != -1) {
  6. QObject *factoryObject = loader->instance(index); // 载入插件所属的库
  7. if (FactoryInterface *factory = qobject_cast<FactoryInterface *>(factoryObject))
  8. if (PluginInterface *result = factory->create(key))
  9. return result; // 返回插件的实体类
  10. }
  11. return 0;
  12. }
  13.  
  14. template <class PluginInterface, class FactoryInterface, class Parameter1>
  15. PluginInterface *qLoadPlugin1(const QFactoryLoader *loader,
  16. const QString &key,
  17. const Parameter1 meter1)
  18. {
  19. const int index = loader->indexOf(key);
  20. if (index != -1) {
  21. QObject *factoryObject = loader->instance(index);
  22. if (FactoryInterface *factory = qobject_cast<FactoryInterface *>(factoryObject))
  23. if (PluginInterface *result = factory->create(key, parameter1))
  24. return result;
  25. }
  26. return 0;
  27. }

插件生产者:Q<pluginType>Factory 类

最后来看Q<pluginType>Factory 类,它是Qt的插件载入机制中位于最上层的类,这个类一般主要实现两

个静态的方法(我们更关心 creat),并且都是静态的。

其定义大致例如以下:

  1. class Q<pluginType>Factory
  2. {
  3. public:
  4. static QStringList keys(...) ; // 获得与 Q<pluginType> 类型的插件相关的keyword列表,
  5. // keyword一般用于描写叙述插件的名称等属性,这个keyword列表中的每一个
  6. // 元素都相应一个实际的插件。如 QPlatformInputContextFactory::keys(...)
  7. // 返回的就是输入法插件的名称列表。
  8.  
  9. static Q<pluginType> * creat(...) ; // 返回一个Q<pluginType>类型的指针(应用程序所需的插件)
  10. }

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()上,这种方法的定义为:

  1. static QPlatformIntegration *platform_integration;
  2. static QPlatformIntegration *platformIntegration()
  3. { 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 文件里,它内部有一句:

  1. GuiApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name,
  2. arguments, argc, argv, platformPluginPath);
  3. ....

QPlatformIntegrationFactory::create 里又通过以下这句载入平台插件

  1. QPlatformIntegration *ret = loadIntegration(directLoader(), platform, paramList, argc, argv))

loadIntegration函数定义在 qtbase/src/gui/kernel/QPlatformIntegrationFactory.cpp中, 这个函数的作用和实现方法都与模板函数 qLoadPlugin 的实现类似。

  1. static inline QPlatformIntegration *loadIntegration(QFactoryLoader *loader, const QString &key, const QStringList meters, int &argc, char ** argv)
  2. {
  3. const int index = loader->indexOf(key);
  4. if (index != -1) {
  5.  
  6. // factory 指向相应的平台插件类的实例。如QLinuxFbIntegrationPlugin类;
  7. // 接着调用其creat方法生成并返回一个 QPlatformIntegration 类的实例的指针,
  8. // 这个指针将终于赋值给 QGuiApplicationPrivate::platform_integration,
  9. // 应用程序就得到了自己的执行平台.
  10. // 同一时候由此可知。假设想自己写一个QPA平台插件,仅仅需派生一个QPlatformIntegrationPlugin类和一个QPlatformIntegration类就可以。
  11. if (QPlatformIntegrationPlugin *factory = qobject_cast<QPlatformIntegrationPlugin *>(loader->instance(index)))
  12. if (QPlatformIntegration *result = factory->create(key, parameters, argc, argv))
  13. return result;
  14. }
  15. return 0;
  16. }

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

附 2 : Qt应用程序的输入法类插件QPlatformInputContext的载入过程

Linux xcb平台或linuxfb平台都是在平台插件 (QXcbIntegration或QLinuxFbIntegration类 ) 的initialize() 函数中 通过

调用 QPlatformInputContextFactory::create() 来初始化 平台的输入法插件(QPlatformInputContext类)的。



QPlatformInputContextFactory::create() 的实现例如以下:

  1. QPlatformInputContext *QPlatformInputContextFactory::create()
  2. {
  3. QPlatformInputContext *ic = 0;
  4.  
  5. QString icString = QString::fromLatin1(qgetenv("QT_IM_MODULE")); // 检測环境变量QT_IM_MODULE。依据它选择要载入的输入法插件
  6.  
  7. if (icString == QLatin1String("none"))
  8. return 0;
  9.  
  10. ic = create(icString); // 调用还有一个create函数载入输入法插件
  11. if (ic && ic->isValid())
  12. return ic;
  13.  
  14. // 以下的代码暂不理会
  15. delete ic;
  16. ic = 0;
  17.  
  18. QStringList k = keys();
  19. for (int i = 0; i < k.size(); ++i) {
  20. if (k.at(i) == icString)
  21. continue;
  22. ic = create(k.at(i));
  23. if (ic && ic->isValid())
  24. return ic;
  25. delete ic;
  26. ic = 0;
  27. }
  28.  
  29. return 0;
  30.  
  31. }
  32.  
  33. QPlatformInputContext *QPlatformInputContextFactory::create(const QString& key)
  34. {
  35. QStringList paramList = key.split(QLatin1Char(':'));
  36. const QString platform = paramList.takeFirst().toLower();
  37.  
  38. #if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
  39. if (QPlatformInputContext *ret = qLoadPlugin1<QPlatformInputContext, QPlatformInputContextPlugin>(loader(), platform, paramList)) // 依据key(插件名称)来载入相应的库并实例化(产生一个QPlatformInputContext类型的指针)。
  40. return ret;
  41. #endif
  42. return 0;
  43. }

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

附 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 框架中的插件载入机制概述的更多相关文章

  1. Qt Creator 中的插件Plugin, 区分说明。。。

    Qt Creator 中可以创建 三中类型的插件Plugin: 1.用的最多的,派生自QGenericPlugin类: 在新建Library,   Plugin类型工程中,新建. 调用使用QPlugi ...

  2. MVC框架中的值提供机制(三)

    在MVC框架中NameValueCollectionValueProvider采用一个NameValueCollection作为数据源,DictionnaryValueProvider的数据源类型自然 ...

  3. MVC框架中的值提供机制(二)

    在MVC框架中存在一些默认的值提供程序模板,这些值提供程序都是通过工厂模式类创建;在MVC框架中存在需要已Factory结尾的工厂类,在值提供程序中也存在ValueProviderFactories工 ...

  4. MVC框架中的值提供机制(一)

    在MVC框架中action方法中的Model数据的绑定的来源有很多个,可能是http请求中的get参数或是post提交的表单数据,会是json字符串或是路径中的相关数据;MVC框架中针对这些不同的数据 ...

  5. android插件化-获取apkplug框架已安装插件-03

    上一篇文章成功的将apkplug框架嵌入了应用中而且启动 链接http://www.apkplug.com/blog/?post=10 这一篇文章实现怎样获取全部已安装插件 一 获取框架的System ...

  6. Java回调机制在RPC框架中的应用示例

    完整源码: https://gitee.com/shiyanjun/x-callback-demo 应用场景描述: 服务提供者在项目启动时,创建并启动一个TCP服务器,然后将自己提供的所有服务注册到注 ...

  7. QT5.8+vs2015配置以及qt creater中出现中文乱码解决办法之一

    1.参考此文档:QT5.6+vs2015配置: 2.出现乱码问题时候 在头文件上加入: #pragma execution_character_set("utf-8") //加入这 ...

  8. C++反射机制:可变参数模板实现C++反射(使用C++11的新特性--可变模版参数,只根据类的名字(字符串)创建类的实例。在Nebula高性能网络框架中大量应用)

    1. 概要   本文描述一个通过C++可变参数模板实现C++反射机制的方法.该方法非常实用,在Nebula高性能网络框架中大量应用,实现了非常强大的动态加载动态创建功能.Nebula框架在码云的仓库地 ...

  9. 【转】【译】JavaScript魔法揭秘--探索当前流行框架中部分功能的处理机制

    推荐语: 今天推荐一篇华为同事的同事翻译的一篇文章,推荐的主要原因是作为一个华为员工居然晚上还能写文章,由不得小钗不佩服!!! 其中的jQuery.angular.react皆是十分优秀的框架,各有特 ...

随机推荐

  1. Redis字符串(STRING)中BIT相关命令

    上篇文章我们对STRING数据类型中一些基本的命令进行了介绍,但是没有涉及到BIT相关的命令,本文我们就来看看几个和BIT相关的命令. 本文是Redis系列的第四篇文章,了解前面的文章有助于更好的理解 ...

  2. python_购物车程序

    #需求1.启动程序后,让用户输入工资,然后打印商品列表2.允许用户根据商品编号购买商品3.用户选择商品后,检测余额是否够,够就直接扣款,不够就提醒4.可随时退出,退出时,打印已购买商品和余额 #先定义 ...

  3. java application指的是什么

    在Java语言中,能够独立运行的程序称为Java应用程序(Application).Java语言还有另外一种程序——Applet程序.Applet程序(也称Java小程序)是运行于各种网页文件中,用于 ...

  4. linux文件与用户和群组

    文件基本属性 在图片中alogrithm的文件属性为drwxrwxr-x,其中d代表此文件为目录. 后面rwx,rwx,r-x分别代表文件所属者(ower),组(group),其他用户(other)的 ...

  5. JAVA学习之 异常处理机制

    今天就来说说java的异常处理机制,异常处理不是第一接触,尤其是写过非常多c#的代码,基本都会写到异常处理的代码,事实上c#的异常处理与java的异常处理基本都是一样的,仅仅是在一些细节上不是非常一样 ...

  6. hdu Swipe Bo(bfs+状态压缩)错了多次的题

    Swipe Bo Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total S ...

  7. bzoj 1600 &amp; Usaco 月赛 2008 建造栅栏 题解

    [原题] 1600: [Usaco2008 Oct]建造栅栏 Time Limit: 5 Sec  Memory Limit: 64 MB Submit: 785  Solved: 443 [Subm ...

  8. hdu 4544 湫湫系列故事——消灭兔子 优先队列+贪心

    将兔子的血量从小到大排序,箭的威力也从小到大排序, 对于每仅仅兔子将威力大于血量的箭增加队列,写个优先队列使得出来数位价钱最少.. #include<stdio.h> #include&l ...

  9. 每一个JavaScript开发者都应该知道的10道面试题

    JavaScript十分特别.而且差点儿在每一个大型应用中起着至关关键的数据.那么,究竟是什么使JavaScript显得与众不同,意义非凡? 这里有一些问题将帮助你了解其真正的奥妙所在:   1.你能 ...

  10. 【LeetCode-面试算法经典-Java实现】【058-Length of Last Word (最后一个单词的长度)】

    [058-Length of Last Word (最后一个单词的长度)] [LeetCode-面试算法经典-Java实现][全部题目文件夹索引] 原题 Given a string s consis ...