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: 480

    toolBar: 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.

    1. Is this way of destroying Items children correct?
    2. Do I really need to set container.children = [] list explicitly?
    3. 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.
    4. 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?

    1. 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?
    2. 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:

    1. Can it be done efficient enough from QML side and there is no need to use C++?
    2. 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.
    3. 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?
    4. 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.


  • Moderators

    Hi chocis,

    1. Yes
    2. Yes, otherwise container will contain a bunch of undefined objects.
    3. 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).
    4. The objects are instantiated immediately when createObject() is called, but they are rendered the first time after returning from the onClicked javascript code block.
    5. Don't know. You need to measure.
    6. 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.
    7. Don't know. You need to measure.
    8. Send signals from QML to other (C++) threads to terminate them.
    9. Don't know.
    10. 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


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.