QML 中 ID 的范围是什么?



文档说我们不能在一个文件中使用相同的ID。这意味着我们可以在不同的文件中使用相同的 id,对吧?我不知道 QML 中 ID 的范围,所以我编写如下代码来测试它。

//a.qml
Item {
id: a_item
x:20;
y:b_item.x // cannot access this id
y:b1.x1 // can access
Item {
id:a1 
x:20
Component.onCompleted : a1.x //this a1 is a.qml's a1 not the a1 in main.qml
}
}
//b.qml
Item {
id: b_item
x:20;
property int x1: 30;
}
//main.qml
Item {
a {
id:a1
Component.onCompleted : b1.x = 1 //can access
}
b {
id:b1
}
function() {
a_item.x = 1; // cannot access this id
}
}

我的问题

1.

QML 中 ID 的范围是什么?在我的测试中,结果显示 Item 无法访问其孩子和他兄弟的 chilren 的 id,但可以访问他的父母或兄弟,对吧?

阿拉伯数字。

不同文件中的相同 ID只是我在我的代码中显示的,没有错误,我工作了。但是我怎样才能区分它们。

规范的答案是:

id的作用域是组件作用域。

组件范围为:

QML 文档中的每个 QML 组件都定义一个逻辑作用域。每 文档至少有一个根组件,但也可以有其他根组件 内联子组件。组件作用域是对象的联合 组件中的 ID 和组件的根对象的属性。

这本身并不能过多地说明范围到底是什么以及如何充分利用它。信息量更大一点:

在 QML 中,组件实例将其组件作用域连接到 形成范围层次结构。组件实例可以直接访问 其祖先的组件范围。

基本上,qml 文件中的每个id都像该源根项的属性一样实现。除了它不能通过someobj.someId访问,只能通过someId

这意味着由于 qml 的动态作用域,从根对象延伸的分支中存在的任何对象都可以访问此 id。

只要它没有被同名的idproperty所遮蔽。

a_item将在a.qml以及存在于其根Item增长的分支中的任何对象中可见。

main.qml看不到它,因为该对象位于树的更下方,其中未定义a_item

在同样的思路中,可以从a.qml访问b1因为b1是在实例化a.qmlmain.qml中定义的。但b_item会 从那里看不到。

事实上,由于a1b1是在main.qml中定义的,是整个应用程序树的根,因此这两个id从应用程序的每个对象中都是可见的,只要它是对象树的一部分,并且只要标识符没有被阴影化。请注意,它们在单一实例或无父对象中不可见,因为它们不是应用程序对象树的一部分。

obj tree                a1  b1  a_item  b_item
main.qml                D   D   X       X
a.qm            V   V   D       X
Item a1     V   V   V       X
b.qml           V   V   X       D
D - defined here, V - visible here, X - not available

属性也是如此,尽管动态范围仅适用于为 qml 文件根元素定义的属性,这与id不同,即使它们位于不同的子分支上也是可见的,这就是为什么在此答案的第一句话中我将其作为"实现该源根项的属性":

Obj
Obj
Obj
id: objid
property objprop
CustomObj

所以objid将在CustomObj中可见,但objprop不会,因为它不是 id 并且未在根对象中定义。id与执行此操作相同:

Obj
property objid : _objid
Obj
Obj
id: _objid

来自给定源的所有id在 qml 源根对象的上下文中可见,随后在查找无法解析"更高"上下文中的标识符时,最终将下降到此上下文的其他所有内容。

最后,请记住微妙的陷阱 - 只有当您确定应用程序将在兼容的上下文树中实例化对象时,才能跨源使用ids。

例如:

A.qml {
id: objA
B { } // objA will be visible to this object
}
main.qml
A {
B {} // objA will NOT be visible to this object
}
B {} // objA will NOT be visible to this object

陷阱继续 - 上下文树在对象树之前- 创建对象的上下文很重要,一旦设置就无法更改(根据上下文依赖关系对重父级设置某些限制)。

// ObjA.qml
Item {
id: objA
Component {
id: cm
ObjB {}
}
function create() { cm.createObject(objA) }
}
// ObjB.qml
Item {
Component.onCompleted: console.log(objA)
}
// main.qml
Component {
id: cm
Rect {}
}
Obj {
anchors.fill: parent
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if (mouse.button === Qt.LeftButton) {
cm.createObject(parent)
} else {
parent.create()
}
}
}
}

正如这个实际示例所示,即使在这两种情况下,新创建的对象都属于具有objA标识符的同一对象的父级,但在main.qml中创建的对象无法解析它,因为它是在尚未定义objA的上下文中创建的,但如果对象是在上下文中创建的,它就可以工作objA, 即使它被埋在树上更高的地方,它也会起作用。

用更通用的方式来说,id在源的根对象的上下文中变得可见,并且在每个后续子上下文中保持可见,直到它被同名对象遮蔽。可见性无法向下到达树中定义id的上下文之前存在的上下文。请注意细微的区别 -a_item指的是Item,而a1指的是a。而且由于a1a.qml内部可见,因此它将始终引用main.qml中的a的一个实例,无论您可能处于a的哪个实例中,而a_item将为每个不同的a实例引用不同的对象。a_item是"相对的",在每个不同的a实例中都会有所不同,但a1是绝对的,并且将始终指a的特定实例。之所以如此,是因为a1是一个具体的实例,而a_item是一个类型/原型。

// Obj.qml
Item {
id: obj
Component.onCompleted: console.log(obj === oid)
}
// main.qml    
Obj { } // false
Obj { id: oid } // true 

动态id范围非常有用,可以减少实施解决方法以访问所需内容所需的时间。这也是为什么给id描述性名称而不仅仅是main是一个非常好的主意。

例如,如果您有一个管理多个viewsmanager,每个中都包含多个objects,您可以快速访问每个object的相应view,还可以访问管理器,而无需实现任何其他内容。经验法则是,manager必须排在第一位,然后每个view都应该在manager的上下文中创建,不一定直接在其中,而是在其中,并且每个object都应该在view的上下文中创建。当然,要注意不要遮蔽事物。如果你打破了这个规则,事情将无法正确解决。

View.qml { id: view }
manager
view1
object // view is view1
view2
object // view is view2
view3
object // view is view3

当然,这仅在特定目的的设计中才有意义,因为您知道上下文树的一般结构会是什么样子。如果你正在制作几乎可以去任何地方的通用元素,你绝对不应该依赖于跨源访问id,你应该通过属性、别名等来实现一个更通用的使用接口。

相关内容

  • 没有找到相关文章

最新更新