Sur cette page

Premiers pas en programmation avec Qt Quick: une application d'alarmes

Un tutoriel sur Qt Quick basé sur une application d'alarmes.

Ce tutoriel explique comment développer une application d'alarmes simple, en guise d'introduction à Qt Quick et à Qt Quick Controls.

Dans ce tutoriel, vous pouvez saisir, modifier ou supprimer des alarmes. Une alarme peut se déclencher à une date donnée, et vous pouvez la configurer pour qu’elle se répète plusieurs jours d’affilée. Cette application est similaire à l’application d’alarmes que l’on trouve généralement sur un téléphone Android.

Exécution de l'exemple

Vous pouvez exécuter l’exemple à partir de :

Création du projet « Alarms »

Cette section explique comment créer le projet dans Qt Creator. Elle traite des fichiers générés automatiquement par Qt Creator, ainsi que des deux fichiers que le programmeur doit créer dans Qt Creator ou un autre éditeur. Ces deux derniers fichiers sont fournis avec le code source de ce tutoriel.

Remarque : le texte de l'interface utilisateur dans Qt Creator et le contenu des fichiers générés dépendent de la version d'Qt Creator que vous utilisez.

Qt Creator

La configuration d’un nouveau projet dans Qt Creator est facilitée par un assistant qui vous guide pas à pas tout au long du processus de création du projet. L’assistant vous invite à saisir les paramètres nécessaires pour ce type de projet particulier et crée le projet à votre place.

Pour créer le projet « Alarms », sélectionnez « File » > « New Project » > « Application (Qt) » > Qt Quick Application > « Choose ». Saisissez « alarms » dans le champ « Name », puis suivez les instructions de l’assistant. Utilisez l’option « Qt Quick Application (compact) » si vous souhaitez utiliser d’autres systèmes de compilation que CMake ou des versions de Qt antérieures à la version 6.

Mise en place du nouveau projet

Définition de l'emplacement du projet

L'assistant de l'application « Qt Quick » crée un projet contenant les fichiers source suivants :

Fichier sourceObjectif
CMakeLists.txtFichier de projet
main.cppLe fichier de code C++ principal de l'application.
Main.qmlLe fichier de code QML principal de l'application. Nous allons instancier nos types QML personnalisés (AlarmDialog, AlarmModel, AlarmDelegate et TumblerDelegate) dans ce fichier.

L’assistant génère le code dans le fichier main.cpp ci-dessous. Ce bloc de code active la mise à l’échelle haute résolution (High DPI) et déclare app et engine. Le moteur charge ensuite notre fichier QML principal.

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

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

Fichiers source supplémentaires

Fichier sourceObjectif
qtquickcontrols2.confSélectionne le style « Material » avec le thème « Dark ».
AlarmDialog.qmlDéfinit la boîte de dialogue permettant d’ajouter de nouvelles alarmes.
AlarmDelegate.qmlDéfinit la mise en page de l'écran principal de l'application.
AlarmModel.qmlDéfinit l'ListModel utilisée pour stocker les données des alarmes.
TumblerDelegate.qmlDéfinit la mise en page graphique des « Tumblers ».
qml.qrcLe fichier de ressources, qui contient les noms des fichiers source, à l'exception de main.cpp et du fichier de projet.
qtquickcontrols2.conf

L'extrait de code suivant montre comment définir le thème de l'Dark dans le style « Material » :

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

mainWindow, un type QML d’ ApplicationWindow, est l’élément racine de cette application.

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

L’élément « ListView » ( alarmListView ) combine les données issues de alarmModel avec la mise en page définie dans alarmDelegate.

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

De nouvelles alarmes peuvent être ajoutées en cliquant sur RoundButton addAlarmButton . En cliquant dessus, l'écran Dialog s'ouvre : 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

Cette boîte de dialogue comporte un champ « RowLayout » avec un « Tumbler » pour les heures et les minutes, ainsi qu’un autre champ « RowLayout » avec un « Tumbler » pour le jour, le mois et l’année.

    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

            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 (let 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)
                }
            }
        }
    }
}

Si vous cliquez sur OK dans la boîte de dialogue, les données saisies seront ajoutées à alarmModel:

    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

Chaque alarme de l’écran principal correspond à un « ItemDelegate ». L’ ItemDelegate « root » contient tous les champs de l’écran principal et de l’écran de détail. Les champs de l’écran de détail ne sont visibles qu’après avoir cliqué sur une alarme, c’est-à-dire lorsque « root.checked » vaut « true ».

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

Ce fichier QML contient la définition de alarmModel, l'ListModel qui gère les données d'alarme.

Il crée cinq ListElements avec des alarmes d'exemple.

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 définit les propriétés graphiques des Tumblers.

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

Text {
    required property int modelData
    required property int index
    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
}

Ajout de nouvelles alarmes

Au bas de l'écran de démarrage, vous pouvez voir un bouton permettant d'ajouter des alarmes. Cliquez dessus pour ouvrir la boîte de dialogue « Ajouter une nouvelle alarme ».

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

La boîte de dialogue de création d'alarmes :

Configuration des alarmes

Tous les champs sont renseignés à l'aide de types QML d'Tumbler. Si vous appuyez sur « OK », les valeurs sélectionnées dans les tumbler sont enregistrées dans « alarmModel ».

    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

            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 (let 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)
                }
            }
        }
    }
}

Modification des alarmes

Si vous cliquez sur une alarme en particulier, vous pouvez la modifier dans l’écran de détail.

Des réglages d'alarme différents selon les jours

Cliquer sur une alarme définit « root.checked » sur « true », ce qui rend visibles les champs de l’écran de détail.

visible: root.checked

Si vous souhaitez que l’alarme se déclenche également d’autres jours, cochez « alarmRepeat ». Le Repeater affichera une case à cocher « RoundButton » pour chaque jour de la semaine.

        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
                }
            }
        }

Si vous modifiez la description de l’alarme, celle-ci sera ensuite mise à jour sur l’écran principal.

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

Suppression des alarmes

L'écran de détail (voir ci-dessus) comporte un bouton permettant de supprimer des alarmes. Lorsque « onClicked » est sélectionné, l'ListElement actuelle est supprimée de « alarmModel ».

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

Prochaines étapes

L'application ne comporte aucun code permettant d'ajouter un son ou une vibration à l'alarme, et elle ne stocke pas non plus les alarmes dans un format ou une base de données quelconque. Relevez le défi en ajoutant ces fonctionnalités au projet. Le stockage des données pourrait s'effectuer au format JSON.

Fichiers source

Exemple de projet sur code.qt.io

Voir également la prise en charge de JSON dans Qt, tous les exemples Qt, ainsi que les exemples et tutoriels surQt Quick .

© 2026 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.