1

对于一个组件(一个.qml文件),根对象的属性可以被外部访问,但是子对象的属性对外是不可见的,即

[父对象id].[子对象id].[子对象属性]

的写法是不允许的,如

//MyItem.qml
import QtQuick 2.12

Item{
	id:rootItem  //根对象id
	property string name:"hello"
	Item{
		id:childItem   //子对象id
		property string name:"world"
	}
}

//main.qml
import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    id:root
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
	
	MyItem{
		id:myItem
		Component.onCompleted:{
			myItem.name = "haha";   //正确
			myItem.childItem.name = "heyhey";  //错误
		}
	}
}

上述代码运行报错:

TypeError: Value is undefined and could not be converted to an object

如果外部想访问(读、写)一个组件内的子对象属性,应该使用属性别名,如

//MyItem.qml
import QtQuick 2.12

Item{
	id:rootItem  //根对象id
	property string name:"hello"
	property alias childName:childItem.name   //属性别名的写法
	Item{
		id:childItem   //子对象id
		property string name:"world"
	}
}

//main.qml
import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    id:root
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
	
	MyItem{
		id:myItem
		Component.onCompleted:{
			myItem.name = "haha";   
			myItem.childName = "heyhey";  //修改myItem内的childItem.name
		}
	}
}

或者整个item作为属性别名导出

//MyItem.qml
import QtQuick 2.12

Item{
	id:rootItem  //根对象id
	property string name:"hello"
	property alias item:childItem   //属性别名的写法
	Item{
		id:childItem   //子对象id
		property string name:"world"
	}
}

//main.qml
import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    id:root
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
	
	MyItem{
		id:myItem
		Component.onCompleted:{
			myItem.name = "haha";   
			myItem.item.name = "heyhey";  //直接访问myItem.childItem
		}
	}
}

属性别名不会分配内存,其相当于目标属性的引用
其实,只要是作为属性的对象(不管是属性别名,还是一般的属性),就可以直接访问,即

[父对象id].[子对象属性名].[子对象属性]


2

以下考虑跟上述相反的情况,一个组件则可以访问外部组件的属性,因为组件在其声明(创建了对象实例)的时候,就属于所在的作用域,因此可以访问这个作用域内的对象及属性,如

// 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
}

上述的TitleText内用到了title属性,我们知道Text是没有这个属性的,它会自动到上一层去寻找这个属性。
这一点就很危险了,如果我们粗心使用了未定义的属性,程序可能不会报错,因为这个属性可能存在于上一层对象的作用域中,当然qt官方也不建议这种用法:Scope and Naming Resolution


3

同一个qml内,任意对象属性均可以相互访问,通过[对象id].[属性]的形式访问,如果不写对象id,那么将会首先在当前对象内寻找这个属性,所以要注意漏写对象id导致修改了错误的属性,如

//MyItem.qml
import QtQuick 2.12

Item{
    id:rootItem  //根对象id
    property string name:"hello"
    Item{
        id:branch0   //对象分支0
        anchors.fill: parent
        property string name:"world"
        Rectangle{
            id:child0   //子对象的子对象
            property string name:"!!!"
            anchors.fill: parent
            MouseArea{
                id:mousearea
                anchors.fill: parent
                onClicked: {
                    name = branch1.name   //这里原想修改child0.name,但最终修改的是rootItem.name,因为onClicked是mousearea的事件,而mousearea中没有定义name,就到MyItem.qml的组件作用域中寻找了
                    console.log("[2]")
                    console.log("rootItem.name="+rootItem.name)
                    console.log("branch0.name="+branch0.name)
                    console.log("child0.name="+child0.name)
                    console.log("branch1.name="+branch1.name)
                    console.log("child1.name="+child1.name)
                }
            }

            Component.onCompleted: {
                name=child1.name   //这里修改的就是child0.name,因为Component.onCompleted是child0的附加属性事件,属于child0的作用域
                console.log("[1]")
                console.log("rootItem.name="+rootItem.name)
                console.log("branch0.name="+branch0.name)
                console.log("child0.name="+child0.name)
                console.log("branch1.name="+branch1.name)
                console.log("child1.name="+child1.name)
            }
        }
    }
    Item{
        id:branch1   //对象分支1
        anchors.left: branch0.right
        property string name:"dog"
        Rectangle{
            id:child1   //子对象的子对象
            property string name:"???"
        }
    }

    Component.onCompleted: {
        console.log("[0]")
        console.log("rootItem.name="+rootItem.name)
        console.log("branch0.name="+branch0.name)
        console.log("child0.name="+child0.name)
        console.log("branch1.name="+branch1.name)
        console.log("child1.name="+child1.name)
    }
}

