Qt State Machine Guide QML
Qt State Machine Les API QML fournissent des types permettant de créer et d’exécuter des graphes d’états en QML. Ce mécanisme est similaire au framework de machines à états en C++ basé sur l’ouvrage de Harel intitulé « Statecharts : A visual formalism for complex systems », qui sert également de base aux diagrammes d’états UML. À l'instar de son « C++ counterpart », ce framework fournit une API et un modèle d'exécution basés sur le langage State Chart XML (SCXML) afin d'intégrer les éléments et la sémantique des diagrammes d'états dans les applications QML.
Pour les interfaces utilisateur comportant plusieurs états visuels, indépendamment de l’état logique de l’application, envisagez d’utiliser les états et transitions QML.
Pour la liste complète des types QML fournis par le framework pour créer des machines à états pilotées par les événements, voir : Qt State Machine QML Types
Utilisation conjointe des importations QtQuick et QtQml.StateMachine
Avertissement : si vous tentez d’importer à la fois QtQuick et QtQml.StateMachine dans un seul fichier QML, veillez à importer QtQml.StateMachine en dernier. De cette manière, le type State est fourni par le framework de machine à états déclarative et non par QtQuick:
import QtQuick
import QtQml.StateMachine
StateMachine {
State {
// okay, is of type QtQml.StateMachine.State
}
}Vous pouvez également importer QtQml.StateMachine dans un espace de noms distinct afin d'éviter toute ambiguïté avec l'élément State de QtQuick:
import QtQuick
import QtQml.StateMachine as DSM
DSM.StateMachine {
DSM.State {
// ...
}
}Une machine à états simple
Pour illustrer les fonctionnalités essentielles de l’API State Machine, examinons un exemple : une machine à états comportant trois états, s1, s2 et s3. La machine à états est contrôlée par un seul bouton ; lorsque l’on clique sur ce bouton, la machine passe à un autre état. Au départ, la machine à états se trouve dans l’état s1. Le diagramme d’états suivant présente les différents états de notre exemple.

L'extrait de code suivant montre le code nécessaire pour créer une telle machine à états.
Button {
anchors.fill: parent
id: button
// change the button label to the active state id
text: s1.active ? "s1" : s2.active ? "s2" : "s3"
}
StateMachine {
id: stateMachine
// set the initial state
initialState: s1
// start the state machine
running: true
State {
id: s1
// create a transition from s1 to s2 when the button is clicked
SignalTransition {
targetState: s2
signal: button.clicked
}
// do something when the state enters/exits
onEntered: console.log("s1 entered")
onExited: console.log("s1 exited")
}
State {
id: s2
// create a transition from s2 to s3 when the button is clicked
SignalTransition {
targetState: s3
signal: button.clicked
}
// do something when the state enters/exits
onEntered: console.log("s2 entered")
onExited: console.log("s2 exited")
}
State {
id: s3
// create a transition from s3 to s1 when the button is clicked
SignalTransition {
targetState: s1
signal: button.clicked
}
// do something when the state enters/exits
onEntered: console.log("s3 entered")
onExited: console.log("s3 exited")
}
}La machine à états s'exécute de manière asynchrone pour s'intégrer à la boucle d'événements de votre application.
Machines à états qui se terminent
La machine à états définie dans la section précédente ne se termine jamais. Pour qu’une machine à états puisse se terminer, elle doit disposer d’un état final de niveau supérieur (objet `FinalState `). Lorsque la machine à états entre dans l’état final de niveau supérieur, elle émet le signal ` finished ` et s’arrête.
Pour introduire un état final dans le graphe, il suffit de créer un objet ` FinalState ` et de l’utiliser comme cible d’une ou plusieurs transitions.
Partage des transitions
Supposons que nous souhaitions que l’utilisateur puisse quitter l’application à tout moment en cliquant sur un bouton « Quitter ». Pour ce faire, nous devons créer un état final et en faire la cible d’une transition associée au signal clicked() du bouton « Quitter ». Nous pourrions ajouter une transition pour chaque état ; cependant, cela semble redondant et il faudrait également penser à ajouter une telle transition à partir de chaque nouvel état ajouté à l’avenir.
Nous pouvons obtenir le même comportement (à savoir que cliquer sur le bouton « Quit » quitte la machine à états, quel que soit l’état dans lequel elle se trouve) en regroupant les états s1, s2 et s3. Pour ce faire, il suffit de créer un nouvel état de niveau supérieur et de faire des trois états d’origine des enfants de ce nouvel état. Le diagramme suivant illustre la nouvelle machine à états.

