zoukankan      html  css  js  c++  java
  • Qt5的插件机制(1)--Qt 框架中的插件载入机制概述

    概述

    Qt的源代码中通过 Q<pluginType>Factory、Q<pluginType>Plugin 和 Q<pluginType> 这三个类实现了Qt的插件载入机制,

    这个机制可用于载入特定种类的插件。

    比方通过 QPlatformIntegrationFactoryQPlatformIntegrationPluginQPlatformIntegration
    三个类能够实现平台类QPA插件(PlatformIntegration)的载入,通过QPlatformInputContextFactoryQPlatformInputContextPlugin
    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文件里使用)




  • 相关阅读:
    使用visio 2007对现有的数据库进行反向工程
    GOOGLE地图坐标拾取方法、GOOGLE地图获取坐标方法
    Visio 2007中进行数据库建模时如何显示字段类型以及概念名称
    WCF把书读薄(4)——事务编程与可靠会话
    WCF把书读薄(3)——数据契约、消息契约与错误契约
    WCF把书读薄(2)——消息交换、服务实例、会话与并发
    Backbone.js developer 武汉 年薪8w-10w
    Java面试
    从pb文件中恢复计算图并在tensorboard中展示
    Ubuntu下解决u盘变成只读模式
  • 原文地址:https://www.cnblogs.com/clnchanpin/p/7048888.html
Copyright © 2011-2022 走看看