Erste Schritte in der Programmierung mit Qt Quick

Ein Tutorium für Qt Quick basierend auf einer Alarmanwendung.

Dieses Tutorial zeigt, wie man eine einfache Alarmanwendung als Einführung in Qt Quick und Qt Quick Controls entwickelt.

Diese Anwendung ähnelt der Alarmanwendung, die normalerweise auf einem Android-Handy zu finden ist. Mit ihren Funktionen können Sie Alarme eingeben, bearbeiten oder löschen. Ein Alarm kann an einem bestimmten Datum ausgelöst werden, und Sie können ihn so einstellen, dass er an einer Reihe von Folgetagen wiederholt wird.

Auf dem Hauptbildschirm wird die Liste der gespeicherten Alarme angezeigt:

"Alarms application"

Im Detailbildschirm können Sie vorhandene Alarme bearbeiten oder löschen:

"Detail screen"

Das Dialogfenster dient zum Hinzufügen neuer Alarme. Er öffnet sich, wenn Sie auf das "+" RoundButton am unteren Rand des Hauptbildschirms klicken:

"Add alarms"

Die Quelldateien befinden sich im qtdoc-Repository. Sie können die Qt-Quellen entweder aus dem Qt-Projekt holen oder sie als Teil von Qt installieren. Die Anwendung ist auch in der Beispielliste des Willkommensmodus von Qt Creator verfügbar.

Erstellen des Alarms-Projekts

In diesem Abschnitt wird gezeigt, wie das Projekt in Qt Creator erstellt wird. Es wird auf die Dateien eingegangen, die automatisch von Qt Creator generiert werden, sowie auf die beiden Dateien, die der Programmierer in Qt Creator oder einem anderen Editor erstellen muss. Die beiden letztgenannten Dateien sind im Quellcode dieses Lehrgangs enthalten.

Hinweis: Der UI-Text in Qt Creator und der Inhalt der generierten Dateien hängen von der verwendeten Qt Creator Version ab.

Qt Creator

Das Einrichten eines neuen Projekts in Qt Creator wird durch einen Assistenten unterstützt, der Sie Schritt für Schritt durch die Projekterstellung führt. Der Assistent fordert Sie auf, die für den jeweiligen Projekttyp erforderlichen Einstellungen einzugeben, und erstellt das Projekt für Sie.

Um das Projekt "Alarme" zu erstellen, wählen Sie File > New Project > Application (Qt) > Qt Quick Application > Choose. Geben Sie Alarme in das Feld Name ein, und folgen Sie den Anweisungen des Assistenten.

"Qt Creator New Project dialog"

"Project Location"

Der Anwendungsassistent Qt Quick erstellt ein Projekt, das die folgenden Quelldateien enthält:

QuelldateiZweck
CMakeLists.txtDie Projektdatei
main.cppDie Haupt-C++-Code-Datei für die Anwendung.
main.qmlDie QML-Hauptcodedatei für die Anwendung. In dieser Datei werden unsere benutzerdefinierten QML-Typen (AlarmDialog, AlarmModel, AlarmDelegate und TumblerDelegate) instanziiert.

Der Assistent generiert den Code in der Datei main.cpp unten. Dieser Codeblock aktiviert die hohe DPI-Skalierung und deklariert app und engine. Die Engine lädt dann unsere Haupt-QML-Datei.

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

    QQmlApplicationEngine engine;
    QObject::connect(
            &engine, &QQmlApplicationEngine::objectCreationFailed, &app,
            []() { QCoreApplication::exit(-1); }, Qt::QueuedConnection);

Zusätzliche Quelldateien

QuelldateiZweck
qtquickcontrols2.confWählt den Stil Material mit dem Thema Dark.
AlarmDialog.qmlDefiniert den Dialog zum Hinzufügen neuer Alarme.
AlarmDelegate.qmlDefiniert das Layout des Hauptbildschirms der App.
AlarmModel.qmlDefiniert die ListModel, die zum Speichern der Alarmdaten verwendet wird.
TumblerDelegate.qmlDefiniert das grafische Layout der Tumblers.
qml.qrcDie Ressourcendatei, die die Namen der Quelldateien enthält, mit Ausnahme von main.cpp und der Projektdatei.
qtquickcontrols2.conf