Les trois états d’origine ont été renommés s11, s12 et s13 afin d’indiquer qu’ils sont désormais des enfants du nouvel état de niveau supérieur, s1. Les états enfants héritent implicitement des transitions de leur état parent. Cela signifie qu’il suffit désormais d’ajouter une seule transition depuis s1 vers l’état final, s2. Les nouveaux états ajoutés à s1 hériteront automatiquement de cette transition.
Pour regrouper des états, il suffit de spécifier le parent approprié lors de la création de l’état. Vous devez également indiquer lequel des états enfants est l’état initial (c’est-à-dire l’état enfant dans lequel la machine à états doit entrer lorsque l’état parent est la cible d’une transition).
Row {
anchors.fill: parent
spacing: 2
Button {
id: button
// change the button label to the active state id
text: s11.active ? "s11" : s12.active ? "s12" : "s13"
}
Button {
id: quitButton
text: "quit"
}
}
StateMachine {
id: stateMachine
// set the initial state
initialState: s1
// start the state machine
running: true
State {
id: s1
// set the initial state
initialState: s11
// create a transition from s1 to s2 when the button is clicked
SignalTransition {
targetState: s2
signal: quitButton.clicked
}
// do something when the state enters/exits
onEntered: console.log("s1 entered")
onExited: console.log("s1 exited")
State {
id: s11
// create a transition from s11 to s12 when the button is clicked
SignalTransition {
targetState: s12
signal: button.clicked
}
// do something when the state enters/exits
onEntered: console.log("s11 entered")
onExited: console.log("s11 exited")
}
State {
id: s12
// create a transition from s12 to s13 when the button is clicked
SignalTransition {
targetState: s13
signal: button.clicked
}
// do something when the state enters/exits
onEntered: console.log("s12 entered")
onExited: console.log("s12 exited")
}
State {
id: s13
// create a transition from s13 to s11 when the button is clicked
SignalTransition {
targetState: s11
signal: button.clicked
}
// do something when the state enters/exits
onEntered: console.log("s13 entered")
onExited: console.log("s13 exited")
}
}
FinalState {
id: s2
onEntered: console.log("s2 entered")
onExited: console.log("s2 exited")
}
onFinished: Qt.quit()
}Dans ce cas, nous souhaitons que l’application se ferme lorsque la machine à états a terminé, donc le signal `finished() ` de la machine est connecté au slot ` quit() ` de l’application.
Un état enfant peut redéfinir une transition héritée. Par exemple, le code suivant ajoute une transition qui a pour effet d’ignorer le bouton « Quit » lorsque la machine à états se trouve dans l’état « s12 ».
State {
id: s12
// create a transition from s12 to s13 when the button is clicked
SignalTransition {
targetState: s13
signal: button.clicked
}
// ignore Quit button when we are in state 12
SignalTransition {
targetState: s12
signal: quitButton.clicked
}
// do something when the state enters/exits
onEntered: console.log("s12 entered")
onExited: console.log("s12 exited")
}Une transition peut avoir n'importe quel état comme cible, quelle que soit la position de cet état dans la hiérarchie des états.
Utilisation des états d’historique
Imaginons que nous souhaitions ajouter un mécanisme d’« interruption » à l’exemple abordé dans la section précédente ; l’utilisateur devrait pouvoir cliquer sur un bouton pour que la machine à états effectue une tâche sans rapport avec l’action en cours, après quoi la machine à états devrait reprendre ce qu’elle faisait auparavant (c’est-à-dire revenir à l’ancien état, qui est l’un des trois états dans ce cas).
Un tel comportement peut facilement être modélisé à l’aide d’états d’historique. Un état d’historique (objet `HistoryState `) est un pseudo-état qui représente l’état enfant dans lequel se trouvait l’état parent avant sa dernière sortie.
Un état d’historique est créé en tant qu’enfant de l’état pour lequel on souhaite enregistrer l’état enfant actuel ; lorsque la machine à états détecte la présence d’un tel état lors de l’exécution, elle enregistre automatiquement l’état enfant actuel (réel) au moment où l’état parent se termine. Une transition vers l’état d’historique est en fait une transition vers l’état enfant que la machine à états avait précédemment enregistré ; la machine à états « transmet » automatiquement la transition vers l’état enfant réel.
Le diagramme suivant illustre la machine à états après l’ajout du mécanisme d’interruption.

