Qt QML Tips

These tips are for Qt QML development as opposed to c++/QWidget development. See Qt UI Basics for a brief explanation of the difference. These tips give a pragmatic developer view how to go about some basic tasks with Qt QML.

Last Updated: 4 February 2011

The tips are a combination of experience, answers to questions I have posted on the qt.nokia.com forums and help obtained directly from the kind people at Nokia.

Contents

Qt Overview
Qt UI Basics
Qt SDK
UI Components
Screens/Pages
Partitioning Code
Localizing an App
Resolution Independence
File Access
JSON data
Protecting QML Files
Creating Symbian SIS Files
Invoking the External Web Browser
Making a Phone Call
Creating a Splash Screen
Dealing with Arrays

Qt Overview

To get a quick overview of Qt take a look at the presentations from the Nokia Qt Dev Days 2010 FTP site
UPDATE:
Videos are also now available.

Qt UI Basics

Previously when creating Qt UIs, you had to either use Qt QWidgets or build your screens on top of QGraphicsView. QWidgets are the lowest common denominator in Qt in that they work on all Qt platforms (Windows, Linux, Mac, Symbian, MeeGo). However, the user experience is poor on touchscreen phones. You don’t get the phone’s underlying look and feel and hence this method of development isn’t really acceptable for today’s applications.

QGraphicsView is newer and allows use of things such as transitions and animations. You still have to build your own controls (buttons, lists, edit boxes etc) on top of QGraphicsView and for this reason it can take a lot of effort to create an app. Orange Wednesdays is an app that was produced this way.

The very latest (and recommended) option being promoted by Nokia is to use Qt Quick and QML. This is built on top of QGraphicsView and allows for declarative (textual) based descriptions of screens. QML is Nokia’s new declarative language to build Qt UIs and apps. It’s what Nokia is recommending developers (and its internal developers) use to develop new apps so that they will be compatible with current/future Symbian OS-based and future MeeGo-based devices.

Forum Nokia vs qt.nokia.com

Both Forum Nokia and qt.nokia.com have Qt SDKs. The one on Forum Nokia is targeted at developing for Symbian (and MeeGo) while the one on qt.nokia.com is mainly targeted at developing for other platforms (e.g. the desktop). There are additional components you can download and install to make the qt.nokia.com SDK more suitable for mobile but they tend to be beta/alpha and need lots of work to get going. There’s more about this on my blog.

As of January 20th, with the introduction of the Qt SDK 1.1 Tech Preview, Nokia is starting to unify the SDKs so there is less confusion.

A UI for Symbian and/or MeeGo

As of writing this there is no Qt UI for Symbian and MeeGo. For this reason, there’s no framework yet for creating screens and widgets/controls. Instead you have to deal with rectangles, mouse events, images etc. This means you have you build up your own UI. Eventually, Nokia will be providing Qt Components (pre-alpha version available that might or might not lead to released components).

Until then, take a look at the Twitter demo in the SDK to work out how to create buttons etc. Also take a look at Qt Quick Colibri that provides generic cross-platform controls. Colibri has been created by Digia who do a lot of internal consultancy/development for Nokia. Hence, the Colibri is also a great source of examples how to do common things.

Even after Nokia releases QT Components for Nokia and Meego, it’s expected that libraries such as Qt Quick Colibri will continue to be used for projects that require a custom ‘Branded’ look and feel.

Creating Screens/Pages

Most of the SDK samples just have one screen and there’s no framework or recommended way of creating separate screens. There are several ways of achieving this. The most common are either to create all the screens in your main QML file and make them visible non-visible as you switch screens or to use the Qt Loader to dynamically load screens from QML files as you need them. The latter is preferred because it doesn’t rely on having (possibly) memory hungry screens there in the background that aren’t being used. Keep the Loader in your main (first) QML file and signal back to it whenever you need to switch screens. 

Something like this in your main QML…

Loader {
    id: loader
    source: "InitialPage.qml"
}

Connections {
    target: loader.item
    onShowScreen: loader.source = msg;
}

Where each screen has a… 

signal showScreen(string msg)

at the top and a 

thisPage.showScreen("AnotherPage.qml")

.. where you want to change screen. Notice the showScreen vs onShowScreen upper/lower case convention. i.e. add upper case O and lower case n and capitalise the following letter to create a handler for a signal.

Partitioning Code

You might like to partition your code to, for example, separate out your UI framework so you can use it on other projects. There’s no concept of DLLs or linking. The best way I have found of doing this is to just have a subdirectory containing the files. At the top of the file that uses the files put…

import "MyDirectory"

Where MyDirectory is the directory name. You can then use the names of files in the directory to directly use these components.

e.g. for Button.qml in UIDirectory…

import "UIDirectory"
Button {
}

Localizing an App

First of all, the documentation is a bit misleading/simplistic as it’s a one file project and the naming conflicts with what I found at Trolltech.

Here’s what I do…

