Démarrer la programmation avec Qt Quick: une application d'alarme
Un tutoriel pour Qt Quick basé sur une application d'alarme.
Ce tutoriel montre comment développer une application d'alarme 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 programmer pour qu'elle se répète une série de jours suivants. Cette application est similaire à l'application d'alarme que l'on trouve habituellement sur un téléphone Android.
Exécution de l'exemple
Pour exécuter l'exemple à partir de Qt Creatorouvrez le mode Welcome et sélectionnez l'exemple à partir de Examples. Pour plus d'informations, voir Qt Creator: Tutoriel : Construire et exécuter.
Création du projet Alarmes
Cette section montre comment créer le projet dans Qt Creator. Elle traite des fichiers générés automatiquement par Qt Creator, et des deux fichiers que le programmeur doit créer dans Qt Creator ou dans un autre éditeur. Ces deux derniers fichiers sont inclus dans 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 de 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 dans le processus de création du projet. L'assistant vous invite à saisir les paramètres nécessaires pour ce type particulier de projet et crée le projet pour vous.
Pour créer le projet Alarmes, sélectionnez File > New Project > Application (Qt) > Qt Quick Application > Choose. Saisissez alarmes dans le champ Name et suivez les instructions de l'assistant. Utilisez l'application Qt Quick (compact) si vous souhaitez utiliser d'autres systèmes de construction que CMake ou des versions de Qt inférieures à 6.


L'assistant de l'application Qt Quick crée un projet qui contient les fichiers sources suivants :
| Fichier source | Objectif |
|---|---|
| CMakeLists.txt | Le fichier de projet |
| main.cpp | Le fichier de code C++ principal de l'application. |
| Main.qml | Le 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 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 source | Objectif |
|---|---|
qtquickcontrols2.conf | Sélectionne le style Material avec le thème Dark. |
AlarmDialog.qml | Définit la boîte de dialogue pour l'ajout de nouvelles alarmes. |
AlarmDelegate.qml | Définit la disposition de l'écran principal de l'application. |
AlarmModel.qml | Définit le site ListModel utilisé pour stocker les données des alarmes. |
TumblerDelegate.qml | Définit la disposition graphique des Tumblers. |
qml.qrc | Le fichier de ressources, qui contient les noms des fichiers sources, à l'exception de main.cpp et du fichier de projet. |
qtquickcontrols2.conf
L'extrait suivant montre comment définir le thème Dark dans le style Material:
[Controls] Style=Material [Material] Theme=Dark Accent=Red
Main.qml
mainWindowLe thème ApplicationWindow, un type QML , est l'élément racine de cette application.
ApplicationWindow { id: window width: 400 height: 500 visible: true
Le site ListView alarmListView combine les données 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 . Un clic ouvre un écran Dialog 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
Cet écran de dialogue comporte un RowLayout avec un Tumbler pour les heures et les minutes, et un 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 est un ItemDelegate. Le ItemDelegate root contient tous les champs de l'écran principal et de l'écran de détail. Les champs de l'écran détaillé ne sont visibles qu'après avoir cliqué sur une alarme, c'est-à-dire lorsque root.checked est 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, le ListModel qui gère les données d'alarme.
Il crée cinq ListElements avec des exemples d'alarmes.
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 }
Saisir de nouvelles alarmes
En bas de l'écran de démarrage, vous pouvez voir un bouton pour 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 pour les nouvelles alarmes :

Tous les champs sont saisis à l'aide des types QML Tumbler. Si vous appuyez sur OK, les valeurs sélectionnées dans les Tumblers sont écrites sur 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 particulière, vous pouvez la modifier dans l'écran de détail.

Le fait de cliquer sur une alarme fait passer root.checked à true, ce qui rend visibles les champs de l'écran détaillé.
visible: root.checkedSi vous souhaitez que l'alarme se déclenche également les autres jours, cochez alarmRepeat. Le Repeater affichera un RoundButton vérifiable 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, cela se reflétera ensuite dans 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 émis, l'alarme ListElement en cours 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, ni de stocker les alarmes dans un format ou une base de données quelconque. Mettez-vous au défi d'ajouter ces fonctionnalités au projet. Le stockage des données pourrait se faire au format JSON.
Fichiers sources
Voir aussi Support JSON dans Qt, Tous les exemples Qt, et Qt Quick Exemples et tutoriels.
© 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.