I'm implementing a system for syncing data from and to QML and QtScript using a QObject as the backing model and facade objects in QML, QtScript and C++, to simplify data synchronization between different subsystems.
The idea is that one could instantiate a facade object in QML and set a reference to the model QObject, and the facade gets properties added dynamically to allow binding to them in QML. So far it's working quite well, with one problem. The setter for the model QObject gets called too late, which makes bindings fail. If the bindings are done in Component.onCompleted instead it works as expected:
StateQMLFacade {
id: stateFacade
stateObj: myStateObj; // accessible as a context property
}
Text {
id: testText
text: "myInt: " + stateFacade.myInt // Prints "myInt: undefined"
Component.onCompleted: {
// With this line the binding works as expected
testText.text = Qt.binding(function() { return "myInt: " + stateFacade.myInt })
}
}
The facade item is a subclass of QQmlPropertyMap, and gets all properties of the model object added dynamically in the WRITE function for the property stateObj, and connects notify signals in both directions to sync the two. Which is why the properties do not exist when the bindings are attempted before stateObj is set.
The QML is created using QQmlComponent::create(QQmlContext* context)
When investigating further I found that setters for QObject* properties get called after all other bindings are done. For some reason properties that are integers etc. get set in QQmlComponentPrivate::doBeginCreate()
, while properties that are QObject*'s get set in QQmlComponentPrivate::completeCreate()
One way to solve it is to use a delayed Qt.binding like in the example above, or delay the instantiation of the components that bind to the facade object, but I want the mechanism to be seamless for the users. It's all working well except for this last timing/order issue.
I am wondering if there is any way to get more control over the order things are initialized in, or if the issue can be solved in some other way? Maybe it's possible to instantiate and initialize the facade early, and somehow inject it into the other QML?
This is using Qt version 5.15.
Any help and insight is appreciated.