Le code suivant montre comment cela peut être implémenté ; dans cet exemple, nous affichons simplement une boîte de message lorsque l’ s3 est activé, puis nous revenons immédiatement à l’état enfant précédent de s1 via l’état d’historique.
Row {
anchors.fill: parent
spacing: 2
Button {
id: button
// change the button label to the active state id
text: s11.active ? "s11" : s12.active ? "s12" : s13.active ? "s13" : "s3"
}
Button {
id: interruptButton
text: s1.active ? "Interrupt" : "Resume"
}
Button {
id: quitButton
text: "quit"
}
}
StateMachine {
id: stateMachine
// set the initial state
initialState: s1
// start the state machine
running: true
State {
id: s1
// set the initial state
initialState: s11
// create a transition from s1 to s2 when the button is clicked
SignalTransition {
targetState: s2
signal: quitButton.clicked
}
// do something when the state enters/exits
onEntered: console.log("s1 entered")
onExited: console.log("s1 exited")
State {
id: s11
// create a transition from s1 to s2 when the button is clicked
SignalTransition {
targetState: s12
signal: button.clicked
}
// do something when the state enters/exits
onEntered: console.log("s11 entered")
onExited: console.log("s11 exited")
}
State {
id: s12
// create a transition from s2 to s3 when the button is clicked
SignalTransition {
targetState: s13
signal: button.clicked
}
// do something when the state enters/exits
onEntered: console.log("s12 entered")
onExited: console.log("s12 exited")
}
State {
id: s13
// create a transition from s3 to s1 when the button is clicked
SignalTransition {
targetState: s1
signal: button.clicked
}
// do something when the state enters/exits
onEntered: console.log("s13 entered")
onExited: console.log("s13 exited")
}
// create a transition from s1 to s3 when the button is clicked
SignalTransition {
targetState: s3
signal: interruptButton.clicked
}
HistoryState {
id: s1h
}
}
FinalState {
id: s2
onEntered: console.log("s2 entered")
onExited: console.log("s2 exited")
}
State {
id: s3
SignalTransition {
targetState: s1h
signal: interruptButton.clicked
}
// do something when the state enters/exits
onEntered: console.log("s3 entered")
onExited: console.log("s3 exited")
}
onFinished: Qt.quit()
}Utilisation d’états parallèles
Supposons que vous souhaitiez modéliser un ensemble de propriétés mutuellement exclusives d’une voiture dans une seule machine à états. Disons que les propriétés qui nous intéressent sont « Propre » vs « Sale » et « En mouvement » vs « À l’arrêt ». Il faudrait quatre états mutuellement exclusifs et huit transitions pour représenter les états et passer librement d’une combinaison possible à l’autre, comme le montre le diagramme d’états suivant.

