Visual Parent and Object Parent
每個 QML Item
都可以設定 parent
property 。而 Item.parent
跟 QObject::parent
是兩個獨立、無直接關連的資訊。 QML Item
對應到 C++ 的 QQuickItem
,它的函式清單裡面有兩個 parent:
parent() const : QObject *
parentItem() const : QQuickItem *
仔細看看文件內,對 parent
property 的描述:
Property
parent
:QQuickItem *
Access functions:
QQuickItem * parentItem() const
換句話說, QML parent
對應到的是 QQuickItem::parentItem
,而不是 QObject::parent
。在開發 QML 時,大部分的情境下兩個函式會對應到相同的物件。但在某些特殊情況,兩者可能會不同,例如:
- 你在 QML 端設定
Item.parent
- 你的 Item 在 QML
Repeater
底下
實驗
實驗一,一般情境
先做一個可以印出兩種 parent 的 QML Item
:
class ItemEx: public QQuickItem {
Q_OBJECT
public:
ItemEx(QQuickItem * pParent = 0) : QQuickItem(pParent){;}
~ItemEx(){;}
Q_INVOKABLE void printParents() const
{
qDebug() << "QObject::parent = " << parent()
<< ", QQuickItem::parentItem = " << parentItem();
}
};
接下來,在 QML 端做個小小的實驗:
Rectangle {
id: background
width: 100; height: 100
objectName: "background"
ItemEx {
id: itemEx
width: 100; height: 100
}
MouseArea {
anchors.fill: parent
onClicked: { itemEx.printParents(); }
}
}
滑鼠點擊後,你會看到
QObject::parent = QQuickRectangle(0x351bf40, name = "background") ,
QQuickItem::parentItem = QQuickRectangle (this = 0x351bf40 , name= "background" , ... )
對於 itemEx
來說,兩種 parent 對應到相同的物件。
實驗二,在 QML 內改變 parent
property
接下來,試著在 QML 改變 itemEx.parent
:
Rectangle {
id: background
width: 100; height: 100
objectName: "background"
Rectangle {
id: innerRectangle
width: 50; height: 50
objectName: "innerRectangle"
ItemEx {
id: itemEx
width: 50; height: 50
parent: background
}
}
MouseArea {
anchors.fill: parent
onClicked: { itemEx.printParents(); }
}
}
你會發現 itemEx
的兩個 parent 不一樣了,
QObject::parent = QQuickRectangle(0x373cfc0, name = "innerRectangle") ,
QQuickItem::parentItem = QQuickRectangle (this = 0x373cf80 , name= "background" , ... )
從這裡可以看出,QObject::parent
是 QML 程式碼結構中,上層的物件;而 QQuickItem::parentItem
會是 QML 程式碼指定的 parent: background
。無論你在 QML 設定了什麼 parent
property,都無法改變 QObject::parent
。
實驗三,QML Repeater
試著用 Repeater
做幾個 ItemEx
出來:
Rectangle {
id: background
width: 100; height: 100
objectName: "background"
Column {
anchors.fill: parent
objectName: "column"
Repeater {
id: repeater
objectName: "repeater"
model: 5
delegate: ItemEx {
id: itemEx
width: 100; height: 20
}
}
}
MouseArea {
anchors.fill: parent
onClicked: { repeater.itemAt(0).printParents(); }
}
}
此時 printParents
可能會給你意想不到的結果,
QObject::parent = QObject(0x0) ,
QQuickItem::parentItem = QQuickColumn (this = 0x449b540 , name= "column" , ... )
後者很合理,ItemEx
的 parent 是 column
。但前者,QObject::parent
對應到的值是 NULL。
討論
對於多數的 QML 開發,一個物件的 QObject::parent
是誰並不是很重要的問題。但將 QML 跟 C++ 同時使用時,可能會遇到一些意想不到的問題。舉例來說,C++ 端若要拿到某個 QML Item,它可能會用 QObject::findChildren
,但這個方法絕對拿不到 QML Repeater
底下的物件。
有關 Item.parent
的討論,可以參考 Concepts - Visual Parent in Qt Quick 內的說明。裡面也有提到
The concept of the visual parent in Qt Quick is separate from, but related to, the concept of the object parent within the QObject parent hierarchy.