//main.qml
import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    MyItem{
        id:item
        anchors.fill: parent
    }
}

鼠标点击后,打印:

qml: [0]
qml: rootItem.name=hello
qml: branch0.name=world
qml: child0.name=!!!
qml: branch1.name=dog
qml: child1.name=???
qml: [1]
qml: branch0.name=world
qml: child0.name=???
qml: branch1.name=dog
qml: child1.name=???
qml: [2]
qml: rootItem.name=dog
qml: branch0.name=world
qml: child0.name=???
qml: branch1.name=dog
qml: child1.name=???

刚才说了,直接写[属性],会首先在当前对象作用域内寻找属性,那如果当前对象没有这个属性呢?排除第2节提到的情况,这里要分三种情况讨论:
(1)在当前对象中存在属性时,此时直接写[属性]是没问题的。
(2)如果当前对象作用域内没有属性,而根对象作用域定义了属性,此时直接写[属性]的隐藏的id将会是根对象id,说明属性的访问规则是优先当前作用域,再者是根对象作用域

//MyItem.qml
import QtQuick 2.12

Item{
	//[0] rootItem作用域
	
    id:rootItem  //根对象id
    property string name:"hello"
    property int age:10
    function printName()
    {
        childItem1.printName()
        childItem2.printName()
    }
    function printAge()
    {
        childItem3.printAge();
    }
    Item{
    	//[1] childItem1作用域
    	
        id:childItem1   //子对象id
        property string name: name+" world"   //注意,这里的name就是childItem1.name!此处会导致循环绑定,QML Item: Binding loop detected for property "name"
        function printName()
        {
            console.log(name);
        }

		//[1] childItem1作用域
    }
    Item{
    	//[2] childItem2作用域
    	
        id:childItem2   //子对象id
        property int age:20
        property string name1: name+" world"   //这里的name就是rootItem.name
        function printName()
        {
            console.log(name1);
        }
        Item{
        	//[3] childItem3作用域
        	
            id:childItem3
            function printAge()
            {
                console.log(age)   //打印结果:“10”,这里的age为rootItem.age,并不是childItem2.age
            }
            
            //[3] childItem3作用域
        }
        
		//[2] childItem2作用域
    }
    
	//[0] rootItem作用域
}

//main.qml
import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    MyItem {
        id:item
    }

    Component.onCompleted: {
        item.printName()
        item.printAge()
    }
}

(3)如果根对象作用域、当前对象作用域内均没有属性,此时直接写[属性]则会报错not defined,如

import QtQuick 2.12

Item{
	//[0] rootItem作用域
	
    id:rootItem  //根对象id
    property string name:"hello"
    //property int age:10  //此处注释掉
    function printName()
    {
        childItem1.printName()
        childItem2.printName()
    }
    function printAge()
    {
        childItem3.printAge();
    }
    Item{
    	//[1] childItem1作用域
        id:childItem1   //子对象id
        property string name: rootItem.name+" world"  
        function printName()
        {
            console.log(name);
        }
        //[1] childItem1作用域
    }
    Item{
    	//[2] childItem2作用域
        id:childItem2   //子对象id
        property int age:20
        property string name1: name+" world"   //这里的name就是rootItem.name
        function printName()
        {
            console.log(name1);
        }
        Item{
        	//[3] childItem3作用域
            id:childItem3
            function printAge()
            {
                console.log(age)   //错误,ReferenceError: age is not defined,并不会到childItem2的作用域里寻找age
            }
            //[3] childItem3作用域
        }
        //[2] childItem2作用域
    }
    
	//[0] rootItem作用域
}

可见,不注意对象作用域将埋下许多地雷,最好是养成习惯,不管在哪里,属性前面都加上对象的id