I have a Loader object that loads some very heavy components. Some event arrives in the middle of the load that requires loading to stop and go back to empty the Loader. Is it possible?
- 1,021
- 1
- 19
- 36
-
Should be, with an `asyncronous` Loader. But this property fails me somehow. While the `UI` does not freeze as promised (`BusyIndicator` running) it is still completely unresponsive. – derM - not here for BOT dreams Apr 12 '17 at 14:21
-
I have `asynchronous` set, but the problem is it destroys objects in Loader while they are not finished loading, which causes warnings and errors. – Rinat Veliakhmedov Apr 12 '17 at 14:44
-
What about setting `active` to false or setting `source`/`sourceComponent` to null? – folibis Apr 12 '17 at 14:54
-
I don't know what went wrong earlier, but uppon writing it once again, it works... – derM - not here for BOT dreams Apr 12 '17 at 15:20
-
@folibis then it seems to never delete any objects and eventually runs out of memory. – Rinat Veliakhmedov Apr 13 '17 at 13:49
2 Answers
Abort object creation
As documented by Qt, three methods exists to unload/abort an object instantiation:
- Set
Loader.activetofalse - Set
Loader.sourceto an empty string - Set
Loader.sourceComponenttoundefined
Asynchronous behaviour
To be able to change these properties during loading, Loader.asynchronous should be true, otherwise the GUI thread is busy with loading the object. You also need to QQmlIncubationController for your QQmlEngine to control the idle time used for object incubation. Without such a controller Loader.asynchronous does not have any effect. Note that QQmlApplicationEngine automatically installs a default controller if the scene contains a QQuickWindow.
Bugs
Up to the last tested Qt version (Qt 5.8.0, 5.9.0 beta), a severe memory leaks exist when aborting an unfinished object incubation (at least in certain cases, including the example in the answer of derM) resulting in a fast memory usage increase for large components. A bug report is created including a proposed solution.
According to the bug report, this should be fixed in Qt version 5.15 (not tested).
- 10,244
- 7
- 28
- 56
I don't know what your issu is, with those objects that are destroyed before the loader finishs, but maybe the issue is there? If not, this should work: If it does not help, please add some code to your question, that reproduces your problem.
main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: root
visible: true
width: 400; height: 450
Button {
text: (complexLoader.active ? 'Loading' : 'Unloading')
onClicked: complexLoader.active = !complexLoader.active
}
Loader {
id: complexLoader
y: 50
width: 400
height: 400
source: 'ComplexComponent.qml'
asynchronous: true
active: false
// visible: status === 1
}
BusyIndicator {
anchors.fill: complexLoader
running: complexLoader.status === 2
visible: running
}
}
ComplexComponent.qml
import QtQuick 2.0
Rectangle {
id: root
width: 400
height: 400
Grid {
id: grid
anchors.fill: parent
rows: 50
columns: 50
Repeater {
model: parent.rows * parent.columns
delegate: Rectangle {
width: root.width / grid.columns
height: root.height / grid.rows
color: Qt.rgba(Math.random(index),
Math.random(index),
Math.random(index),
Math.random(index))
}
}
}
}
- 235,170
- 19
- 170
- 241
- 13,081
- 4
- 49
- 89
-
If you keep clicking button very fast, it never frees memory allocated for previous objects and eventually causes bad_alloc. – Rinat Veliakhmedov Apr 13 '17 at 13:47
-
How often did you click with my `ComplexComponent`? :D I opted for a timer *clicking* every 200 or 500 ms, and did that for multiple minutes. But yes, the memory increases over time... – derM - not here for BOT dreams Apr 13 '17 at 14:33
-
I set the timer to 200, but increased the number of rows and columns to 200. It grows memory very fast and crashes. – Rinat Veliakhmedov Apr 13 '17 at 14:38
-
For 200x200, I do not see any memory increase (constant ~200MB). For smaller size (50x50), I do so see an increase, but this can be avoided by calling `gc` (i.e. garbage collection) manually after unloading. Except for the first time, `button.clicked()` takes a lot of time (up to 6s for 200x200), the GUI freezes, and the file is not loaded asynchronously (`Loader.Status` never becomes `Loader.Loading`). Tested on Qt 5.8, Ubuntu 16.04. – m7913d Apr 14 '17 at 16:03
-
@m7913d : Interesting that this happens to you. At the beginning I had exactly the same problem (well, actually the GUI-thread it self seemd to work, so the BusyIndicato if I remember it right, kept spinning, but the `Button` was indicated as `down` the whole time, and it was unloaded only after the loading was completed. Then I wanted to post this as an example that *does not work* when it suddenly worked. Tried it on several machines (Linux and Windows) by now, and it works for me :D – derM - not here for BOT dreams Apr 14 '17 at 18:02
-
The above results were for using `QQuickWidget`. I switched to `QQmlApplicationEngine` (which creates a `QQmlIncubationController` automatically if the scene contains a `QQuickWindow`) and now `asynchronous` and aborting works, but memory leaks seem to occur. `gc` doesn't work anymore to clean up memory. – m7913d Apr 14 '17 at 18:55
-
@m7913d I'm on Windows, calling `gc` does basically nothing (I use `QQmlApplicationEngine`). – Rinat Veliakhmedov Apr 14 '17 at 23:40
-
This definitely seems to be a Qt bug. Can I use your example to fill in a bug report? – m7913d Apr 15 '17 at 08:17
-
I submitted a [bugreport](https://bugreports.qt.io/browse/QTBUG-60188) including a possible solution. – m7913d Apr 15 '17 at 10:52
-
@m7913d I'll write a support request when I get to my work laptop, maybe it'll get resolved faster. – Rinat Veliakhmedov Apr 15 '17 at 12:27
-
1Check whether calling `gc()` 3-4 times in a row has an effect on memory usage. One rarely suffices, but 3-4 seem to squeeze as much garbage out as possible. – dtech Apr 17 '17 at 20:01
-