Qt State Machine QML-Handbuch
Qt State Machine QML-APIs stellen Typen zum Erstellen und Ausführen von Zustandsgraphen in QML bereit. Dies ähnelt dem C++-Framework für Zustandsmaschinen, das auf Harels „Statecharts: A visual formalism for complex systems“ basiert, welches auch die Grundlage für UML-Zustandsdiagramme bildet. Wie sein „ C++ counterpart “ bietet das Framework eine API und ein Ausführungsmodell auf Basis von State Chart XML (SCXML), um die Elemente und die Semantik von Zustandsdiagrammen in QML-Anwendungen einzubetten.
Für Benutzeroberflächen mit mehreren visuellen Zuständen, die unabhängig vom logischen Zustand der Anwendung sind, sollten Sie die Verwendung von QML-Zuständen und -Übergängen in Betracht ziehen.
Eine vollständige Liste der vom Framework bereitgestellten QML-Typen zum Erstellen ereignisgesteuerter Zustandsmaschinen finden Sie unter: Qt State Machine QML Types
Verwendung von QtQuick- und QtQml.StateMachine-Importen
Warnung: Wenn Sie versuchen, sowohl ` QtQuick ` als auch `QtQml.StateMachine ` in einer einzigen QML-Datei zu importieren, stellen Sie sicher, dass Sie `QtQml.StateMachine ` zuletzt importieren. Auf diese Weise wird der Typ `State` vom Declarative State Machine Framework bereitgestellt und nicht von ` QtQuick`:
import QtQuick
import QtQml.StateMachine
StateMachine {
State {
// okay, is of type QtQml.StateMachine.State
}
}Alternativ können Sie QtQml.StateMachine in einen separaten Namespace importieren, um jegliche Mehrdeutigkeit mit dem „State“-Element von QtQuick zu vermeiden:
import QtQuick
import QtQml.StateMachine as DSM
DSM.StateMachine {
DSM.State {
// ...
}
}Eine einfache Zustandsmaschine
Um die Kernfunktionalität der State-Machine-API zu veranschaulichen, betrachten wir ein Beispiel: Eine Zustandsmaschine mit drei Zuständen: „ s1 “, „ s2 “ und „ s3 “. Die Zustandsmaschine wird über eine einzige Schaltfläche gesteuert; wenn die Schaltfläche angeklickt wird, wechselt die Maschine in einen anderen Zustand. Zu Beginn befindet sich die Zustandsmaschine im Zustand „ s1 “. Im Folgenden finden Sie ein Zustandsdiagramm, das die verschiedenen Zustände in unserem Beispiel veranschaulicht.

Der folgende Codeausschnitt zeigt den Code, der zum Erstellen einer solchen Zustandsmaschine erforderlich ist.
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")
}
}Die Zustandsmaschine läuft asynchron und wird so Teil der Ereignisschleife Ihrer Anwendung.
Zustandsmaschinen, die beendet werden
Die im vorherigen Abschnitt definierte Zustandsmaschine endet nie. Damit eine Zustandsmaschine beendet werden kann, muss sie über einen Endzustand auf oberster Ebene (Objekt vom Typ „FinalState “) verfügen. Wenn die Zustandsmaschine in den Endzustand auf oberster Ebene wechselt, sendet sie das Signal „ finished “ aus und stoppt.
Um einen Endzustand in den Graphen einzufügen, müssen Sie lediglich ein „ FinalState “-Objekt erstellen und es als Ziel eines oder mehrerer Übergänge verwenden.
Gemeinsame Nutzung von Übergängen
Angenommen, wir möchten, dass der Benutzer die Anwendung jederzeit durch Klicken auf eine „Quit“-Schaltfläche beenden kann. Um dies zu erreichen, müssen wir einen Endzustand erstellen und ihn zum Ziel eines Übergangs machen, der mit dem Signal `clicked() ` der „Quit“-Schaltfläche verknüpft ist. Wir könnten für jeden Zustand einen Übergang hinzufügen; dies erscheint jedoch redundant, und man müsste zudem daran denken, einen solchen Übergang von jedem neuen Zustand aus hinzuzufügen, der in Zukunft hinzugefügt wird.
Wir können dasselbe Verhalten (nämlich, dass ein Klick auf die Schaltfläche „Beenden“ die Zustandsmaschine beendet, unabhängig davon, in welchem Zustand sich die Zustandsmaschine gerade befindet) erreichen, indem wir die Zustände „ s1 “, „ s2 “ und „ s3 “ gruppieren. Dies geschieht, indem wir einen neuen Zustand auf oberster Ebene erstellen und die drei ursprünglichen Zustände zu untergeordneten Zuständen des neuen Zustands machen. Das folgende Diagramm zeigt die neue Zustandsmaschine.

