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 :
- Qt Creator
Ouvrez le mode « Welcome » et sélectionnez l’exemple sur Examples. Pour plus d’informations, consultez Qt Creator: Tutoriel : Compiler et exécuter.
- Qt Extension for Visual Studio Code
Exécutez la commande « Qt: Open Qt examples » à partir de Command Palette, puis sélectionnez l’exemple dans la liste. Pour plus d’informations, consultez Qt Extension for Visual Studio Code: Tutoriel : Compiler et exécuter.
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.


L'assistant de l'application « Qt Quick » crée un projet contenant les fichiers source suivants :
| Fichier source | Objectif |
|---|---|
| CMakeLists.txt | 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 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 source | Objectif |
|---|---|
qtquickcontrols2.conf | Sélectionne le style « Material » avec le thème « Dark ». |
AlarmDialog.qml | Définit la boîte de dialogue permettant d’ajouter de nouvelles alarmes. |
AlarmDelegate.qml | Définit la mise en page de l'écran principal de l'application. |
AlarmModel.qml | Définit l'ListModel utilisée pour stocker les données des alarmes. |
TumblerDelegate.qml | Définit la mise en page graphique des « Tumblers ». |
qml.qrc | Le 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=RedMain.qml
mainWindow, un type QML d’ ApplicationWindow, est l’élément racine de cette application.
ApplicationWindow {
id: window
width: 400
height: 500
visible: trueL’é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 :

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.

Cliquer sur une alarme définit « root.checked » sur « true », ce qui rend visibles les champs de l’écran de détail.
visible: root.checkedSi 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
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.