QQuickView.rootObject() issues, dynamic signal/slot management design
-
Hi all,
first what i'm trying to do: [maybe there is another (easier) 'design pattern' to this, since i can't imagine this problem didn't arise to anyone before]
i have one single QQuickView Window which should support switching between different "root" QML Files, e.g. you got "ProjectManagement" and "Wizard", both are loaded via setSource() und both define signals unique to them (which are emitted somewhere inside their respective qml tree)
The MainWindow (class inheriting QQuickView) has a pointer to a QObject, which offers the slots for the currently displayed qml. After loading a new root qml, the qml itself calls a slot in the mainWindow (exported via a property in the rootContext) and requests a certain slotProvider. The mainWindow itself is 'agnostic' regarding to the qml it is displaying, it just knows about all possible slotProviders which are stored in a map with string keys. The mainWindow (or SlotManager as i called it in the code) disconnects the previous slotProvider and asks the new one to connect his stuff to the signals. So the sequence in short:
0 - SlotManager creates N QObjects offering slots (SlotProvider), stored in QMap<QString, QObject*>
1 - qml is loaded and does e.g. Component.onCompleted: { SlotManager.setSlotProvider('mainMenu') }
2 - SlotManager sets currentSlotProvider to the correct pointer from his map and calls a method inside
the provider which connects his slots to the signals, the latter are defined in the root element of the qml
(3 - qml could request a new root element, which in turn request a new slotProvider and disonnects the old one)So far the design part, maybe this could be done even better/nicer, basically i just need dynamic slots which are requested from the qml side. And thoses slots need to be C++ (at least partial) to offer some low-level stuff qml can't provide.
Now the problem: in order to connect the slots i need a valid QQuickView.rootObject(), because thats the Object which emits the signals, but it seems i can't track the moment this gets non-null from qml. This is what i've tried so far:
1- using Component.onCompleted (inside qml), calls a slot in the exported C++ Object which just prints the pointer, which is null at this moment (as far as i have googled on this, onCompleted gets called after parsing is done, so this won't guarantee a valid pointer?)
2 - using onStatusChanged() (in qml), still null
3 - using onStatusChanged in cpp, (with Status = Ready) not null
4 - checking the pointer just after the setSource call in cpp, not nullNow i could of course do it from cpp side by enforcing a common interface function in qml, so the cpp side actively requests the required slot provider, as in:
setSource("SomeThing.qml");
invokeMethod( rootObject(), "getProvider" );
but i'd like to avoid having such an interface, because the qml side should be the one which starts the whole process.
Or maybe Qt already offers something for this, i just haven't found it yet?Any hints are appreciated! :)
-
If you are ready for another level of indirection, I guess you could make a single, global QML file containing a simple Loader element that would load your current qml code on demand. This way your rootObject would be valid at any time. However, with the engine caching it's items you could run into high RAM usage.
-
bq. If you are ready for another level of indirection, I guess you could make a single, global QML file containing a simple Loader element that would load your current qml code on demand. This way your rootObject would be valid at any time. However, with the engine caching it’s items you could run into high RAM usage.
Well this should work obviously, but then the comfort of having the signal declarations at root level would be lost right? That is, i would have to do a findChild(dren) on the rootObject (which is the Loader, not the actual loaded component) or use the item property of the Loader. But i guess this (using Loader.item) would show the same "null/non-null" behaviour as if i would have loaded it as the root element in the first place? Will check that out...
-
Yeah, I realise that even if it would work it's not a very good solution.
-
As far as i could test this, it somewhat does what it should:
onLoaded slot in a Loader seems to guarantee a non-null Loader.item property (else onLoaded shouldn't be signaled in the first place, right?)
The item can be passed to the SlotManager which does the connect/disconnect magic.
And for the caching stuff, even if everything would be active at the same time, we would be far from lets say 1 GB RAM. Its just approx 8 "screens" with some controls and logic in the cpp background, its not Doom 5 :) -
Hehehe ok, I'm happy it works for you, even if only "somewhat".
-
After sleeping a night over it, it think it's even easier and safer to just export all classes which provide slots to the rootContext and allow to call them directly. You would circumvent the whole signal/slot mechanism (which is of course a not very qt'ish design approach), but i'm safe that the pointers will be around. Is there some documentation about the lifespan of the rootContext? Does it stay the same for the whole lifespan of a QQuickView and its engine?
-
No idea. You can safely ping QtQuick developers on IRC or interest mailing list, though, they should know. You can aim at Alan Alpert, Jerome Pasion or Topi Reinio.