zoukankan      html  css  js  c++  java
  • Qt 插件学习(一)

    插件是什么

    • 注意:这儿暂时不考虑静态插件(潜意识中总觉得它根本就不算插件)。

    插件是一个动态库(共享库)。动态库是一个独立的文件中的独立模块,可被多个程序访问。

    先看动态库的两种用法

    1. 程序链接时指明动态库

    这时程序中包含相应的头文件,编译时指定头文件路径,对于qmake来说:

    LIBS += -L/path1/path2/.../ -labcd
    INCLUDEPATH += /p1/p2/.../

    这样一来,程序启动时会自动加载需要的链接库。

    2. 程序中动态加载动态库

    运行过程中找到来查找某个动态库,加载并解析出其中的某个函数。 Qt提供了QLibrary这个类来封装各个平台下的差异。

    插件是提供特定接口的动态库

    • 它是动态库
    • 它需要一个或几个特定的接口(这样程序才能感知它)
    • 它采用第二种加载方式
    • Qt 为插件提供了 QPluginLoader,比通用 QLibrary 好用

    Qt插件位置

    动态加载,那么程序怎么知道去哪儿加载插件呢?

    很多人抱怨,装有Qt的机器上一切正常,发布后,jpeg等格式图片看不成,数据库无法连接,汉字乱码...

    其实答案很简单:

    QCoreApplication 的 libraryPaths() 中有一些路径,比如:

    • $QTDIR/plugins
    • 可执行程序所在文件夹

    然后程序启动后,会去这些路径下的下列子目录

    • imagesformats

      中 找图片插件

      sqldrivers

      中 找数据库驱动插件

      codecs

      中 找字符的编解码插件

      ...

    这就是为什么:将 jpeg4.dll 放到可执行程序所在文件夹的 imagesformats 中即可解决问题的原因。

    除此之外,要设置插件路径,我们还可以修改:

    QCoreApplication::libraryPaths()

    通过 addLibraryPath()

    QLibraryInfo::location(QLibraryInfo::PluginsPath)

    通过 qt.conf 文件

    QT_PLUGIN_PATH

    环境变量

    Manual 中对此有详细介绍。有关插件路径问题,请考虑 http://blog.csdn.net/dbzhang800/archive/2011/06/14/6543489.aspx

    插件API

    Qt 提供了两个层次的Api

    Higher-level

    用于扩展Qt自身的工程,比如前述的图片插件、编解码插件等等
    前面的插件位置提到的主要是这个

    Lower-level

    用于扩展 应用程序的功能,比如 QtCreator 本身用了非常多的专有插件

    higher-level

    这部分,插件本身的api很简单,困难在功能实现上。比如实现一个 style 插件:

    • 派生 QStyle 或它的子类,实现自己的 Style 类(难点)
    • 派生 QStylePlugin,实现插件,在它内部创建 Style 的对象

    这两个都没什么好说的,只是一个宏特别关键:

    Q_EXPORT_PLUGIN2(PluginName, ClassName)

    看它的源代码,也是一堆宏,贴出来也不好看

    # define Q_EXPORT_PLUGIN2(PLUGIN, PLUGINCLASS)
    Q_PLUGIN_VERIFICATION_DATA
    Q_EXTERN_C Q_DECL_EXPORT
    const char * Q_STANDARD_CALL qt_plugin_query_verification_data()
    { return qt_plugin_verification_data; }
    Q_EXTERN_C Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QObject) * Q_STANDARD_CALL qt_plugin_instance()
    Q_PLUGIN_INSTANCE(PLUGINCLASS)

    展开看一下:

    static const char qt_plugin_verification_data[] =
    "pattern=QT_PLUGIN_VERIFICATION_DATA "
    "version=4.7.0 debug=false buildkey=xxx";

    extern "C" Q_DECL_EXPORT const char * qt_plugin_query_verification_data()
    {
    return qt_plugin_verification_data;
    }

    extern "C" Q_DECL_EXPORT qt_plugin_instance()
    {
    static QPointer<QObject> _instance;
    if (!_instance)
    _instance = new ClassName;
    return _instance;
    }

    这样一来舒服多了,导出了两个函数:

    • 一个返回值是一个长长的字符串,用来校验差价和程序所用Qt版本什么的是否匹配
    • 另一个返回的是插件类的对象,这样通过该函数就可以使用插件了。

    lower-level

    这一部分和本文其他部分关系不是太紧密,而且内容可能比较多,还是单独出来准备"插件学习二"吧。

    • 不像图片插件、数据库插件等,Qt为他们提供了现成的接口。这儿我们必须实现自己的接口(别人也不清楚我们的需求,对吧)

    静态插件

    • 这个东西怎么说呢,尽管静态编译过Qt,但基本没用过。所以在这静态插件上面应该更没有什么发言权。
    • 熟悉Qt静态编译的,应该对此更熟一些,因为这是插件都是静态的。

    静态插件使用

    这时它是一个静态库。静态库如何使用?不用多说了,肯定直接链接到程序中。于是我们需要:

    • 包含头文件
    • 指定链接库

    对与Qt提供的 higher-level 插件,比如图片插件 jpeg,这意味着:

    代码中,(使用宏,也就是使动态编译时它不起作用)

    • #include<QtPlugin>

    • Q_IMPORT_PLUGIN(qjpeg)

    pro文件内,(指定要链接的库)

    • QTPLUGIN += qjpeg

    静态插件用到的宏

    当静态编译时,插件中的宏

    Q_EXPORT_PLUGIN2(PLUGINNAME, ClassName)

    展开为

    qt_plugin_instance_PLUGINNAME()
    {
    static QPointer<QObject> _instance;
    if (!_instance)
    _instance = new ClassName;
    return _instance;
    }

    同时,工程中的宏

    Q_IMPORT_PLUGIN(PluginName)

    展开为:

    QObject *qt_plugin_instance_PLUGINNAME();
    class StaticPLUGINNAMEPluginInstance
    {
    public:
    StaticPLUGINNAMEPluginInstance() 
    {
    qRegisterStaticPluginInstanceFunction(qt_plugin_instance_PLUGINNAME);
    }
    };
    static StaticPLUGINNAMEPluginInstance staticPLUGINNAMEInstance;

    这也很容易理解,Q_EXPORT_PLUGIN2(PLUGINNAME, ClassName) 中的第一个参数做什么用了。

    参考

  • 相关阅读:
    云原生技术实践,起步k8s
    线程,同步构造
    线程,限制的异步操作
    DotNet Core管道通信
    打下设计模式
    739--Daily Temperatures
    1021--RemoveOutermostParentheses
    Java并行程序基础。
    155--MinStack
    1047--Remove All Adjacent Duplicates In String
  • 原文地址:https://www.cnblogs.com/lvdongjie/p/3758094.html
Copyright © 2011-2022 走看看