Machine à café
Une application « Qt Quick » dotée d'une interface utilisateur personnalisée basée sur les états.

Cette application illustre une approche multiplateforme, adaptée à différentes tailles d’écran et responsive, pour la mise en œuvre d’une interface utilisateur classique destinée à la commande de produits.
Exécution de l'exemple
Vous pouvez exécuter l'exemple à partir de :
- Qt Creator
Ouvrez le mode « Welcome » et sélectionnez l’exemple à l’adresse 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.
Présentation de l’interface utilisateur
Cette section vous guide à travers le parcours utilisateur type de l’application.
Sélection d’une recette

L'application « Machine à café » vous permet de choisir un type de café et vous indique le nombre de cafés de ce type qu'il vous reste à commander.
Personnalisation du café

Une fois la recette sélectionnée, l'application affiche la composition du mélange de café :
- café infusé
- lait chaud
- mousse de lait
- sucre
Ces proportions peuvent être modifiées à l'aide de curseurs.
Affichage de l'état de la préparation

Une fois que l'utilisateur a confirmé que la tasse est bien placée dans la machine, la préparation commence.
Une fois la préparation lancée, l'application affiche une animation illustrant le processus de préparation.
Le café est prêt
Une fois le processus de préparation terminé, l’application affiche pendant quelques secondes une tasse de café contenant le mélange souhaité, avant de revenir à la page d’accueil.
Fonctionnement
Nous expliquons ici comment ces fonctionnalités sont mises en œuvre.
Adaptation à la taille et à l'orientation de l'écran
L’objet racine de la fenêtre de l’application dispose de valeurs initiales pour la hauteur et la largeur, qui seront utilisées comme dimensions de la fenêtre sur les plateformes de bureau. L’ QWindow::AutomaticVisibility par défaut garantit que la fenêtre s’affichera en mode maximisé ou en plein écran sur les plateformes qui l’exigent, telles que les plateformes mobiles.
ApplicationWindow {
visible: true
width: 1000
height: 600
title: qsTr("Coffee")
background: Rectangle {
color: Colors.currentTheme.background
}À partir de là, les objets enfants de l’ ApplicationWindow récupèrent la taille de l’ ApplicationWindow e afin de déterminer leurs propres tailles en conséquence.
L’application utilise l’ GridLayout dans tous les composants de la page d’application afin de s’adapter facilement aux changements d’orientation de l’écran.

{L'écran passe en mode paysage lorsque l'orientation de l'appareil Android est en mode paysage}

{Le mode paysage est l’orientation par défaut sur le bureau Windows}
Remarque : sur la version bureau, la largeur doit être supérieure à la hauteur.
Navigation entre les pages de l’application
L'application utilise le type QML « StackView » pour afficher les différentes pages de l'application. L'StackView, qui gère les différents composants de page, est implémentée dans « ApplicationFlowForm.ui.qml ».
La navigation d’une page à l’autre déclenche un changement d’état dans ` ApplicationFlow.qml `, où les modifications de propriétés nécessaires sont gérées par le type QML ` PropertyChanges ` :
states: [
State {
name: "Home"
PropertyChanges {
target: toolbar
backButton.opacity: 0
backButton.enabled: false
themeButton.opacity: 0
themeButton.enabled: false
logo.sourceSize.width: 70
logo.sourceSize.height: 50
}Les animations qui se produisent lors de ces changements d’état sont implémentées à l’aide de Transition et de PropertyAnimation à l’intérieur du composant StackView dans ApplicationFLowForm.ui.qml.
Implémentation de la page d’accueil
L’application démarre en affichant la page Home à l’utilisateur en tant qu’élément initial dans le StackView:
StackView {
id: stack
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: parent.height / 20
pushEnter: Transition {
PropertyAnimation {
property: "x"
from: stack.width
to: 0
duration: 400
}
}Le composant Home est structuré en plaçant le composant Item comme objet racine avec une machine à états et les alias de propriétés appropriés, suivi de GridLayout. Ce même type de structuration sera utilisé dans tous les composants de pages de l’application.
La page Home affiche l’image d’une tasse à café surmontée du logo Qt, le titre « Coffee Machine », une légende contenant une brève description accrocheuse de ce à quoi l’utilisateur peut s’attendre, ainsi qu’un bouton « getStartedButton ».
L’utilisateur peut passer à l’étape suivante en appuyant sur le bouton « getStartedButton » ; la fonction du bouton « onClicked » est implémentée dans ` ApplicationFlow.qml` :
getStartedbutton.onClicked: {
applicationFlow.state = "Coffee-selection"
applicationFlow.stack.pushItem(applicationFlow.choosingCoffee, {appFlow: applicationFlow})
}Cela déclenchera le passage à l’état « Coffee-selection » dans ApplicationFlow.qml et affichera le composant « choosingCoffee » par-dessus le composant Home dans StackView.
Mise en œuvre de la sélection du café
La page « Sélection du café » ChoosingCoffee.qml s’affiche ; l’utilisateur y voit 4 options de café différentes parmi lesquelles choisir. Ces options sont affichées sous forme de composants CoffeeCards situés à l’intérieur du composant GridLayout sur ChoosingCoffee.qml:
GridLayout {
id: cards
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
rowSpacing: 20
columnSpacing: 20
CoffeeCard {
id: cappuccino
coffeeName: "Cappuccino"
ingredients: "Milk, Espresso, Foam"
time: 2
}
CoffeeCard {
id: latte
coffeeName: "Latte"
ingredients: "Coffee, Foam"
time: 3
}
CoffeeCard {
id: espresso
coffeeName: "Espresso"
ingredients: "Milk, Espresso"
time: 2
}
CoffeeCard {
id: macchiato
coffeeName: "Macchiato"
ingredients: "Milk foam, Espresso"
time: 4
}
}L’implémentation de la carte « CoffeeCard » se trouve à l’adresse CoffeeCard.qml.
Ces cartes peuvent s’afficher sous forme de grille ou de ligne, en fonction des propriétés de largeur et de hauteur d’écran disponibles que l’objet racine « ApplicationWindow » suit et transmet à « ChoosingCoffee.qml », puis à « GridLayout » via la machine à états.
CoffeeCards varient en termes de noms, de temps de préparation, d’ingrédients et du nombre de tasses disponibles à ce moment-là.
Sur cette page, l’utilisateur peut également modifier le thème de l’application pour la première fois en appuyant sur le petit bouton en forme de soleil situé dans le coin supérieur droit de l’écran. Appuyer sur le bouton de thème appelle la fonction ` themeButton ` dans ` ApplicationFlow.qml` :
function themeButton() {
if (Colors.currentTheme == Colors.dark) {
Colors.currentTheme = Colors.light
} else {
Colors.currentTheme = Colors.dark
}
}Cette fonction modifie la propriété ` currentTheme ` de ` Colors.qml ` et les couleurs de l’application sont automatiquement mises à jour via les liaisons de propriétés. Toutes les couleurs utilisées dans l’application se trouvent dans ` Colors.qml`.
Si le thème passe en mode clair, l’icône de changement de thème prend la forme d’un croissant de lune.
Appuyer sur l’une des fiches de café déclenche un changement d’état au sein de ` AbstractButton ` dans ` CoffeeCard.qml `, ce qui déclenche à son tour ` NumberAnimation ` via ` Transition` :
AbstractButton {
width: parent.width - 2
height: parent.height - 2
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
id: button
hoverEnabled: true
checkable: true
enabled: (root.cupsLeft != 0) ? true : false
transitions: Transition {
NumberAnimation {
properties: "scale"
duration: 50
easing.type: Easing.InOutQuad
}
}Cela réduit la taille de la carte de café sélectionnée et colore ses bords en vert, indiquant ainsi à l’utilisateur que le café a bien été sélectionné.
La propriété « button » de chaque carte de café, alias de la fonction onClicked, est liée à une fonction située dans ApplicationFlow.qml. Lorsque l’utilisateur clique sur l’une des cartes de café, la fonction dont le nom correspond à l’option de café choisie est appelée. La fonction définit l’état de ApplicationFlow.qml sur Settings, ajoute un nouveau composant à la StackView et définit les propriétés des ingrédients en conséquence.
Mise en œuvre des paramètres de café
Sur cette page, l’utilisateur peut personnaliser son café selon ses préférences en ajustant les types QML personnalisés Slider. La modification de la valeur des curseurs affectera les niveaux de liquide visibles à l’intérieur de la tasse à café ; cela est rendu possible grâce à la liaison de la hauteur des images de liquide dans Cup.qml aux valeurs des curseurs correspondants.
Les valeurs des curseurs de la page Settings sont stockées dans CoffeeConfig en vue d’une utilisation ultérieure à l’aide du type QML Synchronizer.
Synchronizer {
sourceObject: settingsForm.coffeeConfig
sourceProperty: "sugarAmount"
targetObject: settingsForm.sugarSlider
targetProperty: "value"
}Un clic sur le bouton « Démarrer » fait passer l’état de ApplicationFlow.qml à « Insérer », et l’application affiche Insert.qml.
Mise en œuvre de l’insertion de la tasse
Sur cette page, l’utilisateur est invité à placer sa tasse sur la machine avant que le processus d’infusion puisse commencer.
Appuyer sur le bouton « Continue » (Insérer la tasse) redirige l’application vers la page « Progress » (Insérer la tasse).
Mise en œuvre de la préparation du café
La page de progression affiche une tasse à café et une barre de progression qui, chacune à leur manière, indiquent en temps réel l’avancement du processus d’infusion.
Une fois remplie, la tasse de café affichera exactement la même configuration que celle sélectionnée par l’utilisateur sur la page Settings. Ceci est assuré en liant les variables de propriété Cup aux valeurs correspondantes enregistrées dans ApplicationFlow.qml.
L’animation de remplissage de la tasse de café est gérée par les moteurs d’état Transition et SmoothedAnimation.
Cup {
id: cup
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
state: "0"
}L'animation de la barre de progression est mise en œuvre à l'aide de Behavior.
Behavior on greenBar.width {
SmoothedAnimation {
easing.type: Easing.Linear
velocity: (progressForm.contentItem.width / progressForm.brewTime) * 1000
}
}Timer se chargera de mettre à jour l'état de l'application une fois l'infusion terminée.
Timer {
id: timer
interval: progressForm.brewTime
running: true
onTriggered: {
progressForm.appFlow.onFinished()
}
}Implémentation de la page « Café prêt »
La page « Prêt » affiche une tasse de café remplie selon la configuration choisie par l'utilisateur, accompagnée du texte « Votre café est prêt » et d'une icône en forme de coche.
Lorsque cette page s’affiche, une fonction Timer se lance et, une fois l’intervalle défini écoulé, l’utilisateur est redirigé vers Home.qml.
Fichiers source
Tests d’interface graphique avec Squish
Cette application est fournie avec des tests d'interface graphique Squish destinés à Qt pour Android. Les tests sont réalisés avec Squish pour Qt, écrits en Python et se trouvent dans le répertoire de test de l'application.
Lorsque vous utilisez Squish pour Qt sur Android, veillez à utiliser le hook intégré à Qt et à rediriger le port Squish via adb. Si vous obtenez l’erreur « Impossible de charger la bibliothèque » et que l’ dlopen échoue, désactivez QT_USE_TARGET_ANDROID_BUILD_DIR dans la configuration CMake, ou supprimez-le de Projects->Build Settings->CMake->Current Configuration dans Qt Creator.
Si vous ne disposez pas d’une licence Squish, vous pouvez bénéficier d’un essai gratuit.
Voir également Tous les exemples Qt.
© 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.