Die drei ursprünglichen Zustände wurden in „ s11 “, „ s12 “ und „ s13 “ umbenannt, um zu verdeutlichen, dass sie nun Kinder des neuen Zustands der obersten Ebene „ s1 “ sind. Untergeordnete Zustände erben implizit die Übergänge ihres übergeordneten Zustands. Das bedeutet, dass es nun ausreicht, einen einzigen Übergang von „ s1 “ zum Endzustand „ s2 “ hinzuzufügen. Neue Zustände, die zu „ s1 “ hinzugefügt werden, erben diesen Übergang automatisch.
Um Zustände zu gruppieren, muss lediglich beim Erstellen des Zustands der richtige übergeordnete Zustand angegeben werden. Außerdem muss festgelegt werden, welcher der untergeordneten Zustände der Anfangszustand ist (der untergeordnete Zustand, in den die Zustandsmaschine wechseln soll, wenn der übergeordnete Zustand das Ziel eines Übergangs ist).
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()
}In diesem Fall soll die Anwendung beendet werden, wenn die Zustandsmaschine fertig ist; daher wird das Signal `finished() ` der Maschine mit dem Slot ` quit() ` der Anwendung verbunden.
Ein untergeordneter Zustand kann einen geerbten Übergang überschreiben. Der folgende Code fügt beispielsweise einen Übergang hinzu, der bewirkt, dass die Schaltfläche „Quit“ ignoriert wird, wenn sich die Zustandsmaschine im Zustand „ s12 “ befindet.
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")
}Ein Übergang kann jeden beliebigen Zustand als Ziel haben, unabhängig davon, wo sich der Zielzustand in der Zustandshierarchie befindet.
Verwendung von Verlaufszuständen
Stellen wir uns vor, wir wollten dem im vorherigen Abschnitt besprochenen Beispiel einen „Unterbrechungsmechanismus“ hinzufügen; Der Benutzer soll durch Klicken auf eine Schaltfläche die Zustandsmaschine dazu bringen können, eine nicht damit zusammenhängende Aufgabe auszuführen, woraufhin die Zustandsmaschine ihre vorherige Tätigkeit fortsetzen soll (d. h. in den alten Zustand zurückkehren, der in diesem Fall einer der drei Zustände ist).
Ein solches Verhalten lässt sich mithilfe von Verlaufszuständen leicht modellieren. Ein Verlaufszustand (HistoryState -Objekt) ist ein Pseudozustand, der den untergeordneten Zustand repräsentiert, in dem sich der übergeordnete Zustand vor seinem letzten Verlassen befand.
Ein Verlaufszustand wird als Kind des Zustands erstellt, für den wir den aktuellen Kindzustand aufzeichnen möchten; wenn die Zustandsmaschine zur Laufzeit das Vorhandensein eines solchen Zustands erkennt, zeichnet sie automatisch den aktuellen (tatsächlichen) Kindzustand auf, sobald der übergeordnete Zustand beendet wird. Ein Übergang zum Verlaufszustand ist faktisch ein Übergang zu dem untergeordneten Zustand, den die Zustandsmaschine zuvor gespeichert hatte; die Zustandsmaschine „leitet“ den Übergang automatisch an den tatsächlichen untergeordneten Zustand weiter.
Das folgende Diagramm zeigt die Zustandsmaschine nach dem Hinzufügen des Interrupt-Mechanismus.

Der folgende Code zeigt, wie dies implementiert werden kann; in diesem Beispiel zeigen wir einfach ein Meldungsfeld an, wenn „ s3 “ aufgerufen wird, und kehren dann sofort über den Verlaufszustand zum vorherigen untergeordneten Zustand „ s1 “ zurück.
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()
}Verwendung paralleler Zustände
Angenommen, Sie möchten eine Reihe sich gegenseitig ausschließender Eigenschaften eines Autos in einer einzigen Zustandsmaschine modellieren. Nehmen wir an, die Eigenschaften, die uns interessieren, sind „Sauber vs. Schmutzig“ und „In Bewegung vs. Nicht in Bewegung“. Es wären vier sich gegenseitig ausschließende Zustände und acht Übergänge erforderlich, um die Zustände darzustellen und frei zwischen allen möglichen Kombinationen zu wechseln, wie in der folgenden Zustandsübersicht gezeigt.

Würden wir eine dritte Eigenschaft hinzufügen (z. B. „Rot“ vs. „Blau“), würde sich die Gesamtzahl der Zustände auf acht verdoppeln; und würden wir eine vierte Eigenschaft hinzufügen (z. B. „Kupé“ vs. „Cabrio“), würde sich die Gesamtzahl der Zustände erneut auf 16 verdoppeln.
Dieser exponentielle Anstieg lässt sich durch die Verwendung paralleler Zustände reduzieren, was ein lineares Wachstum der Anzahl von Zuständen und Übergängen ermöglicht, wenn wir weitere Eigenschaften hinzufügen. Darüber hinaus können Zustände zum parallelen Zustand hinzugefügt oder daraus entfernt werden, ohne dass dies Auswirkungen auf die zugehörigen Geschwisterzustände hat. Das folgende Zustandsdiagramm zeigt die verschiedenen parallelen Zustände für das Auto-Beispiel.

