Efficient/correct sync/async QML dynamic object construction/destruction
-
PROBLEM: Application reads different configuration files, where GUI
elements are defined and based on that creates dynamic QML objects (checkboxes,
input fields, drop downs etc.) - like a big form with many data fields, which can
be submites/saved to server. And this form must be dynamically changed when
different configuration file is loaded.
Just wanted to know what would be the best way (memory and UX) wise to do that
cross platform - Windows, Andoid, iOS etc.As I have discovered myself, many QML Items/Components will use many RAM, which
is limitted for mobile devices - so the obvios solution is to create dynamic
form only when needed and immediatelly destroy elements when changed to different
form (not to keep in memory unused objects - saves RAM, but uses CPU).To test this, I created simple button, where onClicked function re-creates Column
of checkboxes. (in reality of course there could be many different types of input
elements/controls). Code is really simple.ApplicationWindow {
width: 640
height: 480toolBar: Row { Button { text: "DYNAMIC" onClicked: { for (var i = 0; i < container.children.length; i++) { container.children[i].destroy(); } container.children = []; for (var i = 0; i < 100; i++) { checkBox.createObject(container, {text: i}); } } } BusyIndicator {} } Component { id: checkBox CheckBox {} } Column { id: container }
}
Button simply destroys previous children and adds new children. BusyIndicator is good
to show GUI freeze on longer functions.- Is this way of destroying Items children correct?
- Do I really need to set container.children = [] list explicitly?
- Why there isn't a more efficient way of destroying all children at the same time?
Is it because destruction is deferred to later, so there is no difference, baucause
destroy() only sets some destruction flag and real job is done later. - Are the objects created and instantiated exactly at .createObject() function call and
are only rendered when onClicked() exits?
Anyway, creating many object dynamically can be seen by small GUI freeze even on my
64-bit Intel i7 16GB RAM computer and has serious lag on mobile devices - Android phone,
which is quite new. What would you suggest to imporve this?
5) Would creating these 100 checkbox from c++ side have a noticable performance benefit?
Or would it spend 99% of time in QML engine anyway, so there is no real benefit?
6) I guess to overcome this, I have 2 choises:
*) Create needed objects one by one, so that GUI doesn't freeze ( but in the long
run this is probably slower, because each element will be rendered before next
is created?)
*) Create objects Asynchronously.
What would you suggest?If Async mode is the preferred one, then:
7) Can it be done efficient enough from QML side and there is no need to use C++?
8) How to control Async objects in a controlled way? Because Async object creation must be
stopped if it takes long time and user has already chosen to do something else -
or to start initiating other dynamic view.
9) Are Async children going to be created in the order/sequence it was done in code? If no, then are they
at least going to be ordered in the correct way under parent.children after all child
objects are instantiated?
10) Would you suggest using Component.incubateObject for this task? There is also Loader, but
I guess it wouldn't really work, because there is no static .qml file which can be loaded
asynchronously. All the children objets are dynamic and using a Loader for each such child
would use even more resources (I guess).This could be a good post/discussion for reference for other users.
-
Hi chocis,
- Yes
- Yes, otherwise container will contain a bunch of undefined objects.
- The objects are destroyed immediately when destroy() is called and their memory is also freed immediately (the javascript garbage collector is not involved in this case).
- The objects are instantiated immediately when createObject() is called, but they are rendered the first time after returning from the onClicked javascript code block.
- Don't know. You need to measure.
- If your targeted hardware supports multiple threads: Create the objects outside the GUI thread. Either use incubateObject() in QML or do it in C++ with QThread. In addition: Create invisible objects when the CPU / GUI thread is idle and set them visible as soon as you need them.
- Don't know. You need to measure.
- Send signals from QML to other (C++) threads to terminate them.
- Don't know.
- Like you already stated, if the component you want to instantiate isn't in a separate qml file then Loader can't be used. Try incubateObject().
Cheers!
Wieland