<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

JSON 与Qt插件的元信息 MetaData

Qt插件的源代码中。基本都能见到一个 xxx.json 的文件,这个文件里通常仅仅包括一句:



{

    "Keys": [ "yyy" ]

}



我们能够猜到这个文件里的"Keys"应该是指定了与插件相关的keyword。那这个 .json 文件究竟是怎样起作用的?

先来认识一下 JSON .



JSON是一种存储结构化数据的格式,它有6中基本数据类型。各自是:



bool    布尔型。取值能够是 true 或 false

double    数字类型

string    字符串类型

array    数组类型

object    对象类型

null    空类型



详细可參见 Qt Assistant 中关于"JSON Support in Qt "的介绍。



A simple JSON document encoding a person, his/her age, address and phone numbers could look like:

{
"FirstName": "John", # FirstName是变量(字段)的名称;John是变量的值
"LastName": "Doe",
"Age": 43,
"Address": {
"Street": "Downing Street 10",
"City": "London",
"Country": "Great Britain"
},
"Phone numbers": [
"+44 1234567",
"+44 2345678"
]
}

值得一提的是,数组类型的字段在.json文件里赋值时应该用方括号 '[' 和 ']' 括起来,对象类型的字段在赋值时

应用花括号 '{' 和 '}' 括起来,普通类型的数据则不须要括。每个 .json 文件描写叙述了一个 JSON对象,而一个JSON

对象中的对象类型字段,又能够看做是一个子JSON对象(JSON对象的嵌套)。

再回过头来看看插件源代码中的 xxx.json 文件,能够发现当中的变量 Keys 事实上是个数组变量(由于它赋值时用方括号

括住了),仅仅只是这个数组中通常仅仅有一个元素而已。即一个插件一般仅仅有一个keyword,当然也能够为一个插件设置多个

keyword。



.json在Qt插件中主要用于存储Qt插件的元信息(metaData)。在Qt中,有一个专门的类 QJsonObject 来描写叙述一个JSON。

QLibraryPrivate::metaData() 返回的是一个QJsonObject类的指针,通过这个指针能够訪问该 QLibraryPrivate 对象相应的库的元信息;

QFactoryLoader::metaData() 返回的是一个 QList<QJsonObject> 列表,这个列表中顺序存放了与该 QFactoryLoader 对象相关的全部库

的元信息(动态库的元信息在前,静态库的元信息在后)。

另外值得注意的是,QLibraryPrivate::metaData() 返回的QJsonObject对象中一般都有一个 MetaData 字段,这个字段是对象类型的数据。

他能够看做是库的元信息的一个子JSON对象,并且它相应的就是 .json 文件里的内容。

(.json文件里的内容仅仅是一个库的元信息的一部分)



每一个QFactoryLoader对象都有一个 d->iid 成员 (d是与QFactoryLoader对象关联的QFactoryLoaderPrivate实例)。可用于描写叙述插件的种类,

每一类插件都有一个独立的 IID 值, 比方平台输入法类插件的IDD都应该是 org.qt-project.Qt.QPlatformInputContextFactoryInterface,

平台类插件的IDD都应是 org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.2 等。







<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

QLibraryPrivate的metaData(库的元信息)的产生过程

在编写Qt插件时须要用到 Q_PLUGIN_METADATA 这个宏来设置一个插件的元信息,但在Qt的源代码中。发现这个宏是空的。所以不用说。这个

宏是被MOC解析的而不是C++编译器。

那就看看MOC会怎么对付这个宏吧。找了个非常easy的文件。用moc处理一下,看看都生成了什么。

main.cpp文件内容例如以下。当中使用了Q_PLUGIN_METADATA宏

#include <qpa/qplatforminputcontextplugin_p.h>
#include <QtCore/QStringList>
#include "QtMinimalInputMethodFrame.h"
QT_BEGIN_NAMESPACE
class QtMinimalInputMethodFramePlugin : public QPlatformInputContextPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPlatformInputContextFactoryInterface" FILE "qtminimal.json") // 指定了IID和.json文件 public:
QtMinimalInputMethodFrame *create(const QString &, const QStringList &);
};
QtMinimalInputMethodFrame *QtMinimalInputMethodFramePlugin::create(const QString &system, const QStringList ¶mList)
{
Q_UNUSED(paramList);
if (system.compare(system, QStringLiteral("qtminimal"), Qt::CaseInsensitive) == 0)
return new QtMinimalInputMethodFrame;
return 0;
}
QT_END_NAMESPACE
#include "main.moc"

