1. Introduction
    1. GUI in C++, lul
    2. What is QtQuick
  2. First touch
    1. Running qml code
      1. qmlscene
      2. import
      3. qml file structure
    2. id and properties
    3. Nesting
    4. Children and parents
    5. Reactive programming or property binding
  3. Stuff & co.
    1. Adding custom properties
    2. signals
    3. Anchors
    4. MouseArea
  4. Combine all the things
    1. Beyonce
    2. Additional information
    3. Solution

Introduction

GUI in C++, lul

I know, right? Whatever happened to using solid frameworks! things like Electron! I’m not gonna detail “why not electron” here, not even if it’s made with fearless concurrency; we already know it’s shite. Let’s move.

What is QtQuick

QtQuick is a framework for developing user interfaces with QML. QML is a declarative markup language made by the Qt Company. It can be used for several things, like making a language agnostic build system.

QtQuick is an altenative to traditional Qt widgets when developing gui. The interfaces built with it are fluid and ready to be used on any device without any tweaking. Imagine the beauty of Qt, but without creating visual components with C++ code. Imagine the beauty of Qt, but without creating visual components with C++ code!

If you’re familiar with Qt widgets (or any other framework really), using QtQuick will most likely bring you loads of advantages. Things like property binding, smooth transitions and animations, the whole mobile experience shenanigans, code reuse, etc.

First touch

Running qml code

qmlscene

To run qml code you only need the qmlscene executable included with your Qt5 install. qmlscene file.qml will display the interface described in the file. This should only be used for debugging and prototyping. If you intend to deploy or do anything involving real data, you should instead develop a C++ or a Python application.

  1. Python or C++?

    Right now, the answer is neither. In this guide we will only use qml to create a gui without any backend. If you want to go ahead and start using Python, note that PySide2 are the latest officialy supported Qt bindings for Python.

import

Import statements can be used in QML to get access to other qml modules, javascript, C++ (or Python) types, etc. In this guide the only thing we’ll need to import is the QtQuick module with import QtQuick 2.9. You can use anything between 2.0 and the latest version available to you. Just don’t use 1.4 or anything older, unless you have no other choice.

qml file structure

In a qml file you have the import statements at the top and below them a single top level qml component.

// file.qml
import QtQuick 2.9

Rectangle {
// everything else goes here
}

The Rectangle is the most basic visual component of QtQuick. It’s a rectangle with usual properties like, x, y, width, height, and also things like a radius, a color, and so on.

id and properties

The id property is a unique (in the component scope) name associated with each QML component. It’s close to the name of a variable, and it’s visible everywhere in the file. This allows you to use the component anywhere within the file without trouble. In C++ you would declare a variable with Rectangle rect(0, 0, 12, 12); for a square of height 12 placed at position (0, 0). In QML you’d write the same as follows:

Rectangle {
    id: square // no semicolons needed
    x: 0, y: 0 // it's recommended to declare related properties on the same line
    width: 12
    height: width // you can use a property's value to initialize another one

    color: "lightblue"
}

Some other properties that are available in a rectangle are border, gradient, opacity, visible, and much more.

Although the id looks like one, it’s not a real property.

Nesting

You can also nest elements. This is very useful when you want to create somewhat complex components, both in look and behaviour:

Rectangle {
   id: square
   ...
   color: "lightblue"
   Rectangle {
       id: innerBox

       width: 6
       height: width

       // x and y are relative to the parent's coordinates
       // the inner box will ended up centered in its parent
       x: 3, y: 3
   }
}

Children and parents

When a component is nested inside another one, the child can access its parent’s attributes through the parent attribute. The parent can access its children’s through the children attribute, which is a list.

Rectangle {
    id: box
    ...
    radius: innerBox.height / 3 // access any item's properties through its id
    Rectangle {
        id: innerBox
        ...
        // Qt.darker returns the color one shade darker than the one it receives as parameter
        color: Qt.darker(parent.color) // access the parent's properties
    }
}

Reactive programming or property binding

Sometimes you want the user interface you’re building to stay consistent (proportion wise) when the application window is resized or displayed on screens of different size. For the precedent example, it would be nice to keep the inner box centered when the parent’s proportions change. QtQuick, QML in general, provides a really neat reactive programming mechanism to achieve this, property binding. And guess what! We’ve already been using it. Indeed, to bind some property to another one it suffices to declare the former using the later, as we’ve been doing for the height property. height: width When declared like this, everytime the width change, the height is appropriately updated, so the rectangle is always a square. With that in mind we can redefine our two rectangles to get even better reactivity and keep the inner box centered at all times.