Um eine parallele Zustandsgruppe zu erstellen, setzen Sie `childMode` auf ` QState.ParallelStates`.
Beim Eintritt in eine parallele Zustandsgruppe werden alle zugehörigen untergeordneten Zustände gleichzeitig betreten. Übergänge innerhalb der einzelnen untergeordneten Zustände verlaufen normal. Jeder der untergeordneten Zustände kann jedoch einen Übergang ausführen, der den übergeordneten Zustand verlässt. In diesem Fall werden der übergeordnete Zustand und alle seine untergeordneten Zustände verlassen.
Die Parallelität im State-Machine-Framework folgt einer verschachtelten Semantik. Alle parallelen Operationen werden in einem einzigen, atomaren Schritt der Ereignisverarbeitung ausgeführt, sodass kein Ereignis die parallelen Operationen unterbrechen kann. Ereignisse werden jedoch weiterhin sequenziell verarbeitet, da die Maschine selbst single-threaded ist. Betrachten wir beispielsweise die Situation, in der es zwei Übergänge gibt, die aus derselben parallelen Zustandsgruppe herausführen, und deren Bedingungen gleichzeitig wahr werden. In diesem Fall hat das Ereignis, das von den beiden zuletzt verarbeitet wird, keine Auswirkung.
Verlassen eines zusammengesetzten Zustands
Ein untergeordneter Zustand kann ein Endzustand sein (ein „ FinalState “-Objekt); wenn ein untergeordneter Endzustand erreicht wird, sendet der übergeordnete Zustand das Signal „ State::finished “ aus. Das folgende Diagramm zeigt einen zusammengesetzten Zustand „ s1 “, der vor dem Eintritt in einen Endzustand einige Verarbeitungsschritte durchführt:

Wenn der Endzustand von „ s1 “ erreicht wird, sendet „ s1 “ automatisch das Signal „ finished “ aus. Wir verwenden einen Signalübergang, um zu bewirken, dass dieses Ereignis einen Zustandswechsel auslöst:
State {
id: s1
SignalTransition {
targetState: s2
signal: s1.finished
}
}Die Verwendung von Endzuständen in zusammengesetzten Zuständen ist nützlich, wenn Sie die internen Details eines zusammengesetzten Zustands verbergen möchten. Die Außenwelt sollte in den Zustand eintreten und eine Benachrichtigung erhalten können, wenn der Zustand seine Arbeit abgeschlossen hat, ohne die internen Details kennen zu müssen. Dies ist ein sehr leistungsfähiger Abstraktions- und Kapselungsmechanismus beim Aufbau komplexer (tief verschachtelter) Zustandsmaschinen. (Im obigen Beispiel könnten Sie natürlich einen Übergang direkt aus dem Zustand „ done “ von „ s1 “ erstellen, anstatt sich auf das Signal „finished()“ von „ s1 “ zu verlassen – dies hätte jedoch zur Folge, dass Implementierungsdetails von „ s1 “ offengelegt würden und man davon abhängig wäre.)
Bei parallelen Zustandsgruppen wird das Signal „ State::finished “ ausgesendet, sobald alle untergeordneten Zustände in ihre Endzustände übergegangen sind.
Zielfreie Übergänge
Ein Übergang muss keinen Zielzustand haben. Ein Übergang ohne Ziel kann auf dieselbe Weise wie jeder andere Übergang ausgelöst werden; der Unterschied besteht darin, dass er keine Zustandsänderungen bewirkt. Dies ermöglicht es Ihnen, auf ein Signal oder Ereignis zu reagieren, wenn sich Ihre Maschine in einem bestimmten Zustand befindet, ohne diesen Zustand verlassen zu müssen. Zum Beispiel:
Button {
id: button
text: "button"
StateMachine {
id: stateMachine
initialState: s1
running: true
State {
id: s1
SignalTransition {
signal: button.clicked
onTriggered: console.log("button pressed")
}
}
}
}Die Meldung „Taste gedrückt“ wird bei jedem Klick auf die Schaltfläche angezeigt, doch die Zustandsmaschine verbleibt in ihrem aktuellen Zustand (s1). Wäre der Zielzustand explizit auf s1 festgelegt, würde s1 jedes Mal verlassen und erneut betreten werden (die Signale „ QAbstractState::entered “ und „ QAbstractState::exited “ würden ausgegeben).
Weiterführende Informationen
© 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.