Fading Coder

One Final Commit for the Last Sprint

Home > Tools > Content

Getting Started with QML: Core Concepts and Fundamentals

Tools Nov 6 1
  1. Overview

QML (Qt Markup Language) represents a declarative approach to building user interfaces within the Qt framework. Having worked with Qt for several years, from version 4.7 through the current Qt 5.6 releases, exploring QML opens up possibilities for building modern embedded and mobile applications. This guide walks through essential QML concepts and provides practical examples to illustrate key functionality.

  1. Prerequisites

Before diving into QML development, familiarize yourself with these fundamental areas:

  1. Basic elements
  2. Components (composite elements)
  3. Positioners (layout containers)
  4. Anchor-based element positioning
  5. Input elements for single-line and multi-line text
  6. Quick Controls components
  7. Models and views
  8. Canvas drawing capabilities
  9. Basic Elements

QML elements divide into two categories: visual and non-visual.

Visual Elements

  • Item - Base container for visual elements
  • Rectangle - Rectangular shape with customizable fill and border
  • Text - Display text content
  • Image - Display raster graphics

Non-Visual Elements

  • MouseArea - Defines interactive mouse regions

An important distinction: MouseArea itself is invisible despite providing interaction capabilities. It simply defines a region that responds to mouse events.

Separation of Display and Interaction

QML follows a design principle where visual display elements remain separate from interaction handlers. This separation allows developers to build flexible, maintainable interfaces by combining visual components with interaction areas.

  1. Components

A component encapsulates reusable combinations of basic elements into a single file. This promotes code reuse and cleaner application architecture. Creating components involves grouping related elements and exposing necessary properties and signals.

  1. Positioners

Positioners automatically arrange child elements within a container:

  • Row - Arranges items horizontally
  • Column - Arranges items vertically
  • Grid - Arranges items in a grid pattern
  • Flow - Arranges items like text wrapping
  1. Anchor-Based Layout

Beyond postiioners, elements can be positioned using anchors. Anchors define relationships between element edges and centers, enabling precise layout control through properties like anchors.left, anchors.right, anchors.centerIn, and anchors.horizontalCenter.

  1. Input Elements
  • TextInput - Single-line text input field
  • TextEdit - Multi-line rich text editing control
  1. Quick Controls Components

Qt Quick Controls provides a comprehensive set of UI components for desktop and mobile applications:

Application Structure

ApplicationWindow Top-level application window (similar to QMainWindow)
MenuBar Horizontal menu bar at window top
StatusBar Status information display bar
ToolBar Toolbar for containing tool buttons and controls
Action Abstract action for binding to UI elements

Navigation and Views

ScrollView Scrollable container view
SplitView Resizable split panel layout
StackView Stack-based navigation container
TabView Tabbed panel container
TableView Table display with headers and scrolling

Controls

BusyIndicator Activity indication display
Button Push button control
CheckBox Checkbox selection control
ComboBox Dropdown selection control
GroupBox Container with title and border
Label Text display label
ProgressBar Progress indication bar
RadioButton Radio button selection
Slider Slider control for value selection
SpinBox Numeric value spinner
Switch Toggle switch control
TextArea Multi-line text editing area
TextField Single-line text input field
ToolButton Toolbar button control

Menu Components

Menu Menu, submenu, and popup menu
MenuSeparator Menu separator line
MenuItem Individual menu entry
  1. Models and Views

QML provides sophisticated model-view architecture through components like ListView, GridView, and Repeater. These work together with delegate components to display data efficiently. The Repeater element creates multiple instances based on a model, while delegates define how each item renders.

  1. Canvas Element

The Canvas element, introduced in Qt5, enables custom drawing operations similar to HTML5 canvas. It supports:

  • Drawing paths with custom pens
  • Fill and gradient styles
  • Text rendering
  • Path-based drawing commands
  1. Practical Examples

Example 1: Reusable Rounded Rectangle Component

import QtQuick 2.5

// Rounded rectangle with click interaction support
Rectangle {
    property alias displayText: label.text;
    property alias displayColor: label.color;

    id: root;
    width: 120;
    height: 120;
    radius: 60;
    antialiasing: true;
    signal activated();

    MouseArea {
        width: root.width;
        height: root.height;

        onClicked: {
            root.activated();
            console.log("rectangle activated");
        }
    }

    Text {
        id: label;
        text: "";
        color: "black";
        anchors.centerIn: parent;
    }
}

