“美的事物是永恒的喜悦” --- 济慈

                                                                     


对于qml引擎的解读,该系列总共有四篇文章。文章内容参考了国外的相关系列文章 QML Engine Internals

该系列博文都是基于qt5的QtQuick2.0。

每一个qml基本类型都对应了一个C++类,当你写的qml文件被加载时,qml引擎最终会为文件中的每个基本类型创建一个C++类对象,这些对象被按照树的结构组织起来。关于qt qml的基本用法,qt官网上有说明http://doc.qt.io/qt-5/qtqml-index.html, 不熟悉的人可以参照学习。

以下面的例子作为说明:

import QtQuick 2.0

Rectangle {
id: root
width: 360
height: width + 50
color: "lightsteelblue"
property alias myWidth: root.width
property int counter: 1 function reactToClick() {
root.counter++
} Text {
id: text
text: qsTr("Hello World: " + counter)
anchors.centerIn: parent
} MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: {
reactToClick()
}
}
}

该文件包含了三个基本的qml基本元素, Rectangle, Text and MouseArea. 分别对应C++类: QQuickRectangle, QQuickText and QQuickMouseArea. 这些类对qt用户来说是不可见的。 这些元素最终会被qt的内部机制通过调用Opengl来绘制出来,绘制和事件处理的都是由QQuickWindow类来进行管理的,例如,如果系统由专门的渲染线程的话,该类负责与该线程进行交互。

可以借助 KDAB’s Qt introspection tool, Gammaray,来查看针对qml文件生成的c++对象树,例如上面的qml文件对应的对象树如下所示:

上图中类QQuickMouseArea 和QQuickText 按照预期出现在了树中,但是,类QQuickRectangle_QML_0是什么呢? 在qt中没有这个c++类,在后续的文章中会给出解释的,这里暂且将其看作是类QQuickRectangle 。

QML profiler来分析加载该qml文件的程序,得到:

从上图可以看出,场景绘制的过程中,Creating 和 painting阶段花费了一些时间。其中的compiling阶段具体是在干什么呢,这就需要仔细研究qml文件被qml引擎加载的过程了。

加载QML文件的步骤:

1)Parsing     2)Compiling  3)Creating

下面分别进行介绍:


1) Parsing阶段:

First of all, the QML file is parsed, which is handled by QQmlScript::Parser. Most of the parser internals are auto-generated from a grammar file. The abstract syntax tree (AST) of our example looks like this:

首先,qml文件被 QQmlScript::Parser解析,通过语法解析后,会建立一个abstract syntax tree(AST),即抽象语法树,对于上面的qml文件,对应的语法树如下:

这个AST是相当底层的,了解一下即可。然后,该语法树会被一个visitor进行遍历,将其转换成一个较为高层的数据结构,该数据结构包含 Objects, PropertiesValues,其中Objects对应QML元素,property/value对应的是 QML中的属性/值,例如color属性的取值为lightblue,另外,信号和信号对应的槽函数亦可以看作是属性/值,例如 onClicked信号及其对应的槽函数(Javascript function)。

2) Compiling  阶段:

至此,得到 Objects, PropertiesValues结构之后,这些信息对于创建相应的C++类对象并为对象的属性赋值 已经足够了,但是,为了提高效利,qml引擎并不会直接用这些数据来建立c++对象,而是先用对这些数据进行处理,并生成 QQmlCompiledData object ,这个过程就是compiling阶段,对应QML profiler中的compiling阶段!!  之所以有这个过程,是因为使用 QQmlCompiledData 来建立c++对象更快。 例如,有一个Button.qml文件,该文件会经常被其他的qml文件使用,这个文件会仅仅被compile一次,生成的QQmlCompiledData会被保存,每当Button被使用的时候,只需读取这份被保存的数据来创建一个c++对象即可。

To sum up: Parsing a QML file and compiling it is only done once, after that the QQmlCompiledData object is used to quickly create the C++ objects. The next step is creating.

3) Creating阶段:

这里不对QQmlCompiledData进行分析,但是QQmlCompiledData中的一个成员是值得提到的: “QByteArray bytecode” 。bytecode中包含了关键的信息:建立c++对象的说明,为对象的属性附上什么值。bytecode之外的其他成员仅仅起到辅助作用。