qtminimal.json文件内容例如以下:

{
"Keys": [ "qtminimal" ]
}

使用 "moc main.cpp moc_main.cpp" ,生成moc_main.cpp文件,打开。发现里面有一部分代码例如以下:

    ...
... static const unsigned char qt_pluginMetaData[] = {
'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', ' ', // "QTMETADATA",这段字符串可看做Qt的插件元信息的头,通过这个keyword能搜索到元信息的位置
0x71, 0x62, 0x6a, 0x73, 0x01, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
0xec, 0x00, 0x00, 0x00, 0x1b, 0x03, 0x00, 0x00,
0x03, 0x00, 0x49, 0x49, 0x44, 0x00, 0x00, 0x00,
0x37, 0x00, 0x6f, 0x72, 0x67, 0x2e, 0x71, 0x74,
0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74,
0x2e, 0x51, 0x74, 0x2e, 0x51, 0x50, 0x6c, 0x61,
0x74, 0x66, 0x6f, 0x72, 0x6d, 0x49, 0x6e, 0x70,
0x75, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78,
0x74, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79,
0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63,
0x65, 0x00, 0x00, 0x00, 0x9b, 0x0c, 0x00, 0x00,
0x09, 0x00, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e,
0x61, 0x6d, 0x65, 0x00, 0x1f, 0x00, 0x51, 0x74,
0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x61, 0x6c, 0x49,
0x6e, 0x70, 0x75, 0x74, 0x4d, 0x65, 0x74, 0x68,
0x6f, 0x64, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x50,
0x6c, 0x75, 0x67, 0x69, 0x6e, 0x00, 0x00, 0x00,
0x5a, 0x60, 0xa0, 0x00, 0x07, 0x00, 0x76, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00,
0x11, 0x00, 0x00, 0x00, 0x05, 0x00, 0x64, 0x65,
0x62, 0x75, 0x67, 0x00, 0x95, 0x16, 0x00, 0x00,
0x08, 0x00, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61,
0x74, 0x61, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
0x14, 0x03, 0x00, 0x00, 0x04, 0x00, 0x4b, 0x65,
0x79, 0x73, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x09, 0x00, 0x71, 0x74, 0x6d, 0x69, 0x6e, 0x69,
0x6d, 0x61, 0x6c, 0x00, 0x8b, 0x01, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
0xa4, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
0x98, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00
};
...
... // 留意一下QT_MOC_EXPORT_PLUGIN这个宏
QT_MOC_EXPORT_PLUGIN(QtMinimalInputMethodFramePlugin, QtMinimalInputMethodFramePlugin) ...
...

上面的代码中生成了一个名为 qt_pluginMetaData 的数组。看其名字就能猜出来这个数组是与插件的元信息有关的,

将该数组用字符显示,结果为:

QTMETADATA  qbjs^A^@^@^@^@^A^@^@^K^@^@^@ì^@^@^@^[^C^@^@^C^@
IID^@^@^@7^@org.qt-project.Qt.QPlatformInputContextFactoryInterface // IDD
^@^@^@<9b>^L^@^@^@
className^@^_^@QtMinimalInputMethodFramePlugin // 类名
^@^@^@Z` ^@^G^@
version^@^@^@^Q^@^@^@^E^@ // 版本号
debug^@<95>^V^@^@^H^@ // debug
MetaData^@^@8^@^@^@^C^@^@^@4^@^@^@^T^C^@^@^D^@ // MetaData
Keys^@^@^\^@^@^@^B^@^@^@^X^@^@^@^@qtminimal // Keys
^@<8b>^A^@^@^L^@^@^@^L^@^@^@¤^@^@^@T^@^@^@<98>^@^@^@<88>^@^@^@

能够看到当中已经包括了IDD、Keys等信息,因此能够确定与插件相关的元信息就是存在这个数组中。

上面给出的代码最后。使用了QT_MOC_EXPORT_PLUGIN宏,它的作用是定义两个函数。qt_plugin_instance() 和

qt_plugin_query_metadata(),前者用于返回插件类(QtMinimalInputMethodFramePlugin)的一个实例,后者用于

返回数组qt_pluginMetaData的指针。

因此。当这个库被载入后。通过库中的qt_plugin_query_metadata()函数就能够获得

库的元信息数组(二进制数据)。

#define Q_PLUGIN_INSTANCE(IMPLEMENTATION) \    // 这个宏用于返回一个类名是IMPLEMENTATION的类的静态实例
{ \
static QT_PREPEND_NAMESPACE(QPointer)<QT_PREPEND_NAMESPACE(QObject)> _instance; \
if (!_instance) \
_instance = new IMPLEMENTATION; \
return _instance; \
} #if defined(QT_STATICPLUGIN)
// 这部分是静态编译时用的宏。我们暂无论它
# define QT_MOC_EXPORT_PLUGIN(PLUGINCLASS, PLUGINCLASSNAME) \
static QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance_##PLUGINCLASSNAME() \
Q_PLUGIN_INSTANCE(PLUGINCLASS) \
static const char *qt_plugin_query_metadata_##PLUGINCLASSNAME() { return (const char *)qt_pluginMetaData; } \
const QT_PREPEND_NAMESPACE(QStaticPlugin) qt_static_plugin_##PLUGINCLASSNAME() { \
QT_PREPEND_NAMESPACE(QStaticPlugin) plugin = { qt_plugin_instance_##PLUGINCLASSNAME, qt_plugin_query_metadata_##PLUGINCLASSNAME}; \
return plugin; \
} #else
// 这部分才是动态编译时用的宏
# define QT_MOC_EXPORT_PLUGIN(PLUGINCLASS, PLUGINCLASSNAME) \
Q_EXTERN_C Q_DECL_EXPORT \
const char *qt_plugin_query_metadata() \ // 这个函数返回一个指针,指向元信息数组
{ return (const char *)qt_pluginMetaData; } \
Q_EXTERN_C Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance() \ 这个函数返回插件类PLUGINCLASS的一个静态实例
Q_PLUGIN_INSTANCE(PLUGINCLASS) #endif

我们尽管能够通过qt_plugin_query_metadata()函数获得库的元信息数组(二进制数据),可是还须要把这些

二进制数据转换成 QJsonObject 对象。这样才方便在程序中对其各个字段进行訪问。

QLibraryPrivate类有个

静态成员函数fromRawMetaData。能够从二进制数据中构造一个 QJsonDocument 对象,

而QJsonDocument对象的 object() 方法就能返回所需的 QJsonObject 对象(库的元信息)。

通过这些能够推測,QLibraryPrivate类的 metaData 成员是这样被设置的:

载入一个库文件后,将库中的qt_plugin_query_metadata()这个符号resolve出来。并通过它得到数组 qt_pluginMetaData 的指针,

然后调用QLibraryPrivate类的fromRawMetaData方法讲二进制存储的元信息转换到一个QJsonObject 对象中,最后将这个对象赋值

给QLibraryPrivate类的metaData成员。





如今问题是。 QLibraryPrivate的metaData是在哪里,被谁设置的?



当第一次使用 QLibraryPrivate类的 isPlugin() 方法调查一个库是否是插件时。这种方法内部会调用 QLibraryPrivate::updatePluginState() 。

bool QLibraryPrivate::isPlugin()
{
if (pluginState == MightBeAPlugin) // pluginState在构造函数中会被初始化为MightBeAPlugin
updatePluginState(); return pluginState == IsAPlugin;
}

而QLibraryPrivate::updatePluginState()函数中。须要读取QLibraryPrivate类的 metaData 信息来确认一个库是不是插件。而 metaData 也就是在这时

被设置的。从这个函数中还能够看出,仅仅有Qt插件才有 metaData 元信息。 普通的库是没有的(这一点我上不能打保票。只是从以下这个函数的代码上看,应该

是这种)。

void QLibraryPrivate::updatePluginState()
{
errorString.clear();
if (pluginState != MightBeAPlugin)
return; // 假设pluginState的状态已经确定了,则直接返回 bool success = false; #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
if (fileName.endsWith(QLatin1String(".debug"))) {
// refuse to load a file that ends in .debug
// these are the debug symbols from the libraries
// the problem is that they are valid shared library files
// and dlopen is known to crash while opening them // pretend we didn't see the file
errorString = QLibrary::tr("The shared library was not found.");
pluginState = IsNotAPlugin;
return;
}
#endif // 这里開始设置 metaData 。findPatternUnloaded() 和 qt_get_metadata() 两个函数的内部都会设置 metaData
if (!pHnd) {
// 假设库还没有被载入。(pHnd==NULL说明库未被载入)。则调用findPatternUnloaded函数。它内部会
// 讲相应的库文件打开,读取其内容。在当中寻找Qt的插件元信息的头Header(就是前面提到的"QTMETADATA")。
// 假设找到了。就解析元信息。这样的方法的优点是在不载入库的情况下也能获得其元信息。
// scan for the plugin metadata without loading
success = findPatternUnloaded(fileName, this);
} else {
// 假设库已被载入,则调用qt_get_metadata获得元信息。
// QtPluginQueryVerificationDataFunction是一个函数指针类型。其详细类型是 : char*(*)(), 它返回
// 一个字符指针(指向一个二进制数组),准确的说,他应该返回上面提到的qt_pluginMetaData数组的指针 。 // library is already loaded (probably via QLibrary)
// simply get the target function and call it.
QtPluginQueryVerificationDataFunction getMetaData = NULL;
getMetaData = (QtPluginQueryVerificationDataFunction) resolve("qt_plugin_query_metadata");
// 上面这行将库中的qt_plugin_query_metadata符号resolve出来
success = qt_get_metadata(getMetaData, this);
} if (!success) {
// 假设获取 元信息 失败。即当前库没有元信息,就将pluginState设置为IsNotAPlugin。代表非Qt插件。
// 这里由于当前库没有元信息,就觉得它不是Qt插件。所以这是不是意味着。仅仅有Qt插件才有元信息而普通库是没有的?
if (errorString.isEmpty()){
if (fileName.isEmpty())
errorString = QLibrary::tr("The shared library was not found.");
else
errorString = QLibrary::tr("The file '%1' is not a valid Qt plugin.").arg(fileName);
}
pluginState = IsNotAPlugin;
return;
} pluginState = IsNotAPlugin; // be pessimistic // 假设获取 元信息 成功,再看版本是否符合要求。假设版本合适,则将pluginState设置为IsAPlugin。代表是Qt插件
uint qt_version = (uint)metaData.value(QLatin1String("version")).toDouble();
bool debug = metaData.value(QLatin1String("debug")).toBool();
if ((qt_version & 0x00ff00) > (QT_VERSION & 0x00ff00) || (qt_version & 0xff0000) != (QT_VERSION & 0xff0000)) {
if (qt_debug_component()) {
qWarning("In %s:\n"
" Plugin uses incompatible Qt library (%d.%d.%d) [%s]",
(const char*) QFile::encodeName(fileName),
(qt_version&0xff0000) >> 16, (qt_version&0xff00) >> 8, qt_version&0xff,
debug ? "debug" : "release");
}
errorString = QLibrary::tr("The plugin '%1' uses incompatible Qt library. (%2.%3.%4) [%5]")
.arg(fileName)
.arg((qt_version&0xff0000) >> 16)
.arg((qt_version&0xff00) >> 8)
.arg(qt_version&0xff)
.arg(debug ? QLatin1String("debug") : QLatin1String("release"));
#ifndef QT_NO_DEBUG_PLUGIN_CHECK
} else if(debug != QLIBRARY_AS_DEBUG) {
//don't issue a qWarning since we will hopefully find a non-debug? --Sam
errorString = QLibrary::tr("The plugin '%1' uses incompatible Qt library."
" (Cannot mix debug and release libraries.)").arg(fileName);
#endif
} else {
pluginState = IsAPlugin;
}
}

版权声明:本文博客原创文章。博客,未经同意,不得转载。

Qt5该插件机制(4)--QtMeta信息窗口小部件metaData的更多相关文章

  1. Android开发5:应用程序窗口小部件App Widgets的实现

    前言 本次主要是实现一个Android应用,实现静态广播.动态广播两种改变 widget内容的方法,即在上篇博文中实验的基础上进行修改,所以此次实验的重点是AppWidget小部件的实现啦~ 首先,我 ...

  2. Android 之窗口小部件详解--App Widget

    Android 之窗口小部件详解--App Widget  版本号 说明 作者 日期  1.0  添加App Widge介绍和示例  Sky Wang 2013/06/27        1 App ...

  3. Android 之窗口小部件详解(三)  部分转载

    原文地址:http://blog.csdn.net/iefreer/article/details/4626274. (一) 应用程序窗口小部件App Widgets 应用程序窗口小部件(Widget ...

  4. Android 之窗口小部件高级篇--App Widget 之 RemoteViews - 跨到对岸去

    在之前的一篇博文( Android 之窗口小部件详解--App Widge t)中,已经介绍了App Widget的基本用法和简单实例.这篇主要讲解 App Widget 的高级内容,即通过 Remo ...

  5. Android 之窗口小部件高级篇--App Widget 之 RemoteViews

    Android 之窗口小部件高级篇--App Widget 之 RemoteViews 在之前的一篇博文(Android 之窗口小部件详解--App Widget)中,已经介绍了App Widget的 ...

  6. 在android程序中加入widget(窗口小部件)并与之交互的关键代码

    摘要: widget(窗口小部件)可以增强应用程序的交互性, 是很多应用中都会用到的功能,本文不求大而全,但是会给出程序与widget交互的关键代码 正文: 其实widget是嵌入(embedded) ...

  7. Android Widget(窗口小部件)

    Android Widget简介 应用程序窗口小部件(Widget)是微型的应用程序视图,它可以被嵌入到其它应用程序中(比如桌面)并接收周期性的更新.你可以通过一个App Widget Provide ...

  8. Qt5该插件机制(2)--QxxxFactory类和QFactoryLoader类别

    <<<<<<<<<<<<<<<<<<<<<<<<< ...

  9. Qt5的插件机制(6)--开发Qt插件时几个重要的宏

    怎样开发Qt插件,能够在Qt Assistant 中搜索"Qt Plugins"或"How to Create Qt Plugins",看看那篇manual中的 ...

随机推荐

  1. EJB体系结构

    为了适应企业的快速发展.缩短企业信息系统的设计和开发周期.降低构建信息系统的成本,Sun公司制订了Java2 SDK Enterprise Edition(J2EE)规范,定义基于组件的方式设计.开发 ...

  2. cocos2d-x: 33种切换场景

    [1]:CCTransitionCrossFade::create(时间,目标场景); //慢慢淡化到还有一场景 [2]:CCTransitionFade::create(时间,目标场景); //本场 ...

  3. 多线程——达到Runnable介面

    部分博客(多线程--继承Thread类)介绍了java多线程的第一种实现方法--继承Thread类.这篇博客介绍另外一种方法--实现Runnable接口,并实现run方法. 还用上篇博客的样例.如今用 ...

  4. java线程学生进实训室

    Instructor: Dr. Simina FlutureCSCI 34 CSCI 34 CSCI 34CSCI 34 0 Summer 201 ummer 201 ummer 201ummer 2 ...

  5. Codeforces 32E Hide-and-Seek 乞讨2关于镜面反射点 计算几何

    主题链接:点击打开链接 必须指出的是,反射镜和2个人共线是不是障碍,但根据该壁其他情况 #include<cstdio> #include<iostream> #include ...

  6. 《Linux Device Drivers》 第十七章 网络驱动程序——note

    基本介绍 第三类是标准的网络接口Linux设备,本章介绍的内核,其余的交互网络接口描述 网络接口,必须使用特定的内核数据结构本身注册,与外部分组交换数据线打电话时准备 经常使用的文件上的网络接口操作是 ...

  7. C#读取excel等表格常用方法

    0. 利用NPOI. 请查阅此插件的相关文档. 1.方法一:采用OleDB读取EXCEL文件: 把EXCEL文件当做一个数据源来进行数据的读取操作,实例如下: 1 2 3 4 5 6 7 8 9 10 ...

  8. Javascript中的__proto__、prototype、constructor

    今天重温了下Javacript,给大家带来一篇Javascript博文,相信对于Javacript有一定了解的人都听过prototype原型这个概念,今天我们深度的分析下prototype与__pro ...

  9. iOS英语—》中国本土化,如调用专辑,摄像头的变化“cancel”,“photos”至“撤消”,“摄像头”

    呼叫系统相册.系统相簿界面后标题显示"photos",可是手机语言已经设置显示中文,纠结半天,终于在info.plist设置解决这个问题. 仅仅须要改三个地方: 1.plist文件 ...

  10. 学习通过Thread+Handler实现非UI线程更新UI组件(转)

    [Android线程机制] 出于性能考虑,Android的UI操作并不是线程安全的,这就意味着如果有多个线程并发操作UI组件,可能导致线程安全问题.为了解决这个问题,Android制定了一条简单的规则 ...