QML & Qt Quick

Declarative UI with QML, Qt Quick components, animations, and integrating QML with C++ backends.

4 min read
30202 chars

What is QML?

QML (Qt Modeling Language) is a declarative language for designing fluid, animated UIs. It is ideal for:

  • Embedded HMI — dashboards, touch interfaces
  • Modern desktop apps — animated, GPU-accelerated UIs
  • Rapid prototyping — visual results without recompiling C++

QML handles the view layer; C++ handles logic and data.


Your First QML File

// Main.qml
import QtQuick 6.0
import QtQuick.Controls 6.0

ApplicationWindow {
    id: root
    visible: true
    width: 480
    height: 320
    title: "EmbeddexAI — QML Demo"

    Rectangle {
        anchors.fill: parent
        color: "#1e1e2e"

        Column {
            anchors.centerIn: parent
            spacing: 20

            Text {
                text: "Hello from QML"
                color: "#cdd6f4"
                font.pixelSize: 28
                font.bold: true
                anchors.horizontalCenter: parent.horizontalCenter
            }

            Button {
                text: "Click Me"
                anchors.horizontalCenter: parent.horizontalCenter
                onClicked: label.text = "Button clicked!"
            }

            Text {
                id: label
                text: "Waiting..."
                color: "#a6e3a1"
                font.pixelSize: 16
                anchors.horizontalCenter: parent.horizontalCenter
            }
        }
    }
}

Loading QML from C++

// main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>

int main(int argc, char *argv[]) {
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl("qrc:/qml/Main.qml"));

    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

CMakeLists.txt — add QML module:

find_package(Qt6 REQUIRED COMPONENTS Quick Qml)

qt_add_qml_module(MyApp
    URI MyApp
    VERSION 1.0
    QML_FILES
        qml/Main.qml
        qml/Dashboard.qml
)

target_link_libraries(MyApp PRIVATE Qt6::Quick Qt6::Qml)

QML Layout & Anchors

QML uses an anchor system for positioning:

Rectangle {
    width: 200; height: 100
    color: "#313244"
    radius: 8

    Text {
        anchors.centerIn: parent          // center in parent
        text: "Centered"
        color: "white"
    }

    Rectangle {
        anchors.top: parent.top           // stick to top
        anchors.right: parent.right       // stick to right
        anchors.margins: 8
        width: 12; height: 12
        color: "#f38ba8"
        radius: 6
    }
}

Use RowLayout, ColumnLayout, GridLayout from QtQuick.Layouts:

import QtQuick.Layouts 6.0

RowLayout {
    spacing: 12
    Button { text: "A"; Layout.fillWidth: true }
    Button { text: "B"; Layout.fillWidth: true }
    Button { text: "C"; Layout.preferredWidth: 80 }
}

Animations

Rectangle {
    id: box
    width: 60; height: 60
    color: "#89b4fa"
    radius: 8

    // Property animation
    NumberAnimation on x {
        from: 0; to: 400
        duration: 1000
        easing.type: Easing.InOutQuad
        loops: Animation.Infinite
    }

    // Behavior — animate any change to opacity
    Behavior on opacity {
        NumberAnimation { duration: 300 }
    }

    MouseArea {
        anchors.fill: parent
        onClicked: box.opacity = box.opacity < 1 ? 1.0 : 0.3
    }
}

State-based animations:

Rectangle {
    id: indicator
    width: 100; height: 100
    color: "gray"

    states: [
        State { name: "active";   PropertyChanges { target: indicator; color: "#a6e3a1" } },
        State { name: "error";    PropertyChanges { target: indicator; color: "#f38ba8" } }
    ]

    transitions: Transition {
        ColorAnimation { duration: 400 }
    }

    MouseArea {
        anchors.fill: parent
        onClicked: indicator.state = (indicator.state === "active") ? "error" : "active"
    }
}

Exposing C++ to QML

Method 1 — Register a QObject as Context Property

// backend.h
class Backend : public QObject {
    Q_OBJECT
    Q_PROPERTY(float temperature READ temperature NOTIFY temperatureChanged)

public:
    float temperature() const { return m_temp; }

public slots:
    void readSensor() { m_temp = 36.5f; emit temperatureChanged(m_temp); }

signals:
    void temperatureChanged(float temp);

private:
    float m_temp = 0.0f;
};
// main.cpp
Backend backend;
engine.rootContext()->setContextProperty("backend", &backend);
engine.load(QUrl("qrc:/qml/Main.qml"));
// Main.qml
Text {
    text: "Temp: " + backend.temperature.toFixed(1) + " °C"
}
Button {
    text: "Read"
    onClicked: backend.readSensor()
}

Method 2 — Register as QML Type

// In main.cpp before engine.load()
qmlRegisterType<Backend>("com.embeddexai", 1, 0, "Backend");
import com.embeddexai 1.0

Backend { id: backend }
Text { text: backend.temperature.toFixed(1) + " °C" }

ListView with C++ Model

// sensormodel.h
class SensorModel : public QAbstractListModel {
    Q_OBJECT
public:
    enum Roles { NameRole = Qt::UserRole + 1, ValueRole };
    // ... implement rowCount, data, roleNames
};
ListView {
    width: 300; height: 400
    model: sensorModel   // set via context property

    delegate: Rectangle {
        width: ListView.view.width
        height: 48
        color: index % 2 === 0 ? "#313244" : "#1e1e2e"

        Row {
            anchors { left: parent.left; leftMargin: 16; verticalCenter: parent.verticalCenter }
            spacing: 16
            Text { text: model.name;  color: "#cdd6f4" }
            Text { text: model.value; color: "#a6e3a1" }
        }
    }
}

Summary

ConceptKey Point
QMLDeclarative language for UI
Qt QuickQML component library (Rectangle, Text, etc.)
AnchorsFlexible, resolution-independent positioning
AnimationsNumberAnimation, Behavior, State/Transition
C++ integrationsetContextProperty or qmlRegisterType
QML + C++QML = view, C++ = logic and data

Next tutorial → Model/View Architecture