Q 理论使用总结
一、概述
QML属性绑定、内联函数和导入的JavaScript文件都在JavaScript作用域中运行。作用域控制一个表达式可以访问哪些变量,以及当两个或多个变量名称冲突时,哪个变量优先访问。
由于JavaScript的内置作用域机制非常简单,QML对其进行了增强,以更自然地适应QML语言扩展。
二、JavaScript范围
QML的作用域扩展不会干扰JavaScript的自然作用域。JavaScript程序员在使用QML编写函数、属性绑定或导入的JavaScript文件时,可以重用他们现有的知识。
在下面的例子中,addConstant()方法会在传入的参数上加上13,就像程序员预期的那样,而不管QML对象的a和b属性是什么值。
QtObject {
property int a: 3
property int b: 9
function addConstant(b) {
var a = 13;
return b + a;
}
}
QML遵守JavaScript的正常作用域规则,甚至适用于绑定。这种完全离谱的绑定将12赋值给QML对象的a属性。
QtObject {
property int a
a: {
var a = 12; a; }
}
QML中的每个JavaScript表达式、函数或文件都有自己独特的变量对象。在一个地方声明的局部变量永远不会与在另一个地方声明的局部变量冲突。
三、类型名称和导入的JavaScript文件
QML文档包括定义类型名称和文档可见的JavaScript文件的import语句。除了在QML声明中使用之外,JavaScript代码在访问附加属性和枚举值时也会使用类型名。
导入的效果适用于QML文档中的每个属性绑定和JavaScript函数,即使是嵌套的内联组件。下面的例子展示了一个简单的QML文件,它访问一些枚举值并调用导入的JavaScript函数。
import QtQuick 2.0
import "code.js" as Code
ListView {
snapMode: ListView.SnapToItem
delegate: Component {
Text {
elide: Text.ElideMiddle
text: "A really, really long string that will require eliding."
color: Code.defaultColor()
}
}
}
四、绑定作用域对象
具有属性绑定的对象称为绑定的范围对象。在下面的例子中,Item对象是绑定的作用域对象。
Item {
anchors.left: parent.left
}
绑定可以不受限制地访问范围对象的属性。在前面的例子中,绑定直接访问项的parent属性,而不需要任何形式的对象前缀。QML为JavaScript引入了一种更结构化、更面向对象的方法,因此不需要使用JavaScript的this属性。
从绑定中访问附加的属性时必须小心,因为它们与作用域对象交互。概念上的附加属性存在于所有对象上,即使它们只对其中的一个子集有影响。因此,未限定的附加属性读取总是解析为作用域对象上的附加属性,这并不总是程序员想要的。
例如,PathView类型会根据委托在路径中的位置来添加插入的值属性。由于PathView只有意义地将这些属性附加到委托中的根对象上,因此任何访问它们的子对象都必须显式限定根对象,如下所示。
PathView {
delegate: Component {
Rectangle {
id: root
Image {
scale: root.PathView.scale
}
}
}
}
如果Image对象省略了根前缀,它会无意中访问未设置的PathView。在自身上附加属性。
五、Component 组件范围
QML文档中的每个QML组件定义一个逻辑范围。每个文档至少有一个根组件,但也可以有其他内联子组件。组件的作用域是组件内部对象id和组件根对象属性的联合。
Item {
property string title
Text {
id: titletype
text: "<b>" + title + "</b>"
font.pixelSize: 22
anchors.top: parent.top
}
Text {
text: titletype.text
font.pixelSize: 18
anchors.bottom: parent.bottom
}
}
上面的示例显示了一个简单的QML组件,该组件在顶部显示富文本标题字符串,在底部显示相同文本的较小副本。第一种文本类型在形成要显示的文本时直接访问组件的title属性。根类型的属性是可以直接访问的,这使得在整个组件中分发数据变得很容易。
第二种文本类型使用id直接访问第一种文本。id由QML程序员显式指定,因此它们总是优先于其他属性名(JavaScript作用域中的属性名除外)。例如,在前面的例子中,即使绑定的作用域对象有titletype属性,titletype id仍然优先。
六、组件实例层次结构
在QML中,组件实例将它们的组件作用域连接在一起形成作用域层次结构。组件实例可以直接访问它们祖先的组件作用域。
演示这一点的最简单方法是使用内联子组件,其组件作用域隐式地作为外部组件的子组件。
Item {
property color defaultColor: "blue"
ListView {
delegate: Component {
Rectangle {
color: defaultColor
}
}
}
}
组件实例层次结构允许委托组件的实例访问Item类型的defaultColor属性。当然,如果委托组件有一个名为defaultColor的属性,它就会优先。
组件实例作用域层次结构也扩展到越界组件。在下面的例子中,标题页。qml组件创建两个TitleText实例。尽管TitleText类型位于一个单独的文件中,但在TitlePage中使用title属性时,仍然可以访问到它。QML是一种动态作用域的语言——根据在哪里使用,title属性的解析方式可能不同。
// TitlePage.qml
import QtQuick 2.0
Item {
property string title
TitleText {
size: 22
anchors.top: parent.top
}
TitleText {
size: 18
anchors.bottom: parent.bottom
}
}
// TitleText.qml
import QtQuick 2.0
Text {
property int size
text: "<b>" + title + "</b>"
font.pixelSize: size
}
动态作用域非常强大,但必须谨慎使用,以防止QML代码的行为变得难以预测。
一般来说,它应该只在两个组件已经以另一种方式紧密耦合的情况下使用。在构建可重用组件时,最好使用属性接口,如下所示:
// TitlePage.qml
import QtQuick 2.0
Item {
id: root
property string title
TitleText {
title: root.title
size: 22
anchors.top: parent.top
}
TitleText {
title: root.title
size: 18
anchors.bottom: parent.bottom
}
}
// TitleText.qml
import QtQuick 2.0
Text {
property string title
property int size
text: "<b>" + title + "</b>"
font.pixelSize: size
}
七、覆盖的属性
QML允许在对象声明中定义的属性名被另一个对象声明中声明的属性覆盖,该对象声明扩展了第一个对象声明。例如:
// Displayable.qml
import QtQuick 2.0
Item {
property string title
property string detail
Text {
text: "<b>" + title + "</b><br>" + detail
}
function getTitle() {
return title }
function setTitle(newTitle) {
title = newTitle }
}
// Person.qml
import QtQuick 2.0
Displayable {
property string title
property string firstName
property string lastName
function fullName() {
return title + " " + firstName + " " + lastName }
}
在这里,title既被赋予了输出文本的标题以供显示,也被赋予了Person对象的敬语标题。
根据引用被覆盖属性的范围解析该属性。在Person组件的作用域中,或者在引用Person组件实例的外部作用域中,title解析为Person.qml中声明的属性。fullName函数会引用Person内部声明的title属性。
然而,在Displayable组件中,title指向Displayable.qml中声明的属性。getTitle()和setTitle()函数以及对text对象的text属性的绑定都将引用Displayable组件中声明的title属性。
尽管名称相同,但这两个属性是完全独立的。其中一个属性的onChanged信号处理程序不会因同名的另一个属性发生更改而触发。其中一个属性的别名只能引用其中一个属性,不能同时引用两个属性。
八、JavaScript全局对象
QML不允许类型、id和属性名与全局对象上的属性冲突,以防止混淆。Math.min(10, 9) 将始终按预期工作。