Visual Parent and Object Parent

每個 QML Item 都可以設定 parent property 。而 Item.parentQObject::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 時,大部分的情境下兩個函式會對應到相同的物件。但在某些特殊情況,兩者可能會不同,例如:

  1. 你在 QML 端設定 Item.parent
  2. 你的 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.

results matching ""

    No results matching ""