在creating阶段,class QQmlVME负责解析包含了大量关键信息的bytecode,其作用相当于一个interpreter。阅读QQmlVME::run(), 函数可以发现该interpreter遍历bytecode中包含的所有instructions,对每一种instructions都会有像一个的处理分支。 在运行app是令QML_COMPILER_DUMP=1,我们可以bytecode中包含的instructions:

Index           Operation               Data1   Data2   Data3   Comments
-------------------------------------------------------------------------------
0 INIT 4 3 0 0
1 INIT_V8_BINDING 0 17
2 CREATECPP 0
3 STORE_META
4 SETID 0 "root"
5 BEGIN 16
6 STORE_INTEGER 45 1
7 STORE_COLOR 41 "ffb0c4de"
8 STORE_COMPILED_BINDING 10 2 0
9 STORE_DOUBLE 9 360
10 FETCH_QLIST 2
11 CREATE_SIMPLE 32
12 SETID 1 "text"
13 BEGIN 16
14 STORE_V8_BINDING 43 0 0
15 FETCH 19
16 STORE_COMPILED_BINDING 17 1 1
17 POP
18 STORE_OBJECT_QLIST
19 CREATE_SIMPLE 32
20 SETID 2 "mouseArea"
21 BEGIN 16
22 STORE_SIGNAL 42 2
23 FETCH 19
24 STORE_COMPILED_BINDING 16 0 1
25 POP
26 STORE_OBJECT_QLIST
27 POP_QLIST
28 SET_DEFAULT
29 DONE
-------------------------------------------------------------------------------

CREATE_SIMPLE 是最重要的一个instruction,它创建一个c++对象(using a database of registered objects in QQmlMetaType.)

STORE_INTEGER 对应的是将一个整数值赋给一个property。
STORE_SIGNAL   对应 create a bound signal handler.
STORE_*_BINDING 对应 create a property binding. 更多关于binding的介绍在后续的博客中。
SETID obviously sets the identifier of an object, which is not an ordinary property.

对bytecode进行解释的VME解释器 维护一个存放 objects的栈,STORE_* 这种指令操作的是位于栈顶的object。 FETCH 指令将一个特定的QObject放在栈顶,POP指令用来移除栈顶的object。 所有的instructions都大量使用了integer indices,“例如, the STORE_COLOR instruction writes to property 41, which is the property index of the target QObject’s meta object.”

To sum up: Once a QML file is compiled, creating an instance of it is just a matter of executing the bytecode of the compiled data.

总结:

这篇文章讲了 加载qml文件过程中的parse, compile, creating阶段。下一文章将会介绍property binding的过程。


Ref:

https://www.jianshu.com/p/3e959cbaff3a

http://www.kdab.com/qml-engine-internals-part-1-qml-file-loading/

