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

                                                                     


对于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. 给jenkins更换工作空间

    如果使用jenkins的默认工作空间,它默认安放在 /var/lib/jenkins 目录下,但这个在分配Linux磁盘的时候,一般为40G,时间长或者项目多的话,很容易将磁盘空间占满,所以我们需要将 ...

  2. js 对嵌套页面的父页面进行跳转

    window.top.location.href = '/stores';

  3. circus web console 依赖tornado>3.2 无法访问的bug

    circus web console 是一个很不错的web 监控circus 工具,但是对于高版本一直存在一个bug 信息如下 Traceback (most recent call last): F ...

  4. 【JZOJ6222】【20190617】可爱

    题目 给定一个长度为\(n\)的串,定义两个串匹配当且仅当两个串长度相同并且不同字符至多一个 对于每一个长度为\(m\)的子串输出和它匹配的子串个数 $1 \le n \le 10^5  ,  m \ ...

  5. 「2019-8-13提高模拟赛」树 (tree)

    传送门 Description 你有一个 \(n\)个点的树,第 \(i\)个点的父亲是\(p_i\).每个点有一个权值 \(t_i\) 和一个颜色黑或者白.所有点一开始都是白色. 你要进行 \(m\ ...

  6. PHP 之循环创建文件夹

    /** * 循环创建文件夹 * @param string $dir 需要创建的文件夹路径 * @param integer $mode 文件夹权限 * @return bool 返回创建是否成功 * ...

  7. 使用python3完成人脸识别

    原文地址:https://www.jb51.net/article/160197.htm 第一种: # -*- coding:utf-8 -*- import cv2 as cv import num ...

  8. Python plot_surface(Axes3D)方法:绘制3D图形

    3D 图形需要的数据与等高线图基本相同:X.Y 数据决定坐标点,Z 轴数据决定 X.Y 坐标点对应的高度.与等高线图使用等高线来代表高度不同,3D 图形将会以更直观的形式来表示高度. 为了绘制 3D ...

  9. jmeter常用四种断言

    jmeter常用四种断言 一.Response Assertion(响应断言)二.Size Assertion(数据包字节大小断言)三.Duration Assertion(持续时间断言)四.bean ...

  10. 公网IP地址就一定是A类地址和B类地址吗?那C类地址就一定是私有地址吗?

    A,B,C三类中既有公网地址,也有私网地址:在A类地址中,10.0.0.0-10.255.255.255是私有地址.在B类地址中,172.16.0.0-172.31.255.255是私有地址.在C类地 ...