Module 2: Starting a QML project with Clickable
In this module you’ll learn the basics of starting a QML project with Clickable. You’ll learn the different parts of a QML app and how they work together. And at the end of this module you’ve written a simple shopping list app.
You can find this module’s code of the app in the GitLab repository of the course. |
1. Introduction
QML (Qt Modeling Language) is a language for designing user interfaces for applications. You describe the layout of your application in a declarative language similar to JSON or CSS. This way you add buttons, labels, lists, images, and so on.
To define the behaviour of your app, you add code. You can write this code in various programming languages, such as JavaScript or Python. In the previous module you already saw what choices Clickable gives you.
In this module you’ll create a QML app with JavaScript for a simple shopping list.
2. Starting from the QML Only template
Create a new Clickable project like you did in the previous module, but this time answer the questions with some more attention:
clickable create
Clickable first asks you to choose a template. These templates are based on the language you want to use. Choose 1 - QML Only for this module, because you’re going to develop a QML app with JavaScript code.
The next question is about the app title. Give it a short but descriptive title, such as Shopping List. After this, enter a description. This can be a bit longer than the title, but keep it relatively short too, such as A simple shopping list app.
Then comes the app name. This should consist of lowercase letters only, for instance shoppinglist. Then comes your name, also in lowercase letters. After this, you can enter your full name with capitals, as well as your email address.
Then you choose the license you want to use to publish your app. You can find more information about some popular open source licenses on the web site of the Open Source Initiative. After this, enter the current year for the copyright notice (you can just confirm the current year with a press on Enter), and just confirm the default choices for Git Tag Versioning and Save as Default.
Enter the directory the Clickable command created and open the project in Visual Studio Code:
cd shoppinglist code .
Visual Studio Code asks you if you trust the authors of the files in this directory. Confirm with a click on "Yes, I trust the authors".
3. Project structure
At the left, Visual Studio Code shows you some files and directories that Clickable has created from the chosen app template. Click on all directories to reveal their contents. You should see this:

-
The directory assets has a file with an Ubuntu logo by default. You can replace this if you want to give your app another logo.
-
The directory po has a pot file you can use to translate your app.
-
The directory qml has a file Main.qml with the QML code for your app’s user interface.
If you take a look at the other files in the project, you’ll see that Clickable has put all information you have entered after the clickable create
command in the right places, sich as the license in the file LICENSE, and the name, title, description and maintainer in the file manifest.json.in. The latter file is used by Open Store if you upload your app to the store later.
4. The Main.qml file
Let’s have a look at the Main.qml file. If you chose 1 - QML Only as the template for your app, Clickable creates this file:
import QtQuick 2.7 import Ubuntu.Components 1.3 //import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import Qt.labs.settings 1.0 MainView { id: root objectName: 'mainView' applicationName: 'shoppinglist.koenvervloesem' automaticOrientation: true width: units.gu(45) height: units.gu(75) Page { anchors.fill: parent header: PageHeader { id: header title: i18n.tr('Shopping List') } Label { anchors { top: header.bottom left: parent.left right: parent.right bottom: parent.bottom } text: i18n.tr('Hello World!') verticalAlignment: Label.AlignVCenter horizontalAlignment: Label.AlignHCenter } } }
4.1. Import statements
The import
statements in the beginning make a number of components available in your application. QtQuick is the standard library for writing QML applications, providing all basic components for creating user interfaces. Ubuntu.Components includes some components to give your application an Ubuntu Touch look and feel. And Qt.labs.settings provides persistent application settings. For instance, this allows your application to remember its window sizes and positions.
If you need any other specific functionality in your QML app, you just add extra import statements in the beginning of your QML file. You can find the right import statement in the functionality’s documentation. Have a look at the API documentation for Ubuntu Touch and more specifically Qt’s documentation.
You can specify the version of the imported component. Generally you just use the versions defined in Clickable’s templates. |
4.2. Comments
One of the import statements is preceded by two slashes (//
). This is a comment, which means that everyting after the two slashes on this line is ignored by the build tools. In this case the comment is used to 'deactivate' a statement, but you can also use it to add an explanation to your code. We recommend you to always comment more difficult parts of your code.
You can also create multi-line comments. Clickable actually creates one in the beginning of the file, with the app’s license:
/* * Copyright (C) 2022 Koen Vervloesem * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * shoppinglist is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */
Everything between /*
and */
is ignored by the build tools.
4.3. The MainView element
MainView is the root element of your user interface. It has a couple of properties. For instance, applicationName
sets the name of this application. It should match the name
field of the file manifest.json.in.
Other interesting properties are the main view’s width
and height
, which define the initial size of the app’s main window. The dimensions are not defined in terms of pixels, but as grid units. A grid unit is independent of the device’s screen resolution. This guarantees that your interface elements have the same size on all screens.
So if you define the MainView
width as follows:
width: units.gu(45)
The method units.gu
returns the number of pixels 45 grid units correspond to. The units
property is is a global instance of the Units component from Ubuntu.Components.
If you look at the documentation of the MainView
component, you’ll see that it has some other properties that aren’t defined in the template. For instance, you can change the window’s background color by adding the following property:
backgroundColor: UbuntuColors.graphite
If you build your app with clickable desktop
, you’ll see that the background color has changed to grey:

If you want to know which colors you can use this way, consult the documentation of Ubuntu.Components UbuntuColors. This component offers a color palette of a dozen of colors following the Ubuntu brand guidelines. Graphite is recommended for a darker background.
4.4. The Page element
The MainView
element contains a Page element. This represents a single view in an Ubuntu Touch application.
The first line you see in this page is:
anchors.fill: parent
Anchors are an important concept in Qt Quick, used to position layout items. What anchors.fill
does is to set the left, right, top and bottom to the left, right, top and bottom of the target item, in this case parent
. The latter refers to the element that contains this element, in this case MainView
. So essentially this line says: let the page fill the whole main view.
Next the page defines a header
property. This is a PageHeader element. The id
property is used to refer to this page header later. And the title
property defines the title to display in the page header.
You could define the title just as follows:
title: 'Shopping List'
However, Clickable’s template is already prepared for internationalization (i18) support, so it defines it like this:
title: i18n.tr('Shopping List')
Ubuntu.Components i18 is a property that provides internationalization support. Its method tr
translates the specified text to the user’s language.
This means that you don’t have to change your QML code if you want to translate your app to another language, such as French or Dutch. You only have to create .po file based on the .pot file in the po directory, with the string after msgstr
as the translation for the string after the corresponding msgid
. The .po file should be named after your language code, for instance fr.po for French and nl.po for Dutch.
The page header can also have a subtitle:
subtitle: i18n.tr('Never forget what to buy')
If you rebuild the app, you see that the page header now has a subtitle in a slightly smaller font and other color than the title:

Under the hood, Clickable has also regenerated the .pot file and added the new translatable string 'Never forget what to buy' to it:
#: ../qml/Main.qml:39 shoppinglist.desktop.in.h:1 msgid "Shopping List" msgstr "" #: ../qml/Main.qml:40 msgid "Never forget what to buy" msgstr "" #: ../qml/Main.qml:53 msgid "Hello World!" msgstr ""
Each translatable string in the .pot file is provided with the location of that string in your code. This is helpful for translators if they’re not sure in which context the string is used. |
If you want to change the colors of the page header, you need to add a StyleHints element:
header: PageHeader { id: header title: i18n.tr('Shopping List') subtitle: i18n.tr('Never forget what to buy') StyleHints { foregroundColor: UbuntuColors.orange } }
This foregroundColor
property of the StyleHints
element gives the title an orange foreground color:

Other useful properties of the StyleHints
element are backgroundColor
and dividerColor
.
4.5. The Label element
The Page
element also contains a Label element, which is showing the text "Hello World!". First come some anchors:
anchors { top: header.bottom left: parent.left right: parent.right bottom: parent.bottom }
We bind the top of this label to the bottom of the element with id header
, and set the left, right and bottom to the left, right and bottom of the parent element (the Page
element). The result is that the label comes right under the page header and fills the rest of the page.
Then the text
property specifies the text to show in the label, and the verticalAlignment
and horizontalAlignment
properties center the label vertically (Label.AlignVCenter
) and horizontally (Label.AlignHCenter
). That’s why the text "Hello World!" is shown in the center of the part under the page header.
You can also change the label’s font size. For a larger text, this becomes:
font.pixelSize: FontUtils.sizeToPixels("large")
The default text size is "medium" . Have a look at Ubuntu.Components FontUtils for all available values.
|
5. Adding a button
So now that you know the meaning of all elements in the default QML-only template, it’s time to add some other elements.
One of the most interesting components is a button. In Ubuntu Touch apps, you can create this as a Ubuntu.Components Button. So let’s remove the Label
element in your QML file and replace it by the following button:
Button { id: buttonAdd anchors { top: header.bottom right: parent.right topMargin: units.gu(2) rightMargin: units.gu(2) } text: i18n.tr('Add') }
This anchors the top of the button to the bottom of the page header with id header and the right of the button to the right of the parent (the page). It also adds a margin of two grid units at the top and to the right. The result looks like this:

Now a button isn’t worth anything if nothing happens when you click on it, so let’s add some behavior to the button. You do this by adding an onClicked
property to the button:
onClicked: console.log(text + i18n.tr(' button clicked'))
If you build this app and run it on your desktop with clickable desktop
, click on the button. You’ll see the following message in the console where you typed the Clickable command:
qml: Add button clicked
So every time you click on the button, the JavaScript code after onClicked
is executed.
6. Adding a text field
A button is an interesting component to trigger a simple action, but another useful component is a TextField. This is used to enter the text you want to add to your shopping list.
So add a TextField
element like this to the page:
TextField { id: textFieldInput anchors { top: header.bottom left: parent.left topMargin: units.gu(2) leftMargin: units.gu(2) } placeholderText: i18n.tr('Shopping list item') }
This shows a text field with the placeholder text "Shopping list item", positioned at the top left with a margin of 2 grid units to the page header and the left side of the page:

If you now start typing text into this text field, the placeholder text disappears.
The next step is doing something with the text you’ve typed in when you click on the button. Let’s first just show your text in the console. This is done by replacing the onClicked
handler in the Button
element by:
onClicked: console.log(i18n.tr('Shopping list item: ' + textFieldInput.text))
7. Adding a list
In the last part of this course, we’re going to add the text you typed into the text field to a shopping list instead of just showing it on the console.
A list is a bit more complex than the components we’ve seen before, because it actually consists of three parts:
-
a model, which contains the data
-
a view, which displays the data
-
a delegate, which dictates how the data should appear in the view
You can read more about the important concepts of a model, a view and a delegate in the document Models and Views in Qt Quick. |
If we apply this approach to a list, this means we need a ListModel, a ListView, and a ListItem as a delegate.
Let’s first create a model showing some static data, right before the Page
element:
ListModel { id: shoppinglistModel ListElement { name: "apples" } ListElement { name: "milk" } ListElement { name: "bread" } }
A ListElement represents an item in a list, containing role definitions. The name of a role has to begin with a lowercase letter, and the value should be a constant, such as a string in this case. We chose name
as the name of the role, but you could also name it otherwise. You could also add other roles in the same list element, such as a price, but in our simple shopping list app we only need one role.
Contrary to all the other elements we’ve seen, the ListModel element is not in the Page element because it doesn’t display anything.
|
Then create the view, right under the TextField
element:
ListView { id: shoppinglistView anchors { top: textFieldInput.bottom bottom: parent.bottom left: parent.left right: parent.right topMargin: units.gu(2) } model: shoppinglistModel delegate: ListItem { width: parent.width height: units.gu(3) Text { text: name anchors { left: parent.left leftMargin: units.gu(2) verticalCenter: parent.verticalCenter } } } }
This ListView
element refers to the element with ID shoppinglistModel
as its model. You also specify that this view contains ListItem
elements as a delegate, with a Text
element containing name
as its text. This name
comes from the model you defined earlier.
If you now build this application, it will show a list of the three items you defined in the list model:

But what if you want to add your own items dynamically to the list? Just remove the hardcoded list elements from the list model, so the latter is reduced to the following:
ListModel { id: shoppinglistModel }
Then in the Button
element, change the handler for the onClicked
signal to:
onClicked: shoppinglistModel.append({"name": textFieldInput.text})
So when you click on the button, it adds a name role with the contents of the text field as its value to the shopping list model. This name is then shown in the Text
element of the ListItem
delegate.
In the next module, we’re going deeper into some other QML components.