Is the order of loading of Qt Quick children specified?

1.8k views Asked by At

E.g. is it guaranteed that the order of initialization of children matches the order in which they appear in the source code?

Note: by "initialization of a child" I mean "initialization of the child and all its children, descendants, bindings, etc".

1

There are 1 answers

5
dtech On

A simple test can be used to verify the order of object creation.

class Test : public QQuickItem {
    Q_OBJECT
  public:
    Test(QQuickItem * p = 0) : QQuickItem(p) { qDebug() << this; }
};

Then:

  Test {
    objectName: "a"
    Component.onCompleted: console.log(objectName, this)
    Test {
      objectName: "b"
      Component.onCompleted: console.log(objectName, this)
      Test {
        objectName: "c"
        Component.onCompleted: console.log(objectName, this)
      }
      Test {
        objectName: "d"
        Component.onCompleted: console.log(objectName, this)
      }
    }
    Test {
      objectName: "e"
      Component.onCompleted: console.log(objectName, this)
    }
  }

Which gives the output of:

Test(0x6a7378, parent=0x0, geometry=0,0 0x0)
Test(0x6a73d8, parent=0x0, geometry=0,0 0x0)
Test(0x6a7438, parent=0x0, geometry=0,0 0x0)
Test(0x6a7498, parent=0x0, geometry=0,0 0x0)
Test(0x6a74f8, parent=0x0, geometry=0,0 0x0)
qml: a Test(0x6a7378, "a")
qml: e Test(0x6a74f8, "e")
qml: b Test(0x6a73d8, "b")
qml: d Test(0x6a7498, "d")
qml: c Test(0x6a7438, "c")

Which indicates that object constructors are indeed called bottom to top.

Also note that the order of onCompleted is different, depending on where that handler is installed. If you wrap Test in an Obj.qml like this:

Test {
  id: rectangle
  Component.onCompleted: console.log(objectName, this)
}

And declare the structure like this:

  Obj {
    objectName: "a"
    Obj {
      objectName: "b"
      Obj {
        objectName: "c"
      }
      Obj {
        objectName: "d"
      }
    }
    Obj {
      objectName: "e"
    }
  }

Then you get a consistent "back to front" output which you didn't get in the first scenario:

Test(0x4b2458, parent=0x0, geometry=0,0 0x0)
Test(0x4b24b8, parent=0x0, geometry=0,0 0x0)
Test(0x4b2518, parent=0x0, geometry=0,0 0x0)
Test(0x4b2578, parent=0x0, geometry=0,0 0x0)
Test(0x50f9d68, parent=0x0, geometry=0,0 0x0)
qml: e Test(0x50f9d68, "e")
qml: d Test(0x4b2578, "d")
qml: c Test(0x4b2518, "c")
qml: b Test(0x4b24b8, "b")
qml: a Test(0x4b2458, "a")

However, all this reflects the order of object creation, not object completion, which involves a bunch of other stuff, which can be executed in an arbitrary order, depending on the binding expression structure.

In short, you shouldn't really be depending on that order, if you do, you are doing it wrong. You should not depend on anything any finer than the entire QML source tree completion, the qtquick engine itself will take care to delay initialization of binding expressions and such until the entire object tree is completed, so you won't have a problem with that, it happens automatically, but relying on anything that is lower level and finer grained is a potential flaw in the design and to be avoided. Give your objects ids, and execute one single initialization expression for the entire qml file that hooks up stuff together if you want more explicit order of initialization. The statements in that expression will be executed in the order they are defined.