Skip to content
Kimmo edited this page May 22, 2018 · 11 revisions

Dynamic Delegate - Loader (P0 | 2h)

Explanation of the contents of a topic page @ Topic reference page

Back to Week 3

Objective: Efficient delegate implementation

Beginner

  • What is a delegate? (3.06)
  • What is lazy loading?

Intermediate

  • 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.

Expert

Omitted


Course material content

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.

Dynamic object creation

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
            }
        }
    }
}

Instructions and description for the exercise of the topic


Exhaustive reference material mentioned in this topic

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

https://qmlbook.github.io/ch13/index.html

Further reading topics/links:

Clone this wiki locally