Der folgende Ausschnitt zeigt, wie man das Thema Dark im Stil Material einstellt:

[Controls]
Style=Material
[Material]
Theme=Dark
Accent=Red
Main.qml

mainWindowEin ApplicationWindow QML-Typ ist das Wurzelelement in dieser Anwendung.

ApplicationWindow {
    id: window
    width: 400
    height: 500
    visible: true

Die ListView alarmListView kombiniert die Daten von alarmModel mit dem in alarmDelegate definierten Layout.

    ListView {
        id: alarmListView
        anchors.fill: parent
        model: AlarmModel {}
        delegate: AlarmDelegate {}
    }

Neue Alarme können durch Klicken auf RoundButton addAlarmButton hinzugefügt werden. Wenn Sie darauf klicken, öffnet sich ein Dialog Bildschirm alarmDialog.

    RoundButton {
        id: addAlarmButton
        text: "+"
        anchors.bottom: alarmListView.bottom
        anchors.bottomMargin: 8
        anchors.horizontalCenter: parent.horizontalCenter
        onClicked: alarmDialog.open()
    }

    AlarmDialog {
        id: alarmDialog
        x: Math.round((parent.width - width) / 2)
        y: Math.round((parent.height - height) / 2)
        alarmModel: alarmListView.model
    }
AlarmDialog.qml

Dieser Dialog hat ein RowLayout mit je einem Tumbler für Stunden und Minuten und ein RowLayout mit je einem Tumbler für Tag, Monat und Jahr.

    contentItem: RowLayout {
        RowLayout {
            id: rowTumbler

            Tumbler {
                id: hoursTumbler
                model: 24
                delegate: TumblerDelegate {
                    text: alarmDialog.formatNumber(modelData)
                }
            }
            Tumbler {
                id: minutesTumbler
                model: 60
                delegate: TumblerDelegate {
                    text: alarmDialog.formatNumber(modelData)
                }
            }
        }

        RowLayout {
            id: datePicker

            Layout.leftMargin: 20

            property alias dayTumbler: dayTumbler
            property alias monthTumbler: monthTumbler
            property alias yearTumbler: yearTumbler

            readonly property var days: [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

            Tumbler {
                id: dayTumbler

                function updateModel() {
                    // Populate the model with days of the month. For example: [0, ..., 30]
                    var previousIndex = dayTumbler.currentIndex
                    var array = []
                    var newDays = datePicker.days[monthTumbler.currentIndex]
                    for (var i = 1; i <= newDays; ++i)
                        array.push(i)
                    dayTumbler.model = array
                    dayTumbler.currentIndex = Math.min(newDays - 1, previousIndex)
                }

                Component.onCompleted: updateModel()

                delegate: TumblerDelegate {
                    text: alarmDialog.formatNumber(modelData)
                }
            }
            Tumbler {
                id: monthTumbler

                onCurrentIndexChanged: dayTumbler.updateModel()

                model: 12
                delegate: TumblerDelegate {
                    text: alarmDialog.locale.standaloneMonthName(modelData, Locale.ShortFormat)
                }
            }
            Tumbler {
                id: yearTumbler

                // This array is populated with the next three years. For example: [2018, 2019, 2020]
                readonly property var years: (function() {
                    var currentYear = new Date().getFullYear()
                    return [0, 1, 2].map(function(value) { return value + currentYear; })
                })()

                model: years
                delegate: TumblerDelegate {
                    text: alarmDialog.formatNumber(modelData)
                }
            }
        }
    }
}

Wenn Sie in dem Dialog auf OK klicken, werden die eingegebenen Daten zu alarmModel hinzugefügt:

    onAccepted: {
        alarmModel.append({
            "hour": hoursTumbler.currentIndex,
            "minute": minutesTumbler.currentIndex,
            "day": dayTumbler.currentIndex + 1,
            "month": monthTumbler.currentIndex + 1,
            "year": yearTumbler.years[yearTumbler.currentIndex],
            "activated": true,
            "label": "",
            "repeat": false,
            "daysToRepeat": [
                { "dayOfWeek": 0, "repeat": false },
                { "dayOfWeek": 1, "repeat": false },
                { "dayOfWeek": 2, "repeat": false },
                { "dayOfWeek": 3, "repeat": false },
                { "dayOfWeek": 4, "repeat": false },
                { "dayOfWeek": 5, "repeat": false },
                { "dayOfWeek": 6, "repeat": false }
            ],
        })
    }
    onRejected: alarmDialog.close()
AlarmDelegate.qml

Jeder Alarm im Hauptbildschirm ist ein ItemDelegate. Der ItemDelegate root enthält alle Felder des Hauptbildschirms und des Detailbildschirms. Die Felder des Detailbildschirms sind nur sichtbar, wenn ein Alarm angeklickt wurde, d.h. wenn root.checked true ist.

ItemDelegate {
    id: root
    width: parent.width
    checkable: true

    required property int index
    required property int hour
    required property int minute
    required property int day
    required property int month
    required property int year
    required property bool activated
    required property string label
    required property bool repeat
    required property list<var> daysToRepeat

    onClicked: ListView.view.currentIndex = index

    contentItem: ColumnLayout {
        spacing: 0

        RowLayout {
            ColumnLayout {
                id: dateColumn

                readonly property date alarmDate: new Date(
                    root.year, root.month - 1, root.day, root.hour, root.minute)

                Label {
                    id: timeLabel
                    font.pixelSize: (Qt.application as Application).font.pixelSize * 2
                    text: dateColumn.alarmDate.toLocaleTimeString(root.locale, Locale.ShortFormat)
                }
                RowLayout {
                    Label {
                        id: dateLabel
                        text: dateColumn.alarmDate.toLocaleDateString(root.locale, Locale.ShortFormat)
                    }
                    Label {
                        id: alarmAbout
                        text: "⸱ " + root.label
                        visible: root.label.length > 0 && !root.checked
                    }
                }
            }
            Item {
                Layout.fillWidth: true
            }
            Switch {
                checked: root.activated
                Layout.alignment: Qt.AlignTop
                onClicked: root.activated = checked
            }
        }
        CheckBox {
            id: alarmRepeat
            text: qsTr("Repeat")
            checked: root.repeat
            visible: root.checked
            onToggled: root.repeat = checked
        }
        Flow {
            visible: root.checked && root.repeat
            Layout.fillWidth: true

            Repeater {
                id: dayRepeater
                model: root.daysToRepeat
                delegate: RoundButton {
                    required property int dayOfWeek
                    required property bool repeat
                    text: Qt.locale().dayName(dayOfWeek, Locale.NarrowFormat)
                    flat: true
                    checked: repeat
                    checkable: true
                    Material.background: checked ? Material.accent : "transparent"
                    onToggled: repeat = checked
                }
            }
        }

        TextField {
            id: alarmDescriptionTextField
            placeholderText: qsTr("Enter description here")
            cursorVisible: true
            visible: root.checked
            text: root.label
            onTextEdited: root.label = text
        }
        Button {
            id: deleteAlarmButton
            text: qsTr("Delete")
            visible: root.checked
            onClicked: root.ListView.view.model.remove(root.ListView.view.currentIndex, 1)
        }
    }
}
AlarmModel.qml

Diese QML-Datei enthält die Definition von alarmModel, der ListModel, die die Alarmdaten verwaltet.

Sie erstellt fünf ListElements mit Beispielalarmen.

import QtQuick

// Populate the model with some sample data.
ListModel {
    id: alarmModel

    ListElement {
        hour: 6
        minute: 0
        day: 2
        month: 8
        year: 2018
        activated: true
        label: "Wake up"
        repeat: true
        daysToRepeat: [
            ListElement { dayOfWeek: 0; repeat: false },
            ListElement { dayOfWeek: 1; repeat: false },
            ListElement { dayOfWeek: 2; repeat: false },
            ListElement { dayOfWeek: 3; repeat: false },
            ListElement { dayOfWeek: 4; repeat: false },
            ListElement { dayOfWeek: 5; repeat: false },
            ListElement { dayOfWeek: 6; repeat: false }
        ]
    }
    ListElement {
        hour: 6
        minute: 0
        day: 3
        month: 8
        year: 2018
        activated: true
        label: "Wake up"
        repeat: true
        daysToRepeat: [
            ListElement { dayOfWeek: 0; repeat: true },
            ListElement { dayOfWeek: 1; repeat: true },
            ListElement { dayOfWeek: 2; repeat: true },
            ListElement { dayOfWeek: 3; repeat: true },
            ListElement { dayOfWeek: 4; repeat: true },
            ListElement { dayOfWeek: 5; repeat: false },
            ListElement { dayOfWeek: 6; repeat: false }
        ]
    }
    ListElement {
        hour: 7
        minute: 0
        day: 3
        month: 8
        year: 2018
        activated: false
        label: "Exercise"
        repeat: true
        daysToRepeat: [
            ListElement { dayOfWeek: 0; repeat: true },
            ListElement { dayOfWeek: 1; repeat: true },
            ListElement { dayOfWeek: 2; repeat: true },
            ListElement { dayOfWeek: 3; repeat: true },
            ListElement { dayOfWeek: 4; repeat: true },
            ListElement { dayOfWeek: 5; repeat: true },
            ListElement { dayOfWeek: 6; repeat: true }
        ]
    }
    ListElement {
        hour: 5
        minute: 15
        day: 1
        month: 9
        year: 2018
        activated: true
        label: ""
        repeat: false
        daysToRepeat: [
            ListElement { dayOfWeek: 0; repeat: false },
            ListElement { dayOfWeek: 1; repeat: false },
            ListElement { dayOfWeek: 2; repeat: false },
            ListElement { dayOfWeek: 3; repeat: false },
            ListElement { dayOfWeek: 4; repeat: false },
            ListElement { dayOfWeek: 5; repeat: false },
            ListElement { dayOfWeek: 6; repeat: false }
        ]
    }
    ListElement {
        hour: 5
        minute: 45
        day: 3
        month: 9
        year: 2018
        activated: false
        label: ""
        repeat: false
        daysToRepeat: [
            ListElement { dayOfWeek: 0; repeat: false },
            ListElement { dayOfWeek: 1; repeat: false },
            ListElement { dayOfWeek: 2; repeat: false },
            ListElement { dayOfWeek: 3; repeat: false },
            ListElement { dayOfWeek: 4; repeat: false },
            ListElement { dayOfWeek: 5; repeat: false },
            ListElement { dayOfWeek: 6; repeat: false }
        ]
    }
}
TumblerDelegate.qml

TumblerDelegate definiert die grafischen Eigenschaften der Tumblers.

import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Material

Text {
    required property int modelData
    text: modelData
    color: Tumbler.tumbler.Material.foreground
    font: Tumbler.tumbler.font
    opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)
    horizontalAlignment: Text.AlignHCenter
    verticalAlignment: Text.AlignVCenter
}