Example 2: Rotating Pinwheel Animation

import QtQuick 2.0
import QtQuick.Window 2.0
import QtGraphicalEffects 1.0

import "../components"

Window {
    id: mainWindow;
    visible: true;
    width: 600;
    height: 400;

    Rectangle {
        id: background;
        color: "lightsteelblue";
        width: mainWindow.width;
        height: mainWindow.height;
    }

    MouseArea {
        width: background.width;
        height: background.height;

        onClicked: {
            rotationController.pause();
        }
    }

    RoundedBox {
        id: rotatingItem;
        anchors.centerIn: parent;

        ConicalGradient {
            anchors.fill: parent
            gradient: Gradient {
                GradientStop { position: 0.0; color: "lightsteelblue" }
                GradientStop { position: 1.0; color: "blue" }
            }
        }

        RotationAnimation {
            id: rotationController;
            target: rotatingItem;
            loops: Animation.Infinite;
            from: 0;
            to: 360;
            duration: 1000;
        }

        onActivated: {
            if (rotationController.paused) {
                rotationController.resume();
            } else {
                rotationController.start();
            }
        }
    }
}

Example 3: Traffic Light with State-Based Colors

import QtQuick 2.0
import QtQuick.Window 2.0

import "../components"

Window {
    id: root;
    visible: true;
    width: 370;
    height: 130;

    function executeAction() {
        console.log("action executed");
    }

    Rectangle {
        id: container;
        width: root.width;
        height: root.height;
        anchors.centerIn: parent;

        Row {
            id: lightCluster;
            spacing: 2;

            states: [
                State {
                    name: "stopSignal"
                    PropertyChanges { target: redLamp; color: "red"; }
                    PropertyChanges { target: greenLamp; color: "black"; }
                    PropertyChanges { target: amberLamp; color: "black"; }
                },
                State {
                    name: "goSignal"
                    PropertyChanges { target: redLamp; color: "black"; }
                    PropertyChanges { target: greenLamp; color: "green"; }
                    PropertyChanges { target: amberLamp; color: "black"; }
                },
                State {
                    name: "cautionSignal"
                    PropertyChanges { target: redLamp; color: "black"; }
                    PropertyChanges { target: greenLamp; color: "black"; }
                    PropertyChanges { target: amberLamp; color: "yellow"; }
                }
            ]

            anchors.centerIn: parent;

            RoundedBox {
                id: redLamp;
                color: "black";
                radius: width / 2;
            }

            RoundedBox {
                id: greenLamp;
                color: "black";
                radius: width / 2;
            }

            RoundedBox {
                id: amberLamp;
                color: "black";
                radius: width / 2;
            }

            transitions: [
                Transition {
                    from: "stopSignal"
                    to: "cautionSignal"
                    ColorAnimation { target: redLamp; properties: "color"; duration: 1000; }
                    ColorAnimation { target: amberLamp; properties: "color"; duration: 1000; }
                }
            ]
        }

        property bool isRedActive: false;
        MouseArea {
            anchors.fill: parent;

            onClicked: {
                if (lightCluster.state == "stopSignal" || lightCluster.state == "goSignal") {
                    lightCluster.state = "cautionSignal";
                } else {
                    if (container.isRedActive == false) {
                        lightCluster.state = "stopSignal";
                        container.isRedActive = true;
                    } else {
                        lightCluster.state = "goSignal";
                        container.isRedActive = false;
                    }
                }
            }
        }
    }
}

Example 4: Timer-Controlled Traffic Light

property bool isStopLight: false;
property int elapsedCounter: 0;

Timer {
    id: redTimer;
    interval: 50;
    repeat: true;
    triggeredOnStart: true;
    property int duration: 60;

    onTriggered: {
        if (lightCluster.state != "stopSignal") {
            lightCluster.state = "stopSignal";
            root.isStopLight = true;
        }

        ++elapsedCounter;
        redLamp.displayText = duration - elapsedCounter;
        if (duration <= elapsedCounter) {
            elapsedCounter = 0;
            amberTimer.start();
            redTimer.stop();
        }
    }
}

