Speed up loading of QML
-
Hi All,
I have an embedded device and a QML application running on it. I have noticed very long startup times of the application (30-60sec). This is problably caused by the parsing of my QML pages. Currently I have one main QML file were all the other windows are created on:
@
Item {
id: mainWindowOtherWindow{}
SomeOtherWindow{}
MoreWindows{}
@Right now on startup all (25) windows are created and are shown or hidden using the visible flag. This causes a high load when starting the application and it takes a long time when the user interface is visible and usable. When all windows are commented out in QML code the startup time is reduced to 8 seconds.
The way our application is set-up QML is only used for user interface, all functionality is done in C++. Also the key handling is done by c++ and by emitting signals to QML the user interface is triggered.
What I would like to do is to use QML Loader to load a window when it's needed. If you have any other suggestions let me know! Currently I have the following problems:
When I load a QML page my main window is gone / unloaded:
@
Item {
id: mainWindowLoader { source: "OtherWindow.qml"}
//OtherWindow{}
//SomeOtherWindow{}
//MoreWindows{}
@And one problem I have is that I cannot set the properties of the object that's loaded:
@
Item {
id: mainWindow// Before: Window { visible: myCPPObject.visible; text: myCPPObject.text }
Loader { source: "Window.qml"; visible: myCPPObject.visible; myProperty: myCPPObject.myProperty }
//Error: Cannot assign to non-existent property
@ -
I'm also looking at the PageStack QML component which seems to do what I want: dynamicly load QML files and stack windows.
Currently i'm using Qt Quick 1.0 (Qt 4.7.3) which doesn't seem to have PageStack(window) or Page QML component... Is this correct that it's available from Qt 4.7.4 or is it only available for Meego/Symbian?
http://doc.qt.nokia.com/qt-components-symbian/qml-pagestack.html#details
_ -
You state that you're using QtQuick 1.0 - are you able to upgrade to QtQuick 2.0? It provides a lot better support for dynamically loading and instantiating components.
For example, Loader has gained a setSource() overload (only usable from js, not declaratively) which allows setting initial property values of the loaded component - thereby solving your second problem.
You can solve it in QtQuick 1.x by accessing the "item" property of the loader once loading as completed (you'll need to set an onStateChanged: { if (state == Loaded) item.myProperty = myCPPObject.myProperty; } or similar handler).
Your first point was confusing - when you say the main window is unloaded, what do you mean?
Finally, if you look at the desktop components for QtQuick 2.0 (see http://labs.qt.nokia.com/2011/03/10/qml-components-for-desktop/) I'm pretty sure a component similar to pagestack will be provided.
Cheers,
Chris. -
bq. You state that you’re using QtQuick 1.0 – are you able to upgrade to QtQuick 2.0? It provides a lot better support for dynamically loading and instantiating components.
I prefer to stick with 1.1 to minimize project riscs. I've written quite a lot of c++/QML... Also Qt 5 is still alpha/beta and will be final end of june.
bq. Your first point was confusing – when you say the main window is unloaded, what do you mean?
When I load another QML file my main window is gone. I thought the newly loaded qml file will be drawn on top of the main window..
bq. Finally, if you look at the desktop components for QtQuick 2.0 (see http://labs.qt.nokia.com/2011/03/10/qml-components-for-desktop/) I’m pretty sure a component similar to pagestack will be provided.
bq. bq. I think the pagestack component will be a decent solution instead of fooling around with the loader component and setting all the properties using the item property.
Do you have another suggestion except using a beta version of QtQuick 2.0?
-
There are a few ways you can work around these sort of issues in QtQuick 1.x, but they tend to be somewhat ugly.
-
create a .pragma library script to store references (via js var's) to things you need - this will prevent them from being garbage collected (and it's possible that that is what is happening to your mainwindow, although that seems strange).
-
use loaders or other dynamic creation functionality to construct your other visual items as-needed (eg, Qt.createComponent and comp.createObject; you may need to setParentItem on the created object, which may require exposing a function to QML which takes a parent item arg and a child item arg, and sets the parent properly).
-
use properties to control construction time of "most content" of an item, and then use normal hierarchy or even property hierarchy to construct "minimal content" items at startup time.
eg:
@
// main.qml
Item {
property MyMinimalItem c1: MyMinimalItem {
someProp: someValue
someOtherProp: someOtherValue
}property MyMinimalItem c2: MyMinimalItem { someProp: someValue2 someOtherProp: someOtherValue2 } // etc. // use this sort of function to trigger loading of more content function loadMoreContent(whichItem) { whichItem.triggerProperty = true; } Component.onCompleted: { // on startup, we only want to actually load c1's content, not c2. loadMoreContent(c1); }
}
// MyMinimalItem.qml
Item {
property bool triggerProperty: false // false by default
onTriggerPropertyChanged: {
if (triggerProperty == true) {
// dynamically load content, eg with loader etc
} else {
// unload content
}
}
}
@See "Qt4.7 Dynamic Object Management In QML":http://doc.qt.nokia.com/4.7-snapshot/qdeclarativedynamicobjects.html for more information.
/edit: typo in the code, fixed.
-
-
Thanks a lot for your suggestions chriadam! I have used your 2nd approach: loaders.
Before (loading the window when the application is started):
@
ListWindow {
id: ListWindow;
visible: ListWindowModel.visible;
titleText: ListWindowTitleModel.text;
dialogTitle: ListDialogTitleModel.text;
inputText: ListFilterModel.text;
inputIndex: ListFilterModel.cursorPosition;
listModel: ListModel;
listIndex: ListModel.currentIndex;
leftPlaceHolderModel: ListLeftSoftKeyModel;
leftPlaceHolderPage: ListLeftSoftKeyModel.page;
rightPlaceHolderModel: ListRightSoftKeyModel;
rightPlaceHolderPage: ListRightSoftKeyModel.page
}@Now (load the window when it's needed, when mycppobj.visible is set to true):
@
Loader {
id: ListWindowLoader
source: ListWindowModel.visible ? "windows/ListWindow.qml" : ""
onLoaded: {
item.visible= true;
item.parent= mainWindow;
item.titleText= ListWindowTitleModel.text;
item.dialogTitle= ListDialogTitleModel.text;
item.inputText= ListFilterModel.text;
item.inputIndex= ListFilterModel.cursorPosition;
item.listModel= ListModel;
item.listIndex= ListModel.currentIndex;
item.leftPlaceHolderModel= ListLeftSoftKeyModel;
item.leftPlaceHolderPage= ListLeftSoftKeyModel.page;
item.rightPlaceHolderModel= ListRightSoftKeyModel;
item.rightPlaceHolderPage= ListRightSoftKeyModel.page
}
}
@At first I didn't see the loaded window because the parent was not set (item.parent= mainWindow).
Although starting up the application is a lot faster now I notice loading the QML files are not that fast on my target. I was looking for a way to show a animation while the Loader is loading the QML file... While googling I noticed there are only "hacky" ways to do this...
[Edit: Code formatting. Broke lines and indented to help readability; mlong]
-
Depends what you mean by hacky. You can show an animation and then stop it in the onLoaded signal handler. In QtQuick 2.0 all of this stuff is even better, ie with better asynchronous support and threaded compilation. Also, the performance of QtQuick 2.0 is much better, in general (eg, faster parsing, faster compilation, faster instantiation, etc).
Anyway, I'm glad that you found a solution which improves your startup time.
Cheers,
Chris. -
In QtQuick 2.0, if you set asynchronous to true, loading/compilation will occur on a separate thread, and cooperative instantiation (on the main thread) will be used to instantiate it. Thus, while you won't be guaranteed that there won't be glitches in the animation (since the OS scheduling during loading/compilation might lead to skipped animation updates on a single-core system, and during instantiation the incubator only yields on element boundaries which may also lead to skipped frames) for most use-cases (especially if you're careful with your component design) you should see velvet animations.
But you're right - in QtQuick 1.0, everything is done in the main thread, so loading a QML file will block the animation updates.