Eingabe von neuen Alarmen

Am unteren Rand des Startbildschirms befindet sich eine Schaltfläche zum Hinzufügen von Alarmen. Klicken Sie darauf, um den Dialog zum Hinzufügen neuer Alarme zu öffnen.

    RoundButton {
        id: addAlarmButton
        text: "+"
        anchors.bottom: alarmListView.bottom
        anchors.bottomMargin: 8
        anchors.horizontalCenter: parent.horizontalCenter
        onClicked: alarmDialog.open()
    }

Das Dialogfeld für neue Alarme:

"Add alarms"

Alle Felder werden mit Tumbler QML-Typen eingegeben. Wenn Sie OK drücken, werden die in den Tumblern ausgewählten Werte in alarmModel geschrieben.

    contentItem: RowLayout {
        RowLayout {
            id: rowTumbler

            Tumbler {
                id: hoursTumbler
                model: 24
                delegate: TumblerDelegate {
                    text: alarmDialog.formatNumber(modelData)
                }
            }
            Tumbler {
                id: minutesTumbler
                model: 60
                delegate: TumblerDelegate {
                    text: alarmDialog.formatNumber(modelData)
                }
            }
        }

        RowLayout {
            id: datePicker

            Layout.leftMargin: 20

            property alias dayTumbler: dayTumbler
            property alias monthTumbler: monthTumbler
            property alias yearTumbler: yearTumbler

            readonly property var days: [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

            Tumbler {
                id: dayTumbler

                function updateModel() {
                    // Populate the model with days of the month. For example: [0, ..., 30]
                    var previousIndex = dayTumbler.currentIndex
                    var array = []
                    var newDays = datePicker.days[monthTumbler.currentIndex]
                    for (var i = 1; i <= newDays; ++i)
                        array.push(i)
                    dayTumbler.model = array
                    dayTumbler.currentIndex = Math.min(newDays - 1, previousIndex)
                }

                Component.onCompleted: updateModel()

                delegate: TumblerDelegate {
                    text: alarmDialog.formatNumber(modelData)
                }
            }
            Tumbler {
                id: monthTumbler

                onCurrentIndexChanged: dayTumbler.updateModel()

                model: 12
                delegate: TumblerDelegate {
                    text: alarmDialog.locale.standaloneMonthName(modelData, Locale.ShortFormat)
                }
            }
            Tumbler {
                id: yearTumbler

                // This array is populated with the next three years. For example: [2018, 2019, 2020]
                readonly property var years: (function() {
                    var currentYear = new Date().getFullYear()
                    return [0, 1, 2].map(function(value) { return value + currentYear; })
                })()

                model: years
                delegate: TumblerDelegate {
                    text: alarmDialog.formatNumber(modelData)
                }
            }
        }
    }
}

