-
Notifications
You must be signed in to change notification settings - Fork 0
3.04
Explanation of the contents of a topic page @ Topic reference page
Objective: Efficient delegate implementation
- What is a delegate? (3.06)
- What is lazy loading?
- What is an efficient delegate implementation?
- What is a dynamic delegate?
- How lazy loading could be used in Repeater?
Tino: The one above could refer to the earlier Repeater synchronous example.
Tino: I got feedback from the Qt Quick team that dynamic delegates with Loader should be always avoided. Loader is almost as bad as any other extra item in the delegate. It should be emphasised here that the best practise is to create simple delegates, where no dynamic loading is needed. To create the dynamic part, Qt.createComponent/createObject should be used that Loader object is not needed to be created. Loader is convenient though and can be mentioned as well, but with clear remark that it's not the best option.
When using Repeater
or dynamic views, delegates need to be created very quickly. If the delegate creation process is computationally expensive, it can affect the startup time of your application and overall performance of the user interface.
As previously mentioned, Repeater
creates elements synchronously. For simple items, this is very convenient and the generation happens instantaneously. Problems arise when Repeater
needs to load delegates containing Video
items all auto-playing video for some odd reason:
Item {
id: videoDelegate
Video {
id: video
width: 800
height: 600
source: model.videosrc
autoPlay: true
}
}
The target platform is low-end devices, and loading all these items at startup will take an noticeable amount of time. Some devices might not be able to play multiple video sources at the same time.
Insert emphasis: create simple delegates where no dynamic loading is needed
If there is a need to load resource intensive items inside a delegate, we can create objects dynamically.
Qt Quick has the Loader
type, which can be used to load different parts of the UI, like delegates, to improve performance.
Insert emphasis: not the best option available!
Here we have delegate which has a Component
where the video will be contained. We add a Loader
and a MouseArea
to the delegate, so when clicked the sourceComponent
property will be set to the videoComponent
. This change will trigger the Loader
to load the videoComponent
component.
Item {
id: lazyVideoDelegate
width: 200
height: 200
Component {
id: videoComponent
Video {
id: video
width: 800
height: 600
source: model.videosrc
autoPlay: true
}
}
Loader {
id: videoLoader
}
MouseArea {
anchors.fill: parent
onClicked: videoLoader.sourceComponent = videoComponent
}
}
Notice that while a Video
item declared by itself would be automatically rendered and displayed, this is not the case for the above as the item is defined inside a Component.
This will reduce the load time considerably, as the delegate will only contain lightweight Loader
and MouseArea
objects initially. This will also reduce memory consumption and runtime overhead of the application, which will reflect in more responsive UI. We can of course add an Image
which displays a thumbnail of the video or something similar.
UX: can show splash screen when loading main application or parts of application.
Example: we have a delegate that contains a camera. Every time the delegate is loaded, the loading operation will take 500ms and will block the UI. We could also have a Repeater
with heavy delegates, it will instantiate all of them and kill performance.
Solution: we will create a lightweight delegate that uses the Loader
type to load QML components asynchronously. When the user interacts with the delegate (hover, click, etc...) we trigger the loader.
K: Context problems! In the following example, the index context property inserted by the ListView into delegateComponent's context will be inaccessible to Text, as the Loader will use the creation context of myComponent as the parent context when instantiating it, and index does not refer to anything within that context chain.
Tino: Indeed. ListView will create delegates dynamically, so the use of Loader is obsolete here or actually increases memory consumption and should not be used. Just refer to the component id. The use of Loader us useful to dynamically load some heavy part of the delegate. E.g. the Text is light, but using a Camera would be heavy not to mention using a Repeater in the delegate to create 1,000,000 text objects.
Item {
width: 400
height: 400
Component {
id: myComponent
Text { text: modelIndex } //okay
}
ListView {
anchors.fill: parent
model: 5
delegate: Component {
Loader {
property int modelIndex: index
sourceComponent: myComponent
}
}
}
}
https://doc.qt.io/qt-5/qml-qtquick-loader.html#using-a-loader-within-a-view-delegate
https://doc.qt.io/qt-5/qtqml-javascript-dynamicobjectcreation.html
https://doc.qt.io/qt-5/qtquick-performance.html#lazy-initialization