Si nous ajoutions une troisième propriété (par exemple, « Rouge » ou « Bleu »), le nombre total d’états doublerait, pour atteindre huit ; et si nous ajoutions une quatrième propriété (par exemple, « Coupé » ou « Cabriolet »), le nombre total d’états doublerait à nouveau, pour atteindre seize.
Cette augmentation exponentielle peut être réduite grâce à l’utilisation d’états parallèles, ce qui permet une croissance linéaire du nombre d’états et de transitions à mesure que l’on ajoute des propriétés. De plus, des états peuvent être ajoutés ou supprimés d’un état parallèle sans affecter aucun de ses états frères. Le diagramme d’états suivant illustre les différents états parallèles pour l’exemple de la voiture.

Pour créer un groupe d'états parallèles, définissez `childMode` sur ` QState.ParallelStates`.
Lorsqu’un groupe d’états parallèles est activé, tous ses états enfants le sont simultanément. Les transitions au sein de chaque état enfant s’effectuent normalement. Cependant, n’importe quel état enfant peut effectuer une transition qui quitte l’état parent. Lorsque cela se produit, l’état parent et tous ses états enfants sont quittés.
Le parallélisme dans le framework State Machine suit une sémantique entrelacée. Toutes les opérations parallèles seront exécutées en une seule étape atomique du traitement des événements ; ainsi, aucun événement ne peut interrompre les opérations parallèles. Cependant, les événements continueront d’être traités de manière séquentielle, car la machine elle-même est monothread. Prenons par exemple le cas où deux transitions permettent de quitter le même groupe d’états parallèles et où leurs conditions deviennent vraies simultanément. Dans ce cas, l’événement traité en dernier parmi les deux n’aura aucun effet.
Sortie d’un état composite
Un état enfant peut être final (un objet ` FinalState `) ; lorsqu’un état enfant final est atteint, l’état parent émet le signal ` State::finished `. Le diagramme suivant illustre un état composite s1 qui effectue un traitement avant d’entrer dans un état final :

Lorsque l’on entre dans l’état final de ` s1 `, ` s1 ` émet automatiquement ` finished`. Nous utilisons une transition de signal pour que cet événement déclenche un changement d’état :
State {
id: s1
SignalTransition {
targetState: s2
signal: s1.finished
}
}L’utilisation d’états finaux dans des états composites est utile lorsque vous souhaitez masquer les détails internes d’un état composite. Le monde extérieur doit pouvoir entrer dans l’état et recevoir une notification lorsque celui-ci a terminé son travail, sans avoir besoin de connaître les détails internes. Il s’agit d’un mécanisme d’abstraction et d’encapsulation très puissant lors de la construction de machines à états complexes (profondément imbriquées). (Dans l’exemple ci-dessus, vous pourriez bien sûr créer une transition directement à partir de l’état « done » de ` s1 ` plutôt que de vous appuyer sur le signal `finished()` de ` s1 `, mais cela aurait pour conséquence d’exposer les détails d’implémentation de ` s1 ` et d’en dépendre).
Pour les groupes d’états parallèles, le signal State::finished est émis lorsque tous les états enfants sont entrés dans leur état final.
Transitions sans cible
Une transition n’a pas nécessairement besoin d’un état cible. Une transition sans cible peut être déclenchée de la même manière que n’importe quelle autre transition ; la différence est qu’elle n’entraîne aucun changement d’état. Cela vous permet de réagir à un signal ou à un événement lorsque votre machine se trouve dans un certain état, sans avoir à quitter cet état. Par exemple :
Button {
id: button
text: "button"
StateMachine {
id: stateMachine
initialState: s1
running: true
State {
id: s1
SignalTransition {
signal: button.clicked
onTriggered: console.log("button pressed")
}
}
}
}Le message « bouton enfoncé » s'affichera à chaque fois que l'on cliquera sur le bouton, mais la machine à états restera dans son état actuel (s1). Si l'état cible était explicitement défini sur s1, s1 serait quitté puis réintégré à chaque fois (les signaux « QAbstractState::entered » et « QAbstractState::exited » seraient émis).
Informations connexes
© 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.