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

                                                                     


对于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/

最新文章

  1. ModernUI教程:主题资源引用
  2. Dynamics AX 2012 R2 创建一个专用的批处理服务器
  3. easyui+Spring MVC+hibernate = 乐途
  4. C语言范例学习02
  5. spark mllib 之线性回归
  6. VHDL----基础知识1
  7. bluebird-Core API(一)
  8. zoj 1004 dfs
  9. jquery简单封装
  10. 笔记:Maven 项目目录结构
  11. vue初尝试--项目结构
  12. MAC下的mysql忘记密码该怎么办??
  13. 使用BBED跳过归档进行恢复
  14. [转]MingGW64 多个版本区别(silj, seh)
  15. OSGi HelloWorld
  16. Unity全面的面试题(包含答案)
  17. Linux中涉及到计算优先级及其他问题
  18. Android Studio 编译错误
  19. 通过JVM 参数 实现spring 应用的二进制代码与配置分离。
  20. 关于plsqldev无法正常加载oracle instantclient中的oci.dll的其中一个原因

热门文章

  1. MYECLIPSE说明书
  2. DATAGRID显示序号
  3. maven的目录
  4. go选项模式
  5. tensorflow2.0 学习(二)
  6. c++ main函数
  7. 《OKR工作法》——打造一支专一的团队
  8. 洛谷P4343 [SHOI2015]自动刷题机
  9. Python题库系列分享一(17道)
  10. 第07组 Alpha冲刺(5/6)