Aggregate all your file string instances (that are in all QML files) in one file per language and name like qml_en.ts (for English). There’s no need to run lupdate (it just finds qsTr for you which you probably already know as you added the string – you might like to run it once though to get the .ts file syntax).

Manually edit qml_en.ts (drag and drop into Qt creator). You then only have to run lrelease i18n/*.ts every time you change the file (good if QT Creator could do this automatically!). Change the QT Creator project settings (Project icon) to use the translation you wish to test e.g. add -translation qml_en.qm as QML viewer arguments.

Also, I found that qsTr() doesn’t work in a ListModel. You will get a run time error “cannot use script for property value parameter”. Use QT_TR_NOOP() rather than qsTr().

Also, if you have problems showing accented characters, make sure your files are set to be UTF-8 (Edit… Select Encoding in QT Creator).

Making your App Resolution Independent

The samples in the SDK hardcode the UI…

Item {
   id: screen; width: 320; height: 480

Instead, real applications should call SQL from c++ using a c++ class called QmlApplicationViewer. This class is created automatically when you create a new QT Quick application. Call the call with your root qml file…

QmlApplicationViewer viewer;
viewer.setOrientation(QmlApplicationViewer::Auto);
viewer.setMainQmlFile(QLatin1String("qrc:/yourqml.qml"));
viewer.show();

If you don’t hardcode the width and height in your QML then the QML width and height will take on the width and height of the phone.

Notice the use of qrc:/. This fetches the QML from resources rather than the file system, See Protecting QML files for further details.

If you wish to hide the Symbian buttons at the bottom of the screen, you can comment out the final showFullScreen(); in QmlApplicationViewer::show().

File Access

There’s no way to directly create/open/update and delete files from QML. Instead, you need to call into c++ to access files. However, if you just want to read from files it’s possible to use XMLHttpRequest and read from a local file rather than a remote file. See the example below.

Consuming JSON Data

Qt is able to easily consume XML data via XmlListModel. For JSON, you need to do a little more work…

ListModel {
id: model

signal loadCompleted()

Component.onCompleted: {
var xhr = new XMLHttpRequest;
xhr.open("GET", "mydir/myfile.txt");
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
var a = JSON.parse(xhr.responseText);
for (var b in a) {
var o = a[b];
model.append({label: o.label, value: o.value, description: o.description, image: o.image, selected: false});
}
model.loadCompleted()
}
}
xhr.send();
}
}

This is a slightly modified example of code found on the Nokia Bug Reports

Protecting QML Files

The files describing your screens/code are not compiled and remain readable to hackers should they try to uncompress your installer. This might be a problem, for example, if you have proprietary algorithms, web service calls you wish to keep private or just don’t want people copying your app. 

In order to give these files some protection, you should bundle them into a resource file. Adding your QML files to a QRC (resource file) will get them compressed and compiled into the binary. To use these files, you reference your first QML file (in c++) from a resource, all further relative requests for files come from the resource. If you do need to access files from the file system, for example via XMLHttpRequest,then you can use ‘file:<filename>’.

However, don’t put large graphics in a resource as they get hexdumped which bloats the executable hugely.

Creating a Symbian SIS File

Early versions of the SDK created a SIS file automatically. Newer versions don’t do this and it’s now part of the ‘Run’ step. However, you might not actually want to ‘run’ the app if you just want to copy (e.g. Bluetooth) it to your phone or distribute to other people. ‘Run’ needs a phone to be connected. If you don’t have one connected then QT Creator will complain it cant find the port. You can fix this by clicking on Projects…Symbian Device… Run and remove the final deploy step.

Invoking the External Web Browser

You do this by calling…

Qt.openUrlExternally("http://yoururl")

This only works for me on the device but not under simulation.

Making a Phone Call

You do this by calling…

Qt.openUrlExternally("tel:<number>")

Creating a Splash Screen

A splash screen is just a screen (e.g. a Rectangle) that contains a timer. When the timer expires, it goes to the next screen.

Rectangle {
    id: firstScreen

    <image or whatever here> 

    Timer {
        id: timer
        interval: 3000 // 3 secs
        running: true
        onTriggered: firstScreen.showScreen("nextscreen.qml")
    }
}

See Creating Screens/Pages for details on showScreen() and how to switch pages.

You can also allow the user to press the screen to trigger a change of screen by adding…

MouseArea {
    id: mouseArea
    anchors.fill: parent
    onClicked: firstScreen.showScreen("nextscreen.qml")
}

Dealing with Arrays

QML has poor support for collections (list maps etc). There’s a list element but it’s not that useful because it’s immutable and can’t be dynamically added to or removed from through Javascript operations. There’s a quick workaround that involves you declaring a Javascript Array in a separate .js and just referring to the Array from QML…

In myscript.js:

var myVar = new Array()

In QML:

import "myscript.js" as Script

and use Javascript Array operations in your functions such as…

Script.myVar.push(<yourobject>)