Bearbeiten von Alarmen

Wenn Sie auf einen bestimmten Alarm klicken, können Sie ihn im Detailbildschirm bearbeiten.

Wenn Sie einen Alarm anklicken, wird root.checked auf true gesetzt, wodurch die Felder des Detailbildschirms sichtbar werden.

visible: root.checked

Wenn Sie möchten, dass der Alarm auch an anderen Tagen ausgelöst wird, aktivieren Sie alarmRepeat. Der Repeater zeigt dann für jeden Wochentag eine ankreuzbare RoundButton an.

        Flow {
            visible: root.checked && root.repeat
            Layout.fillWidth: true

            Repeater {
                id: dayRepeater
                model: root.daysToRepeat
                delegate: RoundButton {
                    required property int dayOfWeek
                    required property bool repeat
                    text: Qt.locale().dayName(dayOfWeek, Locale.NarrowFormat)
                    flat: true
                    checked: repeat
                    checkable: true
                    Material.background: checked ? Material.accent : "transparent"
                    onToggled: repeat = checked
                }
            }
        }

Wenn Sie die Beschreibung des Alarms ändern, wird dies anschließend auf dem Hauptbildschirm angezeigt.

        TextField {
            id: alarmDescriptionTextField
            placeholderText: qsTr("Enter description here")
            cursorVisible: true
            visible: root.checked
            text: root.label
            onTextEdited: root.label = text
        }

Löschen von Alarmen

Der Detailbildschirm (siehe oben) verfügt über eine Schaltfläche zum Löschen von Alarmen. Wenn onClicked ausgegeben wird, wird der aktuelle ListElement von alarmModel gelöscht.

        Button {
            id: deleteAlarmButton
            text: qsTr("Delete")
            visible: root.checked
            onClicked: root.ListView.view.model.remove(root.ListView.view.currentIndex, 1)
        }
Zusammenfassung

Die App hat keinen Code, um dem Alarm einen Ton oder eine Vibration hinzuzufügen, noch speichert sie die Alarme in irgendeinem Format oder einer Datenbank. Vielleicht wäre es ein interessantes Programmierprojekt, diese Funktionen hinzuzufügen. Das Speichern der Daten könnte schnell und einfach im JSON-Format erfolgen.

Beispielprojekt @ code.qt.io

Siehe auch JSON-Unterstützung in Qt.

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.