Load your app from the network like Netflix using QML
QML does sounds like it’s inspired by the HTML judging by pronunciation but it’s a fairly different type of technology where QML is used to develop (native) applications using QML, which is a new language in itself, that can be paired with JavaScript and extended by C++, while HTML, in essence, was created to make web pages using HTML, JavaScript and CSS.
We have evaluated in previous articles how you can make web apps using QML and Qt for WebAssembly, however, that results in an HTML application that is run, presumably, on the web. QML also has WebGL plugin to stream application over the network, but that has different limitations.
So the question comes, could we actually have a native app built using Qt/QML that can load QML from the network so that your application could be (re)loaded on the fly without re-installation?
Well, we can.
QML Loader
QML has a Loader element which is used to dynamically create and load parts of your QML application . This is a common use-case in apps when you cannot have huge app with all elements created in advance. You would dynamically create parts as you need them.
In QML there are different ways to do that using different elements (i.e. StackView or SwipeView), but the basic one is a Loader and is commonly used with other components.
Loader can load a QML file (using the source property) or a Component object (using the sourceComponent property). It is useful for delaying the creation of a component until it is required: for example, when a component should be created on demand, or when a component should not be created unnecessarily for performance reasons.
There is nothing better than one small example to show how it works:
import QtQuick 2.0
Item {
width: 200; height: 200
Loader { id: pageLoader }
MouseArea {
anchors.fill: parent
onClicked: pageLoader.source = "Page1.qml"
}
}
This is directly from the Loader docs and what is does is loads all new content from Page1.qml into a current position where Loader is (don’t forget, you can always use QML scene to prototype QML code quickly).
(If you are interested more in a video presentation there is a nice one from KDAB on Youtube)
Now, that seems simple enough. But what is not really obvious (if you don’t read documentation ;)) is that source property is an URL. And while that can be used to reference resource files (qrc://) or local files (file://) it can also be used to load a network file (http(s)://) and that is what we are going to do today.
So in this small example it would be as simple as to replace source with a network location, which in this case could be:
onClicked: pageLoader.source = "http://localhost:8000/Page1.qml"
HTTP Server
As expected, this also requires a HTTP server to serve those files, which I leave as an exercise to the reader but NGINX is very good choice that we used in production in similar setup in one company I worked for.
For easy demo setup you can use any of the oneliner HTTP servers to get this example running.
In this case I have Python 3 available so I go to the directory where the QML file is located and run:
python -m http.server 8000
Our app
We will use inspiration from Netflix application above where we can reload application.
Lets have two files. One will be used to load our app, and the other is going to be our main app content.
AppLoader.qml
Window {
property string appUrl: "http://localhost:8000/App.qml" width: 400
height: 400
visible: true
title: qsTr("Hello QML from network") Loader {
id: loader
source: appUrl
anchors.fill: parent
} Button {
text: "Reload"
onClicked: {
loader.source = ""
loader.source = appUrl
}
}
}
App.qml
import QtQuick 2.0Rectangle {
color: "black"
Text {
anchors.centerIn: parent
color: "white"
text: "Coming from the network! " + Math.random()
}
}
So this results in:
And clicking a Reload button we will get a different number any time as expected. Now you are ready to add your QML app content and this can be reloaded on the fly without rebuilding your whole application and restarting it. This can be quite convenient on platforms where app upgrade and redeploy is not a fast process or just during the prototyping phase.
With one BusyIndicator you are ready to show loading state while actual application is loading (in asynchronous way) and with WebSocket you could signal that there was a change and that app should be reloaded to pick up a new version.
Note: if you use qmlscene you will need to create an empty qmldir or loading will fail, this is also what you would need if you would like to load a whole QML module
Where to next
First things first. You should read about QML Loader sizing just to make sure you understand how the sizing of loaded elements is done, and it is quite flexible in how you want to control it.
Second. We focused only on QML loader as that is probably a common element already used in your application and it enables us to reload parts of the interface, or as in this case, potentially even a whole app.
However, you can import QML modules from the network directly. In that case the setup is similar as it is locally (and you will need qmldir file as briefly mentioned above), however, you cannot easily reload your application. For that a restart of the whole application would be needed and with that you cannot really change the location of your QML files after deployment without upgrading your application.
With Loader, you can always dynamically set source location to wherever you HTTPS server is, if that would be needed.
Worth mentioning is that with this you load only QML part of your application and obviously, not any part of AppLoader.qml is loaded or the native C++ extensions you might be using. That is all still part of your native application. Think of it like a Web browser loading web application. You can modify your loaded application but if you need to make changes to your native part then it’s the same as it would be to upgrade your web browser.
Hopefully this analogy makes sense, if not, a bit of hard work with trial and error will get you there ;)
Until the next time, happy loading!