Timer {
    id: amberTimer;
    interval: 1000;
    repeat: true;
    triggeredOnStart: true;
    property int duration: 3;

    onTriggered: {
        if (lightCluster.state != "cautionSignal") {
            lightCluster.state = "cautionSignal";
        }
        ++elapsedCounter;
        amberLamp.displayText = duration - elapsedCounter;
        if (duration <= elapsedCounter) {
            elapsedCounter = 0;
            if (isStopLight) {
                greenTimer.start();
            } else {
                redTimer.start();
            }
            stop();
        }
    }
}

Timer {
    id: greenTimer;
    interval: 150;
    repeat: true;
    triggeredOnStart: true;
    property int duration: 20;

    onTriggered: {
        if (lightCluster.state != "goSignal") {
            lightCluster.state = "goSignal";
            root.isStopLight = false;
        }

        ++elapsedCounter;
        greenLamp.displayText = duration - elapsedCounter;
        if (duration <= elapsedCounter) {
            elapsedCounter = 0;
            amberTimer.start();
            greenTimer.stop();
        }
    }
}

Example 5: Calendar View with GridView

import QtQuick 2.6
import QtQuick.Window 2.0

import "../components"

Window {
    visible: true;
    width: 300;
    height: 300;

    Rectangle {
        id: mainContainer;
        anchors.fill: parent;
        width: mainContainer.width;
        height: mainContainer.height;
        color: "yellow";

        Row {
            id: dayHeader;
            spacing: 2;
            padding: 5;

            Repeater {
                model: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
                Rectangle {
                    width: (mainContainer.width - 6 * dayHeader.spacing - 10) / 7;
                    height: 30;
                    radius: 3;
                    color: "lightBlue"
                    Text {
                        anchors.centerIn: parent;
                        text: modelData;
                    }
                }
            }
        }

        GridView {
            id: dateGrid;
            boundsBehavior: Flickable.StopAtBounds;
            anchors {
                top: dayHeader.bottom;
                left: mainContainer.left;
                leftMargin: 5;
                right: mainContainer.right;
                rightMargin: 5;
                bottom: mainContainer.bottom;
            }
            model: 42;

            cellWidth: (mainContainer.width - 10) / 7;
            cellHeight: (mainContainer.width - 10) / 7;

            delegate: dateDelegate;
            focus: true;
        }

        Component {
            id: dateDelegate;
            Rectangle {
                width: dateGrid.cellWidth;
                height: dateGrid.cellHeight;
                color: GridView.isCurrentItem ? "green" : "lightGreen"
                border.color: Qt.lighter("green");
                Text {
                    anchors.centerIn: parent;
                    font.pixelSize: 10;
                    text: index + 1;
                }
            }
        }
    }
}

  1. Key Takeaways
  • Element separation: QML encourages separating visual display from interaction handling (e.g., MouseArea paired with Rectangle)
  • Component reuse: Group reusable element combinations into components for cleaner code
  • State machines: The State and Transition elements provide powerful mechanisms for managing UI state changes with automatic animations
  • Positioning flexibility: Choose between positioners (Row, Column, Grid) and anchor-based layouts based on your needs
  • Delegate patterns: ListView, GridView, and Repeater use delegates to render model data efficiently
  • Animation support: Built-in animation types (NumberAnimation, RotationAnimation, ColorAnimation) simplify creating smooth transitions

These foundational concepts provide the building blocks for creating sophisticated QML-based applications across desktop and mobile platforms.

Tags: QML

Related Articles

Efficient Usage of HTTP Client in IntelliJ IDEA

IntelliJ IDEA incorporates a versatile HTTP client tool, enabling developres to interact with RESTful services and APIs effectively with in the editor. This functionality streamlines workflows, replac...

Installing CocoaPods on macOS Catalina (10.15) Using a User-Managed Ruby

System Ruby on macOS 10.15 frequently fails to build native gems required by CocoaPods (for example, ffi), leading to errors like: ERROR: Failed to build gem native extension checking for ffi.h... no...

Resolve PhpStorm "Interpreter is not specified or invalid" on WAMP (Windows)

Symptom PhpStorm displays: "Interpreter is not specified or invalid. Press ‘Fix’ to edit your project configuration." This occurs when the IDE cannot locate a valid PHP CLI executable or when the debu...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.