深度解析qml引擎---(1)Qml文件加载的更多相关文章

  1. spring源码深度解析— IOC 之 开启 bean 的加载

    概述 前面我们已经分析了spring对于xml配置文件的解析,将分析的信息组装成 BeanDefinition,并将其保存注册到相应的 BeanDefinitionRegistry 中.至此,Spri ...

  2. 【Spring源码深度解析学习系列】Bean的加载(六)

    Bean的加载所涉及到的大致步骤: 1)转换对应beanName 为什么需要转换beanName呢?因为传入的参数可能是别名,也可能是FactoryBean,所以需要一系列的解析,这些解析内容包括如下 ...

  3. 深入解析QML引擎, 第1部分:QML文件加载

    译者注:这个解析QML引擎的文章共4篇,分析非常透彻,在国内几乎没有找到类似的分析,为了便于国内的QT/QML爱好者和工作者也能更好的学习和理解QML引擎,故将这个系列的4篇文章翻译过来.翻译并不是完 ...

  4. QML从文件加载组件简单示例

    QML从文件加载组件简单示例 文件目录列表: Project1.pro QT += quick CONFIG += c++ CONFIG += declarative_debug CONFIG += ...

  5. html文件在head标签中引入js地址和直接写js代码,所用时间是不同的,因为引入js地址,文件加载的时候需要通过通讯协议去解析地址,读取外部文件

    html文件在head标签中引入js地址和直接写js代码,所用时间是不同的,因为引入js地址,文件加载的时候需要通过通讯协议去解析地址,读取外部文件

  6. js文件加载优化

    在js引擎部分,我们可以了解到,当渲染引擎解析到script标签时,会将控制权给JS引擎,如果script加载的是外部资源,则需要等待下载完后才能执行. 所以,在这里,我们可以对其进行很多优化工作. ...

  7. scrapy cookies:将cookies保存到文件以及从文件加载cookies

    我在使用scrapy模拟登录新浪微博时,想将登录成功后的cookies保存到本地,下次加载它实现直接登录,省去中间一系列的请求和POST等.关于如何从本次请求中获取并在下次请求中附带上cookies的 ...

  8. android源码解析(十七)-->Activity布局加载流程

    版权声明:本文为博主原创文章,未经博主允许不得转载. 好吧,终于要开始讲讲Activity的布局加载流程了,大家都知道在Android体系中Activity扮演了一个界面展示的角色,这也是它与andr ...

  9. Android 的 so 文件加载机制

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 最近碰到一些 so 文件问题,顺便将相关知识点梳理一下. 提问 本文的结论是跟着 System.loadlibrary() 一层层源 ...

  10. ELF文件加载与动态链接(一)

    关于ELF文件的详细介绍,推荐阅读: ELF文件格式分析 —— 滕启明.ELF文件由ELF头部.程序头部表.节区头部表以及节区4部分组成. 通过objdump工具和readelf工具,可以观察ELF文 ...

随机推荐

  1. 关于Java的i++和++i的区别

    之前对于 i++ 和 ++i 的理解就是: int i=1,a=0; 1.i++ 先运算在赋值,例如 a=i++,先运算a=i,后运算i=i+1,所以结果是a==1 2.++i 先赋值在运算,例如 a ...

  2. JS判断某变量是否为某数组中的一个值的3种方法

    1.正则表达式 js 中判断某个元素是否存在于某个 js 数组中,相当于 PHP 语言中的 in_array 函数. 1 Array.prototype.in_array = function (e) ...

  3. Bootstrap内栅格布局,表格,按钮,图片的个人总结

    栅格布局: container,固定宽度的容器. container-fluid,百分百宽度的容器. 使用行(row)在水平方向上创建一组列(colmun). 每一行中最多能够包含12列,超出的列则另 ...

  4. am335x system upgrade kernel tf(五)

    1      Scope of Document This document describes TF hardware design 2      Requiremen 2.1     Functi ...

  5. edgedb-js 来自官方的js 驱动

    目前对于edgedb 主要还是来自官方的python驱动,目前js 版本的已经快发布了,代码在github 可以看到了 同时官方文档也提供了一个关于edgedb 内部的协议说明,结合js 驱动以及文档 ...

  6. PostGraphile 4.4 发布,支持real time 查询

    在4.4 之前,real time 是通过插件完成处理的,4.4 直接内置了,还是很方便的功能,总算 和其他类似graphql 平台看齐了,使用上还是挺方便的. 参考资料 https://www.gr ...

  7. Python 下载超大文件

    使用python下载超大文件, 直接全部下载, 文件过大, 可能会造成内存不足, 这时候要使用requests 的 stream模式, 主要代码如下 iter_content:一块一块的遍历要下载的内 ...

  8. 洛谷 CF448D Multiplication Table

    目录 题目 思路 \(Code\) 题目 CF448D Multiplication Table 思路 二分答案.这个矩阵的每一排都是递增的,所以二分\(ans\),去计算有多少个数等于\(ans\) ...

  9. DACL原理.控制文件的访问权限(文件,注册表.目录.等任何带有安全属性的对象.)

    目录 一丶简介 1.DACL是什么. 2.如何创建一个自己控制的文件. 3.SDDL是个什么鬼. 二丶 编写SDDL 控制的文件 一丶简介 1.DACL是什么. DACL称为自主访问的控制列表.是应用 ...

  10. 从0开始部署GPU集群-0:基本情况

    配置信息(多台服务器) 1 硬件:CPU和GPU*可选 2 操作系统:centos7 3 驱动:nvidia显卡驱动  *可选 4 容器运行时:docker 和 nvidia container ru ...