Eine kleine view framework with QtQuick – Part II

This time we will improve our little view framework by adding on demand loading of the views.

For your app to have a decent startup time you can’t load all of your QML at one go when your app starts up. You might get away with it if you have a very simple app, but once you start adding more and more views which might contain images (these too will get loaded right away) you’ll end up in trouble.

The solution to the problem is using the Loader element of QML. In case you haven’t read the docs let’s have quick recap. Loader is an element that is very (or at least quite) fast to parse and construct. The idea is that you can ask a Loader to construct a QML component for you not when the loader itself is constructed, but at the time you choose it to do so. The source of the loader can either be a QML file or aComponent element that has already been parsed. Since we want to speed up the startup it doesn’t make much sense for us to have our views parsed during the startup and postpone only the construction which is usually relatively fast (excluding loading of images).

Using Loader is simple. Set the source (url) or sourceComponent (a reference to a Component element’s id). After this you can access your loaded QML component via the item property of the Loader (not with the id of the component you defined or with the id of the root element inside the .qml file you loaded). Setting source to a local file or setting the sourceComponent are synchronous. If you refer to a QML file over a networked protocol the loading will naturally be asynchronous and you have to watch the loaded signal of the Loader. The item constructed by Loader can be destroyed by setting sourceto an empty string or setting sourceComponent to null.

My only wish for Loader (or some specialized flavor of it) would be that it could be completely transparent i.e. the loaded component would become the loader itself and it wouldn’t be necessary to reference the content via item property. Properties of the loaded component would become properties of the loader and functions respectively. This of course poses problems like clashes with the names of the properties, signals and functions, but I’d be willing to take those into account. Referring via item isn’t really that bad, but sometimes you build something you realize you should’ve probably loaderized in the first place and would like to change it with a snap of fingers. Also binding to the properties (without warnings) of the loaded component can be a bit cumbersome as we’ll see later. I’m not expecting to see a transparent loader happening so just keep in mind if you’re making something that is heavyish and not shown always or right away, loaderize it from the get-go.

For loaderizing (great word BTW) our KittyApp views, we will develop a small utility component called ViewLoader (version 1, we’ll refine it a bit later). ViewLoader allows us to loaderize the views with just a few lines per view. In addition loading views it will also support unloading of tje,. This can be useful if you have (heavy) views that you expect the user not to visit many times per session.

Let’s see what the ViewLoader has eaten:

The root element of the component is naturally a Loader (duh). The viewSource property is the URL to the QML file (the view) to be loaded. To keep things slim ViewLoader expects loads to be synchronous, so play nice and feed it with files or references to Qt resource files. The keepLoaded property defines whether the view should be destroyed once it’s deactivated. The anchor and opacity definitions are there for convenience reasons. Since our KittyApp views are dimensionally homogeneous (fancy words <3), we can define them here instead of where the ViewLoaders elements are declared. QDeclarativeEngine has an internal component cache, so ViewLoader.qml gets parsed only once and thus we have to parse less QML. Yay! activationComplete() and deactivationComplete() forward the calls to the loaded view (if it has them). The deactivation callback is also used for destroying the view if keepLoaded is false. loadView() is pretty self explanatory – it loads the view. It’s used by ViewSwitcher to load the view, but you can also call it yourself, if you know that some view will be needed and there’s a good spot (there’s already something going on for the user, but you’re waiting for input or there’s an asynchronous request going on) to do it.

To tie things together we’ll add couple of lines to ViewSwitcher from the previous post:

The only difference to the previous version is the two lines where the loadView()gets called. We are checking if loadView() exists, because this gives us the extra flexibility of mixing Item based views and ViewLoader views. Neat!

In the next post we’ll add some context sensitivity to the UI. I’ll post the whole source code and a Symbian SIS file, so you can experiment yourself. Until then, farewell you QDeclarativeItem deriving rapscallions!

This entry was posted in Programming and tagged , , , , , , , . Bookmark the permalink.

8 Responses to Eine kleine view framework with QtQuick – Part II

  1. Pingback: Tweets that mention Eine kleine view framework with QtQuick – Part II | Blogasdf -- Topsy.com

  2. Ville V says:

    I don’t really need this (not enough views to warrant it yet), but I’m going to use it just for kicks.

    BTW, is there a license for these code snippets, or is it just Public Domain?

    • elpuri says:

      Everything’s PD.

      @work I’ve reached the point where I have so many views that just parsing and constructing the loaders is starting to take a significant amount of time. I guess the next step is to start delaying the loading of the loaders :P

      I tried a trivial implementation of loaderizing the loaders, but I ran into some problems with anchoring (or I just messed up something else). Have to take another shot at it soon, because it’s just so stupid to parse the loaders of for example registration wizard on every startup.

      • Ville V says:

        Perhaps if you could switch to using Qt.createComponent instead of Loader…

        BTW, here’s a handy pattern that people probably need when switching to a Loader:

        ViewLoader {
        id: webpreview
        viewSource: “WebPreview.qml”

        function goBack() {
        viewSwitcher.switchView(commentview, true)
        mainview.state = “CommentsState”
        }

        onLoaded: {
        item.reqBack.connect(goBack)
        }
        }

        I.e. “reqBack” is a signal emitted by WebPreview. You can’t bind them directly in Loader for obvious reasons.

  3. Pingback: Eine kleine view framework with QtQuick – Finale | Blogasdf

  4. drtscha says:

    Hi, you’ve done excellent work. Loader is now clear. What’s unclear, though, is the following: if I’m having C++ backend that communicates with qml via signal/slot mechanism then I’m supposed to have references to qml objects (via their objectName property). In given case with delayed loading seems I’ll stuck, because I can’t reach unloaded components. Does this mean that I have to load all such components at startup and will ViewLoader hide these components from C++ (means, will it still be possible, having reference to ViewLoader, to find by objectName loaded component)?
    Have you opinion on this?

    • Juha Turunen says:

      Well if I understood your problem correctly there are several things you could do.

      You could emit signals from your c++ backend before accessing the loadable views. In QML side you catch those signals and call the loadView() function of the corresponding view to make sure it’s loaded.

      Or if you need to access some properties of and unloaded view, you could define those properties to be part of the ViewLoader instance and refer to those in the actual view QML.

      I’m not sure if I understand what you mean by “will ViewLoader hide these components from C++”.

  5. drtscha says:

    I’ll try to explain. Forgive typos and syntax errors, I’m focusing on concept here.

    QML component

    WillBeDynLoaded.qml
    Item{

    objectName: “myDialog”
    signal sigApplyClicked

    }

    ViewLoader will be used to load numerous items, which one – will be decided in run-time by changing source property.
    Now, C++ has in some init procedure

    QDeclarativeItem* myDialogRef(rootObject()->findChild(“myDialog”)); // will never give valid pointer, until myDialog is actually loaded
    connect(myDialogRef, SINGAL(sigApplyClicked()), this, SLOT(handleApplyClicked()));

    Now to circumvent this problem, I’ll quit on deciding ViewLoader source at run-time, I’ll just create as many ViewLoaders as I have sources and I’ll show them when time comes.

    ViewLoader{
    objectName: “myDialogLoader”
    source: “WillBeDynLoaded.qml”
    }

    Supposing that ViewLoader status is Ready before C++ does connect, I’m asking will ViewLoader allow above mentioned myDialogRef become valid pointer?

    I hope it is more clear now, although still not completely transparent.

    Cheers

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>