Prevent GUI From Freezing on massive runtime objects creation
-
Hello everybody, for a couple of days I've been struggling with a problem which I can't get my head around and I hope someone could help me find a workaround for this.
Basically, the application I'm working on has a function to load properties for qml objects from a JSON file and then creating
them dynamically.I will post a simplification here just to expose the structure of the problem:
the loadMission function reads and parses the JSON file and then call addwaypoint function of another qml object (which handles the map) that instantiate them
function loadMission(fileName) { planning.missionLoading = true; var jsObject; jsObject = fileName var error = false; // Try parse the json file try { jsObject = JSON.parse(jsObject); } catch (e) { error = true } var i; .... //read properties from js object and perform a tremendous amount of error checks .... var wp = jsObject.waypoints; //then I have to create the objects based on what I read from the JSON file for (i in wp) { // Add the Waypoint mapmodule.addWayPoint( /*omitting arguments*/) } ... return error; }
here's a snipper for the basic behavior of the addWaypoint function
function addWayPoint( /*omitting arguments*/) { var marker; marker = Qt.createQmlObject('MapWayPoint { /*properties value provided by func args */}', map); // Add the object to the map map.addMapItemGroup(marker); }
So the problem is that when I'm loading a file that has more than 150/200 waypoints in it the GUI freezes down due to the amount of work needed to dynamically create all the objects
Even with a simplified version of the function with no checks and no rendering just creating the objects in a large sequence, the problem persists so my conclusion is that the bottleneck of the computation is in the sequential calling of
Qt.createQmlObject('MapWayPoint { /*properties value provided by func args */}', map)
Since I can't move this C++ side and I want absolutely to avoid calling ProcessEvents() given that the application runs on many, I want to find a workaround for keeping the GUI responsive or being able to visually update a progressBar which currently gets not visually updated because, well, clearly the whole GUI is frozen by the loadMission function computation.
Sorry if something is unclear but it's my first post on this forum and I'm relatively inexperienced with Qt and qml yet cause I started working recently on this project.
Thanks in advance for any help.
-
Have you seen / used incubators instead of direct creation?
https://doc.qt.io/qt-5/qml-qtqml-component.html#incubateObject-methodThere's some tricks to javascript scope as you do but basically put it into a container and retrieve it when it again when it changes or something. This way each component loads when it's ready.
Actually, you're using a map ... are you aware of MapItemView? Why not use a model and a delegate? Much easier to manage, no scripting... even can use proxy / filter models,
-
You could create the component once (make a generic object as a qml file). Then create individual objects with their properties via an js object for each object. That might be less cpu intensive than creating the object from QML script for each object.
Edit: Also, make sure your file reading (assuming your objects are coming from file) is not contributing to this delay. It could be possible to push that to C++ and process the json object in C++ to reduce load. But find out for sure where the biggest time sink is.
Edit2: You can also look into batching the loading so the gui has some time to run the event loop while this is going on. You can do this by dividing up your objects and running Qt.callLater to process the next batch. You can even put in a parameter to control where it starts the next batch if desired.
import QtQuick 2.12 import QtQuick.Window 2.12 Window { visible: true width: 640 height: 480 title: qsTr("Object Creation Benchmarking") Component.onCompleted: { Qt.callLater(batch, 0) } function batch(number){ console.log("batch:", number) if(number < 5){ Qt.callLater(batch, ++number) } } }
-
the biggest time sink is the creation of the objects given that the qml objects I'm creating are rich in data and structures.
I've adopted your approach and moved the JSON parsing to C++ then signaled the properties to qml in order to create the object but the GUI freezing still remains for the reason mentioned above.
Also, the creating of the component once and then calling the createObject function seems like a nice suggestion and less CPU intensive indeed, I just need to figure how to create the component statically: if to pass it to the function that actually creates objects as a parameter or use a Loader or something.
I've found a useful and maybe decisive approach in using qml object incubator to create my objects asynchronously but I need still to implement it and verify it works for my use case as expected.
Thank you for the nice suggestions, if u have others while I work towards a solution I will treasure them.
-
Have you seen / used incubators instead of direct creation?
https://doc.qt.io/qt-5/qml-qtqml-component.html#incubateObject-methodThere's some tricks to javascript scope as you do but basically put it into a container and retrieve it when it again when it changes or something. This way each component loads when it's ready.
Actually, you're using a map ... are you aware of MapItemView? Why not use a model and a delegate? Much easier to manage, no scripting... even can use proxy / filter models,