QML Component

QML Component

参考官方文档: http://doc.qt.io/qt-5/qml-qtqml-component.html

ComponentQtQML 模块定义的对象类型,它是 QML 对象或者对象树 (object tree) 创建的模板。当 一个 QML Document 被 QML 引擎导入后会创建一个对应的 Component 实例。一旦该实例导入成功,它就会用于实例化它所表征的对象或对象树。

QML 的一个重要思想就是 组件化 ,这种思想和 React 如出一辙。

组件化设计

  1. 如果你在发现某个 QML 文件很大,强烈建议将其中可复用的部分抽离出来,放置在一个新的 QML Document 中,这就是我们说的组件 (Component)
  2. 在顶层对象 (root item) 设置接口,包括 property, signal, method
  3. 使用 alias property 实现 子组件实例在顶层对象的属性代理 (property proxy),既可以隐藏子组件的实现细节,又可以节约内存,同时避免 property binding 带来的计算开销

1. Component

Qt 官方解释 Component 的作用如下:

Components are often defined by component files – that is, .qml files. The Component type essentially allows QML components to be defined inline, within a QML document, rather than as a separate QML file. This may be useful for reusing a small component within a QML file, or for defining a component that logically belongs with other QML components within a file.

上面这段话说,QML 引入 Component 的主要原因是避免为每一个 QML 对象类型 (尤其是小组件)单独新建一个文件,而是通过 Component 类型在更大粒度的组件中直接创建匿名的对象类型 (anonymous type),然后在需要时加载使用 (Loader)

下面是一个例子:

The component encapsulates the QML types within, as if they were defined in a separate QML file, and is not loaded until requested (in this case, by the two Loader object)

上面这段话意思是: Component 会将里面所有的 QML 结构封装 (encapsulate) 起来,就好像它们是定义在一个单独的 QML 文件中,它们也不会被加载直到被请求加载。

我们知道,QML Document 只有一个 single top-level item 定义了该 Component 的行为和属性,而 top-level item 外部是不能定义 property 或者 method 等属性的。和 QML 类似,Component 内部有且仅有一个顶层可视化 item, 在这个 item 外部不能定义任何数据,包括 properties, methods, signals 等属性,只能出现 id 属性。

2. Component Scope

The component scope is the union of the object within the component and the component’s root object’s properties.

QML 中,每一个 component 对象类型都有它自身的 component scope,即它的single top-level item 内部的区域所有对象的 id 属性和根对象 (root object) 的 property 属性的并集 (union),它们必须在 component scope 中必须具有唯一性

这样规定的好处是: 在这个 component scope 中,任何一个 object 都可以通过 id 找到她所需要的对象的某些属性,比如 property, signal 等,或者直接使用 root item 的 property,而不需要设置 root item 的 id

比如下面这个例子:

第一个 Text 实例直接使用 root item 的 title property

第二个 Text 实例找到 id 为 titletype 的 对象,并绑定它的 text property

下面这里例子很清楚地展示了 Component Scope 的范围:

请注意,component scope 不包括该 component 本身的 id , 这也就出现了上面代码中一个神奇的现象: Component 的 id 和 它的顶层 item 的 id 不会相互冲突

因为它们是分属于两个 component scope 中的。component 的 id 属于 该 QML Document 的 component scope, 而 Rectangle 的 id 则属于匿名对象类型的 component scope

3. Component Instance Hierarchy

Qt 在 声明一个 Component 实例的时候的上下文称为该 Component 的 creation context

QML 引擎中,component 实例会根据 object tree 连接它们的 component scope 从而形成 scope hierarchy。

可能有点难以理解。前面说过 Component scope 让我们实现了在一个 Component signal top-level item 内部通过 id 找到其他对象,

而 component instance hierarchy 实现了 每个 component 实例的 component scope 的连接,也就是跨 QML Document 的 property 引用

来看一个具体的例子:

TitleText.qml 文档中, 顶层对象 Texttext 属性依赖某个未知的属性 title。如果我们直接使用 qmlscene 工具运行该 QML Document, 它会提示:title is not defined

但是如果初始化某个 TitleText 实例,在该实例的 creation context 发现了 title 变量,那么就不会报错了。

TitlePage.qml 中,我们在 顶层对象 Item 定义了 property title, 那么下面的 TitleText 实例在实际运行时就会通过 component scope hierarchy 找到这个 title property,并使用它。

QML 是动态作用域 (dynamic scoping) 语言,也就是说在不同的地方,title property 解析的结果也会不同。

我们对此必须非常小心,强烈不推荐两个组件的强耦合。通常情况下,都推荐使用 property interface ,实现解耦。基于事件驱动的组件内部状态改变,只依赖顶层对象 (single root )的 property 改变,而不是依赖它的 creation context 中某个潜在的变量。同时,通过顶层对象的 signal 传递组件的变化信息,在实例化组件中通过 on<Signal> 实现信号对应槽函数的实现。

下面是一个优化后的例子

4. attached signal handler – onCompleted()

Component 还有一个很重要的信号(attached signal) – completed()

这个信号会在该对象实例化成功后被发射出去。QML 中通常会在该信号的槽函数中定义一些组件初始化步骤,比如: 信号与槽的连接,日志打印,property 修改等

Note: The order of running the onCompleted handlers is undefined.

5. 动态创建 Component

请勤用 Loader 或者 Component.createObject() ,实现动态创建一些不常用的界面,可以大大加快程序启动时间

desrtroy() 可以销毁不用的组件

One thought on “QML Component

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d 博主赞过: