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.