Rectangle {
   // nothing to change
   Rectangle {
       id: innerBox

       width: parent.width
       height: width
       x: (parent.width - width) / 2
       y: (parent.height - height) / 2
       ...
   }
}

Stuff & co.

Rectangles are cool and all, but they are boring. There is so much more to QML; let’s go over some notions. Some of them should look familiar to anyone who have used Qt before.

Adding custom properties

When using QML, one can extend a component by adding new porperties to it. Say for example we want a certain component to have a name. We could achieve this with something as simple as: property string name: "Component awesome name". This declaration would go inside the component. Note that a new property doesn’t have to be initialized immediately.

signals

In the Qt framework, a signal is a predefined message an object sends when a certain event occurs. They are an indispensable functionality for any respectable gui framework, under some form or another.

signal eventOccured // signal declaration

// signal handling
onEventOccured : {
    width = width * 2
}

signal buttonClicked(Position clickPosition) // note that signals can also take parameters

onButtonClicked : {
    // oh no. Is this a javascript
    console.log("x: " + clickPosition.x + " y: " + clickPosition.y)
}

A signal is associated with each porperty of every QtQuick component. That means we could implement handlers for onWidthChanged, onColorChnaged, onRadiusChanged, etc. This also applies for our own properties. So we could also implement onNameChanged, since we added a name property previously.

Anchors

An anchor is an attribute that lets you place components relatively to each other without much struggle. They let you turn a sentence like “above the status bar, below the menu” into understandable code.

// tie this component's bottom to the statusbar's top
anchors.bottom: statusBar.top 

// tie this component's top to the menubar's bottom
anchors.top: menuBar.bottom


// make the component fill its parent size
anchors.fill: parent

// centerIn, left and right are other elements that can be used to anchor stuff

Note that anchors you can only anchor components to their siblings and parents.

MouseArea

The mouse area is a basic component that defines an area where mouse events can be intercepted. Typically it’s declared inside another component that it fills completely and when the user interact with the said component through the mouse, those events are available to do awesome stuff.

Rectangle {
    MouseArea {
        anchors.fill: parent
        onClicked: {
            parent.color: "#e3c003"
        }
    }
}

Combine all the things

Beyonce

At this point, we can already do a lot of cool stuff. Let’s make a component conform to the following specs:

  • Shape: circle
  • Color: toggle between blue and red with mouse click
  • Border color: green
  • Border width: 5 normally, and 0 when the circle is pressed
  • Display the text “Hello, world!” in white, centered in the circle

Additional information

We’ve already seen most of these. The mouse press event is similar to a click and it can be handled by implementing onPressed. Displaying the text is as simple as nesting a Text component, another basic building block provided by QtQuick, inside another rectangle. The string it should show can be set with the text attribute, e.g. text: "What are generics?".

I will post a solution to this below, but I would recommend anyone interested in learning QML to try doing it on their own before checking my implementation.

Solution

import QtQuick 2.9

// let's start with a rectangle; almost always start with a rectangle tbh
Rectangle {
    id: button

    signal clicked // we will trigger this signal when the button is clicked

    // when the signal is triggered, we'll toggle the color
    onClicked: {
        // Qt.colorEqual is used to compare colors
        // btw you can specify colors in many formats
        // e.g: "blue", "#0000ff", "#ff0000ff" and many more
        // including hsv and hsl
        // here we use strings
        color =  Qt.colorEqual(color, "blue") ? "red" : "blue"
    }

    width: 50
    height: width
    radius: width / 2 // make the rectangle a circle

    color: "blue"
    border.color: "green"

    border.width: {
        // pressed is a read-only boolean property of MouseArea
        mouseArea.pressed ? 0 : 5
    }

    Text {
        id: label

        anchors.centerIn: parent
        color: "white"
        text: "Hello, world!"
    }

    MouseArea {
        id: mouseArea
        anchors.fill: parent

        // note that clicked() is a function call
        // the parentheses are necessary
        onClicked: button.clicked()
    }
}

Now that you’re initiated to QtQuick, the electron fanboys better watch out.