diff options
Diffstat (limited to 'src/statemachine/doc')
45 files changed, 2135 insertions, 0 deletions
diff --git a/src/statemachine/doc/images/animations-architecture.png b/src/statemachine/doc/images/animations-architecture.png Binary files differnew file mode 100644 index 0000000..9b581af --- /dev/null +++ b/src/statemachine/doc/images/animations-architecture.png diff --git a/src/statemachine/doc/images/move-blocks-chart.png b/src/statemachine/doc/images/move-blocks-chart.png Binary files differnew file mode 100644 index 0000000..fd0c165 --- /dev/null +++ b/src/statemachine/doc/images/move-blocks-chart.png diff --git a/src/statemachine/doc/images/rogue-statechart.png b/src/statemachine/doc/images/rogue-statechart.png Binary files differnew file mode 100644 index 0000000..c5f4048 --- /dev/null +++ b/src/statemachine/doc/images/rogue-statechart.png diff --git a/src/statemachine/doc/images/statemachine-button-history.png b/src/statemachine/doc/images/statemachine-button-history.png Binary files differnew file mode 100644 index 0000000..7f51cae --- /dev/null +++ b/src/statemachine/doc/images/statemachine-button-history.png diff --git a/src/statemachine/doc/images/statemachine-button-nested.png b/src/statemachine/doc/images/statemachine-button-nested.png Binary files differnew file mode 100644 index 0000000..762ac14 --- /dev/null +++ b/src/statemachine/doc/images/statemachine-button-nested.png diff --git a/src/statemachine/doc/images/statemachine-button.png b/src/statemachine/doc/images/statemachine-button.png Binary files differnew file mode 100644 index 0000000..10102bd --- /dev/null +++ b/src/statemachine/doc/images/statemachine-button.png diff --git a/src/statemachine/doc/images/statemachine-customevents.png b/src/statemachine/doc/images/statemachine-customevents.png Binary files differnew file mode 100644 index 0000000..62a4222 --- /dev/null +++ b/src/statemachine/doc/images/statemachine-customevents.png diff --git a/src/statemachine/doc/images/statemachine-customevents2.png b/src/statemachine/doc/images/statemachine-customevents2.png Binary files differnew file mode 100644 index 0000000..57b37ef --- /dev/null +++ b/src/statemachine/doc/images/statemachine-customevents2.png diff --git a/src/statemachine/doc/images/statemachine-examples.png b/src/statemachine/doc/images/statemachine-examples.png Binary files differnew file mode 100644 index 0000000..b2ec66e --- /dev/null +++ b/src/statemachine/doc/images/statemachine-examples.png diff --git a/src/statemachine/doc/images/statemachine-finished.png b/src/statemachine/doc/images/statemachine-finished.png Binary files differnew file mode 100644 index 0000000..0ac081d --- /dev/null +++ b/src/statemachine/doc/images/statemachine-finished.png diff --git a/src/statemachine/doc/images/statemachine-nonparallel.png b/src/statemachine/doc/images/statemachine-nonparallel.png Binary files differnew file mode 100644 index 0000000..f9850a7 --- /dev/null +++ b/src/statemachine/doc/images/statemachine-nonparallel.png diff --git a/src/statemachine/doc/images/statemachine-parallel.png b/src/statemachine/doc/images/statemachine-parallel.png Binary files differnew file mode 100644 index 0000000..a65c297 --- /dev/null +++ b/src/statemachine/doc/images/statemachine-parallel.png diff --git a/src/statemachine/doc/qstatemachine-qml-guide.qdoc b/src/statemachine/doc/qstatemachine-qml-guide.qdoc new file mode 100644 index 0000000..deb9d94 --- /dev/null +++ b/src/statemachine/doc/qstatemachine-qml-guide.qdoc @@ -0,0 +1,293 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page qmlstatemachine-qml-guide.html + \title Qt State Machine QML Guide + \brief Overview of the Qt State Machine QML for constructing and executing state graphs. + \ingroup technology-apis + \ingroup explanation + + \tableofcontents + + Qt State Machine QML APIs provide types for creating and + executing state graphs in QML. It is similar to the C++ State Machine + framework based on Harel's + \l{Statecharts: A visual formalism for complex systems}, which + is also the basis for UML state diagrams. Like its + \l{Qt State Machine C++ Classes}{C++ counterpart}, the framework provides an + API and execution model based on \l{State Chart XML: State Machine Notation for + Control Abstraction}{State Chart XML (SCXML)} + to embed the elements and semantics of statecharts in QML applications. + + For user interfaces with multiple visual states, independent of the + application's logical state, consider using QML States and Transitions. + + For the full list of QML types provided by the framework to create event-driven + state machines, see: \l {Qt State Machine QML Types} + + \section1 Using Both QtQuick and QtQml.StateMachine Imports + + \warning If you're attempting to import both \l{QtQuick} and + \e{QtQml.StateMachine} in one single QML file, make sure to import + \e{QtQml.StateMachine} \e{last}. This way, the \e{State} type is provided + by the Declarative State Machine Framework and not by \l{QtQuick}: + + \qml + import QtQuick + import QtQml.StateMachine + + StateMachine { + State { + // okay, is of type QtQml.StateMachine.State + } + } + \endqml + + Alternatively, you can import \e{QtQml.StateMachine} into a separate + namespace to avoid any ambiguity with QtQuick's \e{State} item: + + \qml \QtMinorVersion + import QtQuick + import QtQml.StateMachine as DSM + + DSM.StateMachine { + DSM.State { + // ... + } + } + \endqml + + \section1 A Simple State Machine + + To demonstrate the core functionality of the State Machine API, let's look + at an example: A state machine with three states, \c s1, \c s2 and \c + s3. The state machine is controlled by a single Button; when the button + is clicked, the machine transitions to another state. Initially, the state + machine is in state \c s1. The following is a state chart showing the + different states in our example. + + \image statemachine-button.png + + The following snippet shows the code needed to create such a state machine. + + \snippet qml/statemachine/statemachine-button.qml 0 + + The state machine runs asynchronously to become part of your application's + event loop. + + \section1 State Machines That Finish + + The state machine defined in the previous section never finishes. In order + for a state machine to be able to finish, it needs to have a top-level \e + final state (FinalState object). When the state machine enters the top-level + final state, the machine emits the \l{State::finished}{finished} + signal and halts. + + All you need to do to introduce a final state in the graph is create a + FinalState object and use it as the target of one or more transitions. + + \section1 Sharing Transitions + + Assume we wanted the user to be able to quit the application at any time by + clicking a Quit button. In order to achieve this, we need to create a final + state and make it the target of a transition associated with the Quit + button's \e clicked() signal. We could add a transition for each state; + however, this seems redundant and one would also have to + remember to add such a transition from every new state that is added in the + future. + + We can achieve the same behavior (namely that clicking the Quit button quits + the state machine, regardless of which state the state machine is in) by + grouping states \c s1, \c s2 and \c s3. This is done by creating a new + top-level state and making the three original states children of the new + state. The following diagram shows the new state machine. + + \image statemachine-button-nested.png + + The three original states have been renamed \c s11, \c s12 and \c s13 to + reflect that they are now childrens of the new top-level state, \c s1. Child + states implicitly inherit the transitions of their parent state. This means + it is now sufficient to add a single transition from \c s1 to the final + state, \c s2. New states added to \c s1 will automatically inherit this + transition. + + All that's needed to group states is to specify the proper parent when the + state is created. You also need to specify which of the child states is the + initial one (the child state the state machine should enter when the + parent state is the target of a transition). + + \snippet qml/statemachine/statemachine-button-nested.qml 0 + + In this case we want the application to quit when the state machine is + finished, so the machine's \e finished() signal is connected to the + application's \e quit() slot. + + A child state can override an inherited transition. For example, the + following code adds a transition that effectively causes the Quit button to + be ignored when the state machine is in state, \c s12. + + \snippet qml/statemachine/statemachine-button-nested-ignore-quit.qml 0 + + A transition can have any state as its target irrespective of where the + target state is in the state hierarchy. + + \section1 Using History States + + Imagine that we wanted to add an "interrupt" mechanism to the example + discussed in the previous section; the user should be able to click a button + to have the state machine perform some non-related task, after which the + state machine should resume whatever it was doing before (i.e. return to the + old state, which is one of the three states in this case). + + Such behavior can easily be modeled using \e{history states}. A history + state (HistoryState object) is a pseudo-state that represents the child + state that the parent state was in before it exited last. + + A history state is created as a child of the state for which we wish to + record the current child state; when the state machine detects the presence + of such a state at runtime, it automatically records the current (real) + child state when the parent state exits. A transition to the history + state is in fact a transition to the child state that the state machine had + previously saved; the state machine automatically "forwards" the transition + to the real child state. + + The following diagram shows the state machine after the interrupt mechanism + has been added. + + \image statemachine-button-history.png + + The following code shows how it can be implemented; in this example we + simply display a message box when \c s3 is entered, then immediately return + to the previous child state of \c s1 via the history state. + + \snippet qml/statemachine/statemachine-button-history.qml 0 + + \section1 Using Parallel States + + Assume that you wanted to model a set of mutually exclusive properties of a + car in a single state machine. Let's say the properties we are interested in + are Clean vs Dirty, and Moving vs Not moving. It would take four mutually + exclusive states and eight transitions to represent the states and freely + move between all possible combinations as shown in the following state chart. + + \image statemachine-nonparallel.png + + If we added a third property (say, Red vs Blue), the total number of states + would double, to eight; and if we added a fourth property (say, Enclosed vs + Convertible), the total number of states would double again, to 16. + + This exponential increase can be reduced using parallel states, which enables + linear growth in the number of states and transitions as we add more + properties. Furthermore, states can be added to or removed from the parallel + state without affecting any of their sibling states. The following state + chart shows the different paralles states for the car example. + + \image statemachine-parallel.png + + To create a parallel state group, set childMode to QState.ParallelStates. + + \qml + State { + id: s1 + childMode: QState.ParallelStates + State { + id: s11 + } + State { + id: s12 + } + } + \endqml + + When a parallel state group is entered, all its child states will be + simultaneously entered. Transitions within the individual child states + operate normally. However, any of the child states may take a transition + which exits the parent state. When this happens, the parent state and all of + its child states are exited. + + The parallelism in the State Machine framework follows an interleaved + semantics. All parallel operations will be executed in a single, atomic step + of the event processing, so no event can interrupt the parallel operations. + However, events will still be processed sequentially, as the machine itself + is single threaded. For example, consider the situation where there are two + transitions that exit the same parallel state group, and their conditions + become true simultaneously. In this case, the event that is processed last + of the two will not have any effect. + + \section1 Exiting a Composite State + + A child state can be final (a FinalState object); when a final child state + is entered, the parent state emits the State::finished signal. The + following diagram shows a composite state \c s1 which does some processing + before entering a final state: + + \image statemachine-finished.png + + When \c s1 's final state is entered, \c s1 will automatically emit + \l{State::finished}{finished}. We use a signal transition to cause this event to + trigger a state change: + + \qml + State { + id: s1 + SignalTransition { + targetState: s2 + signal: s1.finished + } + } + \endqml + + Using final states in composite states is useful when you want to hide the + internal details of a composite state. The outside world should be able to + enter the state and get a notification when the state has completed its work, + without the need to know the internal details. This is a very powerful + abstraction and encapsulation mechanism when building complex (deeply nested) + state machines. (In the above example, you could of course create a transition + directly from \c s1 's \c done state rather than relying on \c s1 's + finished() signal, but with the consequence that implementation details of + \c s1 are exposed and depended on). + + For parallel state groups, the State::finished signal is emitted when \e + all the child states have entered final states. + + \section1 Targetless Transitions + + A transition need not have a target state. A transition without a target can + be triggered the same way as any other transition; the difference is that + it doesn't cause any state changes. This allows you to react to a signal or + event when your machine is in a certain state, without having to leave that + state. For example: + + \qml + Button { + id: button + text: "button" + StateMachine { + id: stateMachine + initialState: s1 + running: true + State { + id: s1 + SignalTransition { + signal: button.clicked + onTriggered: console.log("button pressed") + } + } + } + } + \endqml + + The "button pressed" message will be displayed each time the button is clicked, but the + state machine will remain in its current state (s1). If the target state + were explicitly set to s1, s1 would be exited and re-entered each + time (the QAbstractState::entered and QAbstractState::exited + signals would be emitted). + + \section1 Related Information + + \list + \li \l{Qt State Machine QML Types} + \li \l{Qt State Machine Overview} + \endlist +*/ diff --git a/src/statemachine/doc/qt6-changes.qdoc b/src/statemachine/doc/qt6-changes.qdoc new file mode 100644 index 0000000..c7f6efe --- /dev/null +++ b/src/statemachine/doc/qt6-changes.qdoc @@ -0,0 +1,43 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page qtstatemachine-changes-qt6.html + \title Changes to Qt State Machine + \ingroup changes-qt-5-to-6 + \brief Migrate Qt State Machine to Qt 6. + + Qt 6 is a result of the conscious effort to make the framework more + efficient and easy to use. + + We try to maintain binary and source compatibility for all the public + APIs in each release. But some changes were inevitable in an effort to + make Qt a better framework. + + In this topic we summarize those changes in Qt State Machine module, and provide + guidance to handle them. + + \section1 Changes + + The Qt StateMachine module is generally speaking source compatible with the Qt5 + version and users of the library should be able to continue with no or + minor changes to their project. + + \section2 QSignalTransition + + The \l [CPP] QSignalTransition::senderObject() getter and the related + Q_PROPERTY now also use const QObject*. These are now better aligned + with the setter \l [CPP] QSignalTransition::setSenderObject(const QObject*) that takes a + const QObject* as a parameter. + + \section1 Build system + + As with Qt 6 in general, the Qt State Machine module has cmake support in addition + to qmake. + + \section1 QML imports + + The QML import versioning is optional unless one has a specific + reason for not using the latest. Generally speaking the versioned imports + work from version 5.8 to 6.x, where 'x' is the current minor release. +*/ diff --git a/src/statemachine/doc/qtstatemachine-cpp-guide.qdoc b/src/statemachine/doc/qtstatemachine-cpp-guide.qdoc new file mode 100644 index 0000000..a898ba4 --- /dev/null +++ b/src/statemachine/doc/qtstatemachine-cpp-guide.qdoc @@ -0,0 +1,511 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page qtstatemachine-cpp-guide.html + \title Qt State Machine C++ Guide + \brief An overview of the State Machine framework for constructing and + executing state graphs with the C++ API. + \ingroup technology-apis + \ingroup explanation + + \tableofcontents + + The State Machine framework provides classes for creating and executing + state graphs. This page illustrates the framework's key features in C++. + + \sa {Qt State Machine Overview} + \sa {Qt State Machine QML Guide} + + \section1 C++ Classes in the State Machine Framework + + For the full list of C++ classes in the State Machine framework see + \l {Qt State Machine C++ Classes} + + \section1 A Simple State Machine + + To demonstrate the core functionality of the State Machine API, let's look + at a small example: A state machine with three states, \c s1, \c s2 and \c + s3. The state machine is controlled by a single QPushButton; when the button + is clicked, the machine transitions to another state. Initially, the state + machine is in state \c s1. The statechart for this machine is as follows: + + \image statemachine-button.png + + The following snippet shows the code needed to create such a state machine. + First, we create the state machine and states: + + \snippet statemachine/main.cpp 0 + + Then, we create the transitions by using the QState::addTransition() + function: + + \snippet statemachine/main.cpp 1 + + Next, we add the states to the machine and set the machine's initial state: + + \snippet statemachine/main.cpp 2 + + Finally, we start the state machine: + + \snippet statemachine/main.cpp 3 + + The state machine executes asynchronously, i.e. it becomes part of your + application's event loop. + + \section1 Doing Useful Work on State Entry and Exit + + The above state machine merely transitions from one state to another, it + doesn't perform any operations. The QState::assignProperty() function can be + used to have a state set a property of a QObject when the state is + entered. In the following snippet, the value that should be assigned to a + QLabel's text property is specified for each state: + + \snippet statemachine/main.cpp 4 + + When any of the states is entered, the label's text will be changed + accordingly. + + The QState::entered() signal is emitted when the state is entered, and the + QState::exited() signal is emitted when the state is exited. In the + following snippet, the button's \l {QPushButton::}{showMaximized()} slot + will be called when state \c s3 is entered, and the button's \l {QPushButton::}{showMinimized()} + slot will be called when \c s3 is exited: + + \snippet statemachine/main.cpp 5 + + Custom states can reimplement QAbstractState::onEntry() and + QAbstractState::onExit(). + + \section1 State Machines That Finish + + The state machine defined in the previous section never finishes. In order + for a state machine to be able to finish, it needs to have a top-level \e + final state (QFinalState object). When the state machine enters a top-level + final state, the machine will emit the QStateMachine::finished() signal and + halt. + + All you need to do to introduce a final state in the graph is create a + QFinalState object and use it as the target of one or more transitions. + + \section1 Sharing Transitions By Grouping States + + Assume we wanted the user to be able to quit the application at any time by + clicking a Quit button. In order to achieve this, we need to create a final + state and make it the target of a transition associated with the Quit + button's \l{QPushButton::}{clicked()} signal. We could add a transition from each of \c s1, \c + s2 and \c s3; however, this seems redundant, and one would also have to + remember to add such a transition from every new state that is added in the + future. + + We can achieve the same behavior (namely that clicking the Quit button quits + the state machine, regardless of which state the state machine is in) by + grouping states \c s1, \c s2 and \c s3. This is done by creating a new + top-level state and making the three original states children of the new + state. The following diagram shows the new state machine. + + \image statemachine-button-nested.png + + The three original states have been renamed \c s11, \c s12 and \c s13 to + reflect that they are now children of the new top-level state, \c s1. Child + states implicitly inherit the transitions of their parent state. This means + it is now sufficient to add a single transition from \c s1 to the final + state \c s2. New states added to \c s1 will also automatically inherit this + transition. + + All that's needed to group states is to specify the proper parent when the + state is created. You also need to specify which of the child states is the + initial one (i.e. which child state the state machine should enter when the + parent state is the target of a transition). + + \snippet statemachine/main2.cpp 0 + + \snippet statemachine/main2.cpp 1 + + In this case we want the application to quit when the state machine is + finished, so the machine's \l {QStateMachine::}{finished()} signal is connected to the + application's \l {QCoreApplication::}{quit()} slot. + + A child state can override an inherited transition. For example, the + following code adds a transition that effectively causes the Quit button to + be ignored when the state machine is in state \c s12. + + \snippet statemachine/main2.cpp 2 + + A transition can have any state as its target, i.e. the target state does + not have to be on the same level in the state hierarchy as the source state. + + \section1 Using History States to Save and Restore the Current State + + Imagine that we wanted to add an "interrupt" mechanism to the example + discussed in the previous section; the user should be able to click a button + to have the state machine perform some non-related task, after which the + state machine should resume whatever it was doing before (i.e. return to the + old state, which is one of \c s11, \c s12 and \c s13 in this case). + + Such behavior can easily be modeled using \e{history states}. A history + state (QHistoryState object) is a pseudo-state that represents the child + state that the parent state was in the last time the parent state was + exited. + + A history state is created as a child of the state for which we wish to + record the current child state; when the state machine detects the presence + of such a state at runtime, it automatically records the current (real) + child state when the parent state is exited. A transition to the history + state is in fact a transition to the child state that the state machine had + previously saved; the state machine automatically "forwards" the transition + to the real child state. + + The following diagram shows the state machine after the interrupt mechanism + has been added. + + \image statemachine-button-history.png + + The following code shows how it can be implemented; in this example we + simply display a message box when \c s3 is entered, then immediately return + to the previous child state of \c s1 via the history state. + + \snippet statemachine/main2.cpp 3 + + \section1 Using Parallel States to Avoid a Combinatorial Explosion of States + + Assume that you wanted to model a set of mutually exclusive properties of a + car in a single state machine. Let's say the properties we are interested in + are Clean vs Dirty, and Moving vs Not moving. It would take four mutually + exclusive states and eight transitions to be able to represent and freely + move between all possible combinations. + + \image statemachine-nonparallel.png + + If we added a third property (say, Red vs Blue), the total number of states + would double, to eight; and if we added a fourth property (say, Enclosed vs + Convertible), the total number of states would double again, to 16. + + Using parallel states, the total number of states and transitions grows + linearly as we add more properties, instead of exponentially. Furthermore, + states can be added to or removed from the parallel state without affecting + any of their sibling states. + + \image statemachine-parallel.png + + To create a parallel state group, pass QState::ParallelStates to the QState + constructor. + + \snippet statemachine/main3.cpp 0 + + When a parallel state group is entered, all its child states will be + simultaneously entered. Transitions within the individual child states + operate normally. However, any of the child states may take a transition which exits the parent + state. When this happens, the parent state and all of its child states are exited. + + The parallelism in the State Machine framework follows an interleaved semantics. All parallel + operations will be executed in a single, atomic step of the event processing, so no event can + interrupt the parallel operations. However, events will still be processed sequentially, since + the machine itself is single threaded. As an example: Consider the situation where there are two + transitions that exit the same parallel state group, and their conditions become true + simultaneously. In this case, the event that is processed last of the two will not have any + effect, since the first event will already have caused the machine to exit from the parallel + state. + + \section1 Detecting that a Composite State has Finished + + A child state can be final (a QFinalState object); when a final child state + is entered, the parent state emits the QState::finished() signal. The + following diagram shows a composite state \c s1 which does some processing + before entering a final state: + + \image statemachine-finished.png + + When \c s1 's final state is entered, \c s1 will automatically emit + \l {QState::}{finished()}. We use a signal transition to cause this event to trigger a + state change: + + \snippet statemachine/main3.cpp 1 + + Using final states in composite states is useful when you want to hide the + internal details of a composite state; i.e. the only thing the outside world + should be able to do is enter the state, and get a notification when the + state has completed its work. This is a very powerful abstraction and + encapsulation mechanism when building complex (deeply nested) state + machines. (In the above example, you could of course create a transition + directly from \c s1 's \c done state rather than relying on \c s1 's + \l {QState::}{finished()} signal, but with the consequence that implementation details of + \c s1 are exposed and depended on). + + For parallel state groups, the QState::finished() signal is emitted when \e + all the child states have entered final states. + + \section1 Targetless Transitions + + A transition need not have a target state. A transition without a target can + be triggered the same way as any other transition; the difference is that + when a targetless transition is triggered, it doesn't cause any state + changes. This allows you to react to a signal or event when your machine is + in a certain state, without having to leave that state. Example: + + \code + QStateMachine machine; + QState *s1 = new QState(&machine); + + QPushButton button; + QSignalTransition *trans = new QSignalTransition(&button, &QPushButton::clicked); + s1->addTransition(trans); + + QMessageBox msgBox; + msgBox.setText("The button was clicked; carry on."); + QObject::connect(trans, QSignalTransition::triggered, &msgBox, &QMessageBox::exec); + + machine.setInitialState(s1); + \endcode + + The message box will be displayed each time the button is clicked, but the + state machine will remain in its current state (s1). If the target state + were explicitly set to s1, however, s1 would be exited and re-entered each + time (e.g. the QAbstractState::entered() and QAbstractState::exited() + signals would be emitted). + + \section1 Events, Transitions and Guards + + A QStateMachine runs its own event loop. For signal transitions + (QSignalTransition objects), QStateMachine automatically posts a + QStateMachine::SignalEvent to itself when it intercepts the corresponding + signal; similarly, for QObject event transitions (QEventTransition objects) + a QStateMachine::WrappedEvent is posted. + + You can post your own events to the state machine using + QStateMachine::postEvent(). + + When posting a custom event to the state machine, you typically also have + one or more custom transitions that can be triggered from events of that + type. To create such a transition, you subclass QAbstractTransition and + reimplement \l [CPP] {QAbstractTransition::}{eventTest()}, where you check if an event + matches your event type (and optionally other criteria, e.g. attributes of + the event object). + + Here we define our own custom event type, \c StringEvent, for posting + strings to the state machine: + + \snippet statemachine/main4.cpp 0 + + Next, we define a transition that only triggers when the event's string + matches a particular string (a \e guarded transition): + + \snippet statemachine/main4.cpp 1 + + In the \l [CPP] {QAbstractTransition::}{eventTest()} reimplementation, we first check if the event type is the + desired one; if so, we cast the event to a \c StringEvent and perform the + string comparison. + + The following is a statechart that uses the custom event and transition: + + \image statemachine-customevents.png + + Here's what the implementation of the statechart looks like: + + \snippet statemachine/main4.cpp 2 + + Once the machine is started, we can post events to it. + + \snippet statemachine/main4.cpp 3 + + An event that is not handled by any relevant transition will be silently + consumed by the state machine. It can be useful to group states and provide + a default handling of such events; for example, as illustrated in the + following statechart: + + \image statemachine-customevents2.png + + For deeply nested statecharts, you can add such "fallback" transitions at + the level of granularity that's most appropriate. + + \section1 Using Restore Policy To Automatically Restore Properties + + In some state machines it can be useful to focus the attention on assigning properties in states, + not on restoring them when the state is no longer active. If you know that a property should + always be restored to its initial value when the machine enters a state that does not explicitly + give the property a value, you can set the global restore policy to + QStateMachine::RestoreProperties. + + \code + QStateMachine machine; + machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties); + \endcode + + When this restore policy is set, the machine will automatically restore all properties. If it + enters a state where a given property is not set, it will first search the hierarchy of ancestors + to see if the property is defined there. If it is, the property will be restored to the value + defined by the closest ancestor. If not, it will be restored to its initial value (i.e. the + value of the property before any property assignments in states were executed.) + + Take the following code: + + \snippet statemachine/main5.cpp 0 + + Lets say the property \c fooBar is 0.0 when the machine starts. When the machine is in state + \c s1, the property will be 1.0, since the state explicitly assigns this value to it. When the + machine is in state \c s2, no value is explicitly defined for the property, so it will implicitly + be restored to 0.0. + + If we are using nested states, the parent defines a value for the property which is inherited by + all descendants that do not explicitly assign a value to the property. + + \snippet statemachine/main5.cpp 2 + + Here \c s1 has two children: \c s2 and \c s3. When \c s2 is entered, the property \c fooBar + will have the value 2.0, since this is explicitly defined for the state. When the machine is in + state \c s3, no value is defined for the state, but \c s1 defines the property to be 1.0, so this + is the value that will be assigned to \c fooBar. + + \section1 Animations and States Machines + + The State Machine API connects with the \l {The Animation Framework} to allow automatically + animating properties as they are assigned in states. + + The state machine provides a special state that can play an animation. + A QState can also set properties when the state is entered or exited, and + this special animation state will interpolate between these values when given a + QPropertyAnimation. + + We can associate one or more animations to a transition between states + using a QSignalTransition or QEventTransition class. These classes + are both derived from QAbstractTransition, which defines the + convenience function \l [CPP] {QAbstractTransition::}{addAnimation()} that + enables the appending of one or more animations triggered when the + transition occurs. + + We also have the possibility to associate properties with the + states rather than setting the start and end values ourselves. + + Say we have the following code: + + \snippet statemachine/main5.cpp 3 + + Here we define two states of a user interface. In \c s1 the \c button is small, and in \c s2 + it is bigger. If we click the button to transition from \c s1 to \c s2, the geometry of the button + will be set immediately when a given state has been entered. If we want the transition to be + smooth, however, all we need to do is make a QPropertyAnimation and add this to the transition + object. + + \snippet statemachine/main5.cpp 4 + + Adding an animation for the property in question means that the property assignment will no + longer take immediate effect when the state has been entered. Instead, the animation will start + playing when the state has been entered and smoothly animate the property assignment. Since we + do not set the start value or end value of the animation, these will be set implicitly. The + start value of the animation will be the property's current value when the animation starts, and + the end value will be set based on the property assignments defined for the state. + + If the global restore policy of the state machine is set to QStateMachine::RestoreProperties, + it is possible to also add animations for the property restorations. + + \section1 Detecting That All Properties Have Been Set In A State + + When animations are used to assign properties, a state no longer defines the exact values that a + property will have when the machine is in the given state. While the animation is running, the + property can potentially have any value, depending on the animation. + + In some cases, it can be useful to be able to detect when the property has actually been assigned + the value defined by a state. + + Say we have the following code: + + \snippet statemachine/main5.cpp 5 + + When \c button is clicked, the machine will transition into state \c s2, which will set the + geometry of the button, and then pop up a message box to alert the user that the geometry has + been changed. + + In the normal case, where animations are not used, this will operate as expected. However, if + an animation for the \c geometry of \c button is set on the transition between \c s1 and \c s2, + the animation will be started when \c s2 is entered, but the \c geometry property will not + actually reach its defined value before the animation is finished running. In this case, the + message box will pop up before the geometry of the button has actually been set. + + To ensure that the message box does not pop up until the geometry actually reaches its final + value, we can use the state's \l {QState::}{propertiesAssigned()} signal. + The \l {QState::}{propertiesAssigned()} signal will be emitted when the property is assigned + its final value, whether this is done immediately or after the animation has finished playing. + + \snippet statemachine/main5.cpp 6 + + In this example, when \c button is clicked, the machine will enter \c s2. It will remain in state + \c s2 until the \c geometry property has been set to \c QRect(0, 0, 50, 50). Then it will + transition into \c s3. When \c s3 is entered, the message box will pop up. If the transition into + \c s2 has an animation for the \c geometry property, then the machine will stay in \c s2 until the + animation has finished playing. If there is no such animation, it will simply set the property and + immediately enter state \c s3. + + Either way, when the machine is in state \c s3, you are guaranteed that the property \c geometry + has been assigned the defined value. + + If the global restore policy is set to QStateMachine::RestoreProperties, the state will not emit + the \l {QState::}{propertiesAssigned()} signal until these have been executed as well. + + \section1 What Happens If A State Is Exited Before The Animation Has Finished + + If a state has property assignments, and the transition into the state has animations for the + properties, the state can potentially be exited before the properties have been assigned to the + values defines by the state. This is true in particular when there are transitions out from the + state that do not depend on the \l {QState::}{propertiesAssigned()} signal, as described in the previous section. + + The State Machine API guarantees that a property assigned by the state machine either: + \list + \li Has a value explicitly assigned to the property. + \li Is currently being animated into a value explicitly assigned to the property. + \endlist + + When a state is exited prior to the animation finishing, the behavior of the state machine depends + on the target state of the transition. If the target state explicitly assigns a value to the + property, no additional action will be taken. The property will be assigned the value defined by + the target state. + + If the target state does not assign any value to the property, there are two + options: By default, the property will be assigned the value defined by the state it is leaving + (the value it would have been assigned if the animation had been permitted to finish playing). If + a global restore policy is set, however, this will take precedence, and the property will be + restored as usual. + + \section1 Default Animations + + As described earlier, you can add animations to transitions to make sure property assignments + in the target state are animated. If you want a specific animation to be used for a given property + regardless of which transition is taken, you can add it as a default animation to the state + machine. This is in particular useful when the properties assigned (or restored) by specific + states is not known when the machine is constructed. + + \code + QState *s1 = new QState(); + QState *s2 = new QState(); + + s2->assignProperty(object, "fooBar", 2.0); + s1->addTransition(s2); + + QStateMachine machine; + machine.setInitialState(s1); + machine.addDefaultAnimation(new QPropertyAnimation(object, "fooBar")); + \endcode + + When the machine is in state \c s2, the machine will play the default animation for the + property \c fooBar since this property is assigned by \c s2. + + Note that animations explicitly set on transitions will take precedence over any default + animation for the given property. + + \section1 Nesting State Machines + + QStateMachine is a subclass of QState. This allows for a state machine to be a child state of + another machine. QStateMachine reimplements QState::onEntry() and calls QStateMachine::start(), + so that when the child state machine is entered, it will automatically start running. + + The parent state machine treats the child machine as an \e atomic state in the state machine + algorithm. The child state machine is self-contained; it maintains its own event queue and + configuration. In particular, note that the \l{QStateMachine::}{configuration()} + of the child machine is not part of the parent machine's configuration (only the child machine + itself is). + + States of the child state machine cannot be specified as targets of transitions in the parent + state machine; only the child state machine itself can. Conversely, states of the parent state + machine cannot be specified as targets of transitions in the child state machine. The child + state machine's \l{QState::}{finished}() signal can be used to trigger a transition + in the parent machine. +*/ diff --git a/src/statemachine/doc/qtstatemachine-examples.qdoc b/src/statemachine/doc/qtstatemachine-examples.qdoc new file mode 100644 index 0000000..7794b6e --- /dev/null +++ b/src/statemachine/doc/qtstatemachine-examples.qdoc @@ -0,0 +1,12 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! +\group examples-qtstatemachine +\title Qt State Machine Examples +\brief Examples for the Qt State Machine module. + +The Qt State Machine example applications demonstrate the functionality provided by the +\l{Qt State Machine} module. + +*/ diff --git a/src/statemachine/doc/qtstatemachine-index.qdoc b/src/statemachine/doc/qtstatemachine-index.qdoc new file mode 100644 index 0000000..0557efa --- /dev/null +++ b/src/statemachine/doc/qtstatemachine-index.qdoc @@ -0,0 +1,68 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page qtstatemachine-index.html + \title Qt State Machine + \brief Provides functionality to create and execute state graphs. + + The State Machine framework provides classes for creating and executing + state graphs. + + \section1 Using the Module + + \section2 QML API + + \include {module-use.qdocinc} {using the qml api} {QtQml.StateMachine} + + \section2 C++ API + + \include {module-use.qdocinc} {using the c++ api} + + \section3 Building with CMake + + \include {module-use.qdocinc} {building with cmake} {StateMachine} + + \section3 Building with qmake + + \include {module-use.qdocinc} {building_with_qmake} {statemachine} + + \section1 Articles and Guides + + \list + \li \l {Qt State Machine Overview} + \li \l {Qt State Machine C++ Guide} + \li \l {Qt State Machine QML Guide} + \endlist + + \section1 Examples + + \list + \li \l {Qt State Machine Examples} + \endlist + + \section1 Reference + + \list + \li \l {Qt State Machine C++ Classes} {C++ Classes and Namespaces} + \li \l {Qt State Machine QML Types} {QML Types} + \endlist + + \section1 Module Evolution + \l{Changes to Qt State Machine} lists important changes in the module API + and functionality that were done for the Qt 6 series of Qt. + + \section1 Licenses and Trademarks + + The Qt State Machine module is available under commercial licenses from + \l{The Qt Company}. In addition, it is available under free software licenses: + The \l{GNU Lesser General Public License, version 3}, or + the \l{GNU General Public License, version 2}. + See \l{Qt Licensing} for further details. + + Furthermore, Qt State Machine in Qt \QtVersion may contain third party + modules under following permissive licenses: + + \generatelist{groupsbymodule attributions-qtstatemachine} + +*/ diff --git a/src/statemachine/doc/qtstatemachine-module-cpp.qdoc b/src/statemachine/doc/qtstatemachine-module-cpp.qdoc new file mode 100644 index 0000000..fc81267 --- /dev/null +++ b/src/statemachine/doc/qtstatemachine-module-cpp.qdoc @@ -0,0 +1,16 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \module QtStateMachine + \title Qt State Machine C++ Classes + \ingroup modules + \ingroup technology-apis + \qtvariable statemachine + \qtcmakepackage StateMachine + + \brief The Qt State Machine module provides classes for creating and executing state graphs. + + The \l {Qt State Machine} page contains information about how to use the module. + +*/ diff --git a/src/statemachine/doc/qtstatemachine-module-qml.qdoc b/src/statemachine/doc/qtstatemachine-module-qml.qdoc new file mode 100644 index 0000000..d9f84bf --- /dev/null +++ b/src/statemachine/doc/qtstatemachine-module-qml.qdoc @@ -0,0 +1,18 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \qmlmodule QtQml.StateMachine 6.\QtMinorVersion + \title Qt State Machine QML Types + \ingroup qmlmodules + \brief Enables the use of State Machine with QML. + + To import the QML types into your application, use the following import statement + in your .qml file: + + \qml + import QtQml.StateMachine + \endqml + + For more information, see \l {Qt State Machine QML Guide}. + */ diff --git a/src/statemachine/doc/qtstatemachine-module-use.qdocinc b/src/statemachine/doc/qtstatemachine-module-use.qdocinc new file mode 100644 index 0000000..fb3101d --- /dev/null +++ b/src/statemachine/doc/qtstatemachine-module-use.qdocinc @@ -0,0 +1,15 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +//! [cmakebuild] + \code + find_package(Qt6 REQUIRED COMPONENTS StateMachine) + target_link_libraries(mytarget PRIVATE Qt6::StateMachine) + \endcode +//! [cmakebuild] + +//! [qmakebuild] + \code + QT += statemachine + \endcode +//! [qmakebuild] diff --git a/src/statemachine/doc/qtstatemachine-overview.qdoc b/src/statemachine/doc/qtstatemachine-overview.qdoc new file mode 100644 index 0000000..3438d86 --- /dev/null +++ b/src/statemachine/doc/qtstatemachine-overview.qdoc @@ -0,0 +1,42 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page qtstatemachine-overview.html + \title Qt State Machine Overview + \brief An overview of the State Machine framework for constructing and executing state graphs. + \ingroup technology-apis + \ingroup explanation + + \tableofcontents + + The State Machine framework provides classes for creating and executing + state graphs. The concepts and notation are based on those from Harel's + \l{http://www.wisdom.weizmann.ac.il/~dharel/SCANNED.PAPERS/Statecharts.pdf}{Statecharts: A visual formalism for complex systems}, which + is also the basis of UML state diagrams. The semantics of state machine + execution are based on \l{State Chart XML: State Machine Notation for + Control Abstraction}{State Chart XML (SCXML)}. + + Statecharts provide a graphical way of modeling how a system reacts to + stimuli. This is done by defining the possible \e states that the system can + be in, and how the system can move from one state to another (\e transitions + between states). A key characteristic of event-driven systems (such as Qt + applications) is that behavior often depends not only on the last or current + event, but also the events that preceded it. With statecharts, this + information is easy to express. + + The State Machine framework provides an API and execution model that can be + used to effectively embed the elements and semantics of statecharts in Qt + applications. The framework integrates tightly with Qt's meta-object system; + for example, transitions between states can be triggered by signals, and + states can be configured to set properties and invoke methods on {QObject}s. + Qt's event system is used to drive the state machines. + + The state graph in the State Machine framework is hierarchical. States can be nested inside of + other states, and the current configuration of the state machine consists of the set of states + which are currently active. All the states in a valid configuration of the state machine will + have a common ancestor. + + \sa {Qt State Machine C++ Guide}, {Qt State Machine QML Guide}, {Qt SCXML Overview} + + */ diff --git a/src/statemachine/doc/qtstatemachine.qdocconf b/src/statemachine/doc/qtstatemachine.qdocconf new file mode 100644 index 0000000..7736501 --- /dev/null +++ b/src/statemachine/doc/qtstatemachine.qdocconf @@ -0,0 +1,55 @@ +include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtscxml.qdocconf) + +project = QtStateMachine +description = Qt State Machine Reference Documentation +version = $QT_VERSION + +imagedirs = images src/images + +# Install path for the examples +examplesinstallpath = statemachine +exampledirs = ../../../examples/statemachine \ + snippets + +qhp.QtStateMachine.subprojects = classes qmltypes examples +qhp.QtStateMachine.subprojects.classes.title = C++ Classes +qhp.QtStateMachine.subprojects.classes.indexTitle = Qt State Machine C++ Classes +qhp.QtStateMachine.subprojects.classes.selectors = class headerfile +qhp.QtStateMachine.subprojects.classes.sortPages = true +qhp.QtStateMachine.subprojects.qmltypes.title = QML Types +qhp.QtStateMachine.subprojects.qmltypes.indexTitle = Qt State Machine QML Types +qhp.QtStateMachine.subprojects.qmltypes.selectors = qmltype +qhp.QtStateMachine.subprojects.qmltypes.sortPages = true +qhp.QtStateMachine.subprojects.examples.title = Examples +qhp.QtStateMachine.subprojects.examples.indexTitle = Qt State Machine Examples +qhp.QtStateMachine.subprojects.examples.selectors = doc:example + +qhp.projects = QtStateMachine + +qhp.QtStateMachine.file = qtstatemachine.qhp +qhp.QtStateMachine.namespace = org.qt-project.qtstatemachine.$QT_VERSION_TAG +qhp.QtStateMachine.virtualFolder = qtstatemachine +qhp.QtStateMachine.indexTitle = Qt StateMachine +qhp.QtStateMachine.indexRoot = + +depends += qtcore qtdoc qmake qtquick qtwidgets qtgui qtqml qtscxml qtcmake + +headerdirs = .. \ + ../gui \ + ../../statemachineqml \ + ../../../examples/statemachine + +sourcedirs += .. \ + ../gui \ + ../../statemachineqml \ + ../../../examples/statemachine + +tagfile = qtstatemachine.tags + +navigation.landingpage = "Qt State Machine" +navigation.cppclassespage = "Qt State Machine C++ Classes" +navigation.qmltypespage = "Qt State Machine QML Types" + +# Highlighted examples in Data Processing & IO category +manifestmeta.highlighted.names = "QtStateMachine/Traffic Light" diff --git a/src/statemachine/doc/snippets/code/src_corelib_statemachine_qstatemachine.cpp b/src/statemachine/doc/snippets/code/src_corelib_statemachine_qstatemachine.cpp new file mode 100644 index 0000000..4c9a0a5 --- /dev/null +++ b/src/statemachine/doc/snippets/code/src_corelib_statemachine_qstatemachine.cpp @@ -0,0 +1,18 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +//! [simple state machine] +QPushButton button; + +QStateMachine machine; +QState *s1 = new QState(); +s1->assignProperty(&button, "text", "Click me"); + +QFinalState *s2 = new QFinalState(); +s1->addTransition(&button, &QPushButton::clicked, s2); + +machine.addState(s1); +machine.addState(s2); +machine.setInitialState(s1); +machine.start(); +//! [simple state machine] diff --git a/src/statemachine/doc/snippets/qml/statemachine/Button.qml b/src/statemachine/doc/snippets/qml/statemachine/Button.qml new file mode 100644 index 0000000..b968a18 --- /dev/null +++ b/src/statemachine/doc/snippets/qml/statemachine/Button.qml @@ -0,0 +1,51 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Window + +Item { + id: container + + property alias text: buttonLabel.text + property alias label: buttonLabel + signal clicked + property alias containsMouse: mouseArea.containsMouse + property alias pressed: mouseArea.pressed + implicitHeight: Math.max(Screen.pixelDensity * 7, buttonLabel.implicitHeight * 1.2) + implicitWidth: Math.max(Screen.pixelDensity * 11, buttonLabel.implicitWidth * 1.3) + height: implicitHeight + width: implicitWidth + property bool checkable: false + property bool checked: false + + SystemPalette { id: palette } + + Rectangle { + id: frame + anchors.fill: parent + color: palette.button + gradient: Gradient { + GradientStop { position: 0.0; color: mouseArea.pressed ? Qt.darker(palette.button, 1.3) : palette.button } + GradientStop { position: 1.0; color: Qt.darker(palette.button, 1.3) } + } + antialiasing: true + radius: height / 6 + border.color: Qt.darker(palette.button, 1.5) + border.width: 1 + } + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: container.clicked() + hoverEnabled: true + } + + Text { + id: buttonLabel + text: container.text + color: palette.buttonText + anchors.centerIn: parent + } +} diff --git a/src/statemachine/doc/snippets/qml/statemachine/basicstate.qml b/src/statemachine/doc/snippets/qml/statemachine/basicstate.qml new file mode 100644 index 0000000..dac2914 --- /dev/null +++ b/src/statemachine/doc/snippets/qml/statemachine/basicstate.qml @@ -0,0 +1,18 @@ +// Copyright (C) 2014 Ford Motor Company +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +//! [document] +import QtQuick +import QtQml.StateMachine as DSM + +Rectangle { + DSM.StateMachine { + id: stateMachine + initialState: state + running: true + DSM.State { + id: state + } + } +} +//! [document] diff --git a/src/statemachine/doc/snippets/qml/statemachine/finalstate.qml b/src/statemachine/doc/snippets/qml/statemachine/finalstate.qml new file mode 100644 index 0000000..160419a --- /dev/null +++ b/src/statemachine/doc/snippets/qml/statemachine/finalstate.qml @@ -0,0 +1,26 @@ +// Copyright (C) 2014 Ford Motor Company +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +//! [document] +import QtQuick +import QtQml.StateMachine as DSM + +Rectangle { + DSM.StateMachine { + id: stateMachine + initialState: state + running: true + DSM.State { + id: state + DSM.TimeoutTransition { + targetState: finalState + timeout: 200 + } + } + DSM.FinalState { + id: finalState + } + onFinished: console.log("state finished") + } +} +//! [document] diff --git a/src/statemachine/doc/snippets/qml/statemachine/guardcondition.qml b/src/statemachine/doc/snippets/qml/statemachine/guardcondition.qml new file mode 100644 index 0000000..9bacea3 --- /dev/null +++ b/src/statemachine/doc/snippets/qml/statemachine/guardcondition.qml @@ -0,0 +1,31 @@ +// Copyright (C) 2014 Ford Motor Company +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +//! [document] +import QtQuick +import QtQml.StateMachine as DSM + +Rectangle { + Button { + anchors.fill: parent + id: button + DSM.StateMachine { + DSM.State { + DSM.SignalTransition { + targetState: finalState + signal: button.mysignal + // the guard condition uses the mystr string argument from mysignal + guard: mystr == "test" + } + } + DSM.FinalState { + id: finalState + } + } + // define the signal the SignalTransition is connected with + signal mysignal(mystr: string) + // on clicking the button emit the signal with a single string argument + onClicked: button.mysignal("test") + } +} +//! [document] diff --git a/src/statemachine/doc/snippets/qml/statemachine/historystate.qml b/src/statemachine/doc/snippets/qml/statemachine/historystate.qml new file mode 100644 index 0000000..e3e7a43 --- /dev/null +++ b/src/statemachine/doc/snippets/qml/statemachine/historystate.qml @@ -0,0 +1,48 @@ +// Copyright (C) 2014 Ford Motor Company +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +//! [document] +import QtQuick +import QtQml.StateMachine as DSM + +Rectangle { + Button { + anchors.fill: parent + id: button + text: "Press me" + DSM.StateMachine { + id: stateMachine + initialState: parentState + running: true + DSM.State { + id: parentState + initialState: child2 + onEntered: console.log("parentState entered") + onExited: console.log("parentState exited") + DSM.State { + id: child1 + onEntered: console.log("child1 entered") + onExited: console.log("child1 exited") + } + DSM.State { + id: child2 + onEntered: console.log("child2 entered") + onExited: console.log("child2 exited") + } + DSM.HistoryState { + id: historyState + defaultState: child1 + } + DSM.SignalTransition { + targetState: historyState + + // Clicking the button will cause the state machine to enter the child state + // that parentState was in the last time parentState was exited, or the history state's default + // state if parentState has never been entered. + signal: button.clicked + } + } + } + } +} +//! [document] diff --git a/src/statemachine/doc/snippets/qml/statemachine/signaltransition.qml b/src/statemachine/doc/snippets/qml/statemachine/signaltransition.qml new file mode 100644 index 0000000..3d7b4b0 --- /dev/null +++ b/src/statemachine/doc/snippets/qml/statemachine/signaltransition.qml @@ -0,0 +1,40 @@ +// Copyright (C) 2014 Ford Motor Company +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +//! [document] +import QtQuick +import QtQml.StateMachine as DSM + +Rectangle { + DSM.StateMachine { + id: stateMachine + initialState: state + running: true + DSM.State { + id: state + DSM.SignalTransition { + targetState: finalState + signal: button.clicked + guard: guardButton.checked + } + } + DSM.FinalState { + id: finalState + } + onFinished: Qt.quit() + } + Row { + spacing: 2 + Button { + id: button + text: "Finish state" + } + + Button { + id: guardButton + checkable: true + text: checked ? "Press to block the SignalTransition" : "Press to unblock the SignalTransition" + } + } +} +//! [document] diff --git a/src/statemachine/doc/snippets/qml/statemachine/signaltransitionsignal.qml b/src/statemachine/doc/snippets/qml/statemachine/signaltransitionsignal.qml new file mode 100644 index 0000000..2aeae2a --- /dev/null +++ b/src/statemachine/doc/snippets/qml/statemachine/signaltransitionsignal.qml @@ -0,0 +1,25 @@ +// Copyright (C) 2014 Ford Motor Company +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +//! [document] +import QtQuick +import QtQml.StateMachine as DSM + +Rectangle { + Button { + anchors.fill: parent + id: button + DSM.StateMachine { + DSM.State { + DSM.SignalTransition { + targetState: finalState + signal: button.clicked + } + } + DSM.FinalState { + id: finalState + } + } + } +} +//! [document] diff --git a/src/statemachine/doc/snippets/qml/statemachine/simplestatemachine.qml b/src/statemachine/doc/snippets/qml/statemachine/simplestatemachine.qml new file mode 100644 index 0000000..cd7e01e --- /dev/null +++ b/src/statemachine/doc/snippets/qml/statemachine/simplestatemachine.qml @@ -0,0 +1,31 @@ +// Copyright (C) 2014 Ford Motor Company +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +//! [document] +import QtQuick +import QtQml.StateMachine as DSM + +Rectangle { + Button { + anchors.fill: parent + id: button + text: "Finish state" + DSM.StateMachine { + id: stateMachine + initialState: state + running: true + DSM.State { + id: state + DSM.SignalTransition { + targetState: finalState + signal: button.clicked + } + } + DSM.FinalState { + id: finalState + } + onFinished: Qt.quit() + } + } +} +//! [document] diff --git a/src/statemachine/doc/snippets/qml/statemachine/statemachine-button-history.qml b/src/statemachine/doc/snippets/qml/statemachine/statemachine-button-history.qml new file mode 100644 index 0000000..e5947ae --- /dev/null +++ b/src/statemachine/doc/snippets/qml/statemachine/statemachine-button-history.qml @@ -0,0 +1,112 @@ +// Copyright (C) 2014 Ford Motor Company +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +//! [document] +import QtQuick +import QtQml.StateMachine + +Rectangle { +//![0] + 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() + } +//![0] +} +//! [document] diff --git a/src/statemachine/doc/snippets/qml/statemachine/statemachine-button-nested-ignore-quit.qml b/src/statemachine/doc/snippets/qml/statemachine/statemachine-button-nested-ignore-quit.qml new file mode 100644 index 0000000..db40f79 --- /dev/null +++ b/src/statemachine/doc/snippets/qml/statemachine/statemachine-button-nested-ignore-quit.qml @@ -0,0 +1,97 @@ +// Copyright (C) 2014 Ford Motor Company +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +//! [document] +import QtQuick +import QtQml.StateMachine + +Rectangle { + 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") + } + +//![0] + 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") + } +//![0] + + 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() + } +} +//! [document] diff --git a/src/statemachine/doc/snippets/qml/statemachine/statemachine-button-nested.qml b/src/statemachine/doc/snippets/qml/statemachine/statemachine-button-nested.qml new file mode 100644 index 0000000..eae7427 --- /dev/null +++ b/src/statemachine/doc/snippets/qml/statemachine/statemachine-button-nested.qml @@ -0,0 +1,89 @@ +// Copyright (C) 2014 Ford Motor Company +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +//! [document] +import QtQuick +import QtQml.StateMachine + +Rectangle { +//![0] + 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() + } +//![0] +} +//! [document] diff --git a/src/statemachine/doc/snippets/qml/statemachine/statemachine-button.qml b/src/statemachine/doc/snippets/qml/statemachine/statemachine-button.qml new file mode 100644 index 0000000..8873117 --- /dev/null +++ b/src/statemachine/doc/snippets/qml/statemachine/statemachine-button.qml @@ -0,0 +1,63 @@ +// Copyright (C) 2014 Ford Motor Company +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +//! [document] +import QtQuick +import QtQml.StateMachine + +Rectangle { +//![0] + 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") + } + } +//![0] +} +//! [document] diff --git a/src/statemachine/doc/snippets/qml/statemachine/timeouttransition.qml b/src/statemachine/doc/snippets/qml/statemachine/timeouttransition.qml new file mode 100644 index 0000000..60bd420 --- /dev/null +++ b/src/statemachine/doc/snippets/qml/statemachine/timeouttransition.qml @@ -0,0 +1,32 @@ +// Copyright (C) 2014 Ford Motor Company +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +//! [document] +import QtQuick +import QtQml.StateMachine as DSM + +Rectangle { + Button { + anchors.fill: parent + id: button + text: "Finish state" + enabled: !stateMachine.running + onClicked: stateMachine.running = true + DSM.StateMachine { + id: stateMachine + initialState: state + running: true + DSM.State { + id: state + DSM.TimeoutTransition { + targetState: finalState + timeout: 1000 + } + } + DSM.FinalState { + id: finalState + } + } + } +} +//! [document] diff --git a/src/statemachine/doc/snippets/statemachine/eventtest.cpp b/src/statemachine/doc/snippets/statemachine/eventtest.cpp new file mode 100644 index 0000000..14cb48b --- /dev/null +++ b/src/statemachine/doc/snippets/statemachine/eventtest.cpp @@ -0,0 +1,37 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtGui> +#include <QtStateMachine> + +class MyTransition : public QAbstractTransition +{ + Q_OBJECT +public: + MyTransition() {} + +protected: +//![0] + bool eventTest(QEvent *event) override + { + if (event->type() == QEvent::Wrapped) { + QEvent *wrappedEvent = static_cast<QStateMachine::WrappedEvent *>(event)->event(); + if (wrappedEvent->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast<QKeyEvent *>(wrappedEvent); + // Do your event test + } + } + return false; + } +//![0] + + void onTransition(QEvent *event) override + { + + } +}; + +int main(int argv, char **args) +{ + return 0; +} diff --git a/src/statemachine/doc/snippets/statemachine/main.cpp b/src/statemachine/doc/snippets/statemachine/main.cpp new file mode 100644 index 0000000..3cbeb5d --- /dev/null +++ b/src/statemachine/doc/snippets/statemachine/main.cpp @@ -0,0 +1,52 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtStateMachine> +#include <QtWidgets> + +int main(int argv, char **args) +{ + QApplication app(argv, args); + + QLabel *label = new QLabel; + QPushButton *button = new QPushButton; + +//![0] + QStateMachine machine; + QState *s1 = new QState(); + QState *s2 = new QState(); + QState *s3 = new QState(); +//![0] + +//![4] + s1->assignProperty(label, "text", "In state s1"); + s2->assignProperty(label, "text", "In state s2"); + s3->assignProperty(label, "text", "In state s3"); +//![4] + +//![5] + QObject::connect(s3, &QState::entered, button, &QPushButton:showMaximized); + QObject::connect(s3, &QState::exited, button, &QPushButton::showMinimized); +//![5] + +//![1] + s1->addTransition(button, &QPushButton::clicked, s2); + s2->addTransition(button, &QPushButton::clicked, s3); + s3->addTransition(button, &QPushButton::clicked, s1); +//![1] + +//![2] + machine.addState(s1); + machine.addState(s2); + machine.addState(s3); + machine.setInitialState(s1); +//![2] + +//![3] + machine.start(); +//![3] + + label->show(); + + return app.exec(); +} diff --git a/src/statemachine/doc/snippets/statemachine/main2.cpp b/src/statemachine/doc/snippets/statemachine/main2.cpp new file mode 100644 index 0000000..4fe4c19 --- /dev/null +++ b/src/statemachine/doc/snippets/statemachine/main2.cpp @@ -0,0 +1,57 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtStateMachine> +#include <QtWidgets> + +int main(int argv, char **args) +{ + QApplication app(argv, args); + + QStateMachine machine; + +//![0] + QState *s1 = new QState(); + QState *s11 = new QState(s1); + QState *s12 = new QState(s1); + QState *s13 = new QState(s1); + s1->setInitialState(s11); + machine.addState(s1); +//![0] + +//![2] + s12->addTransition(quitButton, &QPushButton::clicked, s12); +//![2] + +//![1] + QFinalState *s2 = new QFinalState(); + s1->addTransition(quitButton, &QPushButton::clicked, s2); + machine.addState(s2); + machine.setInitialState(s1); + + QObject::connect(&machine, &QStateMachine::finished, + QCoreApplication::instance(), &QCoreApplication::quit); +//![1] + + QButton *interruptButton = new QPushButton("Interrupt Button"); + QWidget *mainWindow = new QWidget(); + +//![3] + QHistoryState *s1h = new QHistoryState(s1); + + QState *s3 = new QState(); + s3->assignProperty(label, "text", "In s3"); + QMessageBox *mbox = new QMessageBox(mainWindow); + mbox->addButton(QMessageBox::Ok); + mbox->setText("Interrupted!"); + mbox->setIcon(QMessageBox::Information); + QObject::connect(s3, &QState::entered, mbox, &QMessageBox::exec); + s3->addTransition(s1h); + machine.addState(s3); + + s1->addTransition(interruptButton, &QPushButton::clicked, s3); +//![3] + + return app.exec(); +} + diff --git a/src/statemachine/doc/snippets/statemachine/main3.cpp b/src/statemachine/doc/snippets/statemachine/main3.cpp new file mode 100644 index 0000000..7bae11f --- /dev/null +++ b/src/statemachine/doc/snippets/statemachine/main3.cpp @@ -0,0 +1,24 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtGui> +#include <QtStateMachine> + +int main(int argv, char **args) +{ + QApplication app(argv, args); + +//![0] + QState *s1 = new QState(QState::ParallelStates); + // s11 and s12 will be entered in parallel + QState *s11 = new QState(s1); + QState *s12 = new QState(s1); +//![0] + +//![1] + s1->addTransition(s1, &QState::finished, s2); +//![1] + + return app.exec(); +} + diff --git a/src/statemachine/doc/snippets/statemachine/main4.cpp b/src/statemachine/doc/snippets/statemachine/main4.cpp new file mode 100644 index 0000000..bac255a --- /dev/null +++ b/src/statemachine/doc/snippets/statemachine/main4.cpp @@ -0,0 +1,75 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtGui> +#include <QtStateMachine> + +//![0] +struct StringEvent : public QEvent +{ + StringEvent(const QString &val) + : QEvent(QEvent::Type(QEvent::User+1)), + value(val) {} + + QString value; +}; +//![0] + +//![1] +class StringTransition : public QAbstractTransition +{ + Q_OBJECT + +public: + StringTransition(const QString &value) + : m_value(value) {} + +protected: + bool eventTest(QEvent *e) override + { + if (e->type() != QEvent::Type(QEvent::User+1)) // StringEvent + return false; + StringEvent *se = static_cast<StringEvent*>(e); + return (m_value == se->value); + } + + void onTransition(QEvent *) override {} + +private: + QString m_value; +}; +//![1] + +int main(int argv, char **args) +{ + QApplication app(argv, args); + +//![2] + QStateMachine machine; + QState *s1 = new QState(); + QState *s2 = new QState(); + QFinalState *done = new QFinalState(); + + StringTransition *t1 = new StringTransition("Hello"); + t1->setTargetState(s2); + s1->addTransition(t1); + StringTransition *t2 = new StringTransition("world"); + t2->setTargetState(done); + s2->addTransition(t2); + + machine.addState(s1); + machine.addState(s2); + machine.addState(done); + machine.setInitialState(s1); +//![2] + +//![3] + machine.postEvent(new StringEvent("Hello")); + machine.postEvent(new StringEvent("world")); +//![3] + + return app.exec(); +} + +#include "main4.moc" + diff --git a/src/statemachine/doc/snippets/statemachine/main5.cpp b/src/statemachine/doc/snippets/statemachine/main5.cpp new file mode 100644 index 0000000..3b1b7e9 --- /dev/null +++ b/src/statemachine/doc/snippets/statemachine/main5.cpp @@ -0,0 +1,136 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtGui> +#include <QtStateMachine> + +int main(int argv, char **args) +{ + QApplication app(argv, args); + QWidget *button; + + { +//![0] + QStateMachine machine; + machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties); + + QState *s1 = new QState(); + s1->assignProperty(object, "fooBar", 1.0); + machine.addState(s1); + machine.setInitialState(s1); + + QState *s2 = new QState(); + machine.addState(s2); +//![0] + } + + { + +//![2] + QStateMachine machine; + machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties); + + QState *s1 = new QState(); + s1->assignProperty(object, "fooBar", 1.0); + machine.addState(s1); + machine.setInitialState(s1); + + QState *s2 = new QState(s1); + s2->assignProperty(object, "fooBar", 2.0); + s1->setInitialState(s2); + + QState *s3 = new QState(s1); +//![2] + + } + + { +//![3] + QState *s1 = new QState(); + QState *s2 = new QState(); + + s1->assignProperty(button, "geometry", QRectF(0, 0, 50, 50)); + s2->assignProperty(button, "geometry", QRectF(0, 0, 100, 100)); + + s1->addTransition(button, &QPushButton::clicked, s2); +//![3] + + } + + { +//![4] + QState *s1 = new QState(); + QState *s2 = new QState(); + + s1->assignProperty(button, "geometry", QRectF(0, 0, 50, 50)); + s2->assignProperty(button, "geometry", QRectF(0, 0, 100, 100)); + + QSignalTransition *transition = s1->addTransition(button, &QPushButton::clicked, s2); + transition->addAnimation(new QPropertyAnimation(button, "geometry")); +//![4] + + } + + { + QMainWindow *mainWindow = 0; + +//![5] + QMessageBox *messageBox = new QMessageBox(mainWindow); + messageBox->addButton(QMessageBox::Ok); + messageBox->setText("Button geometry has been set!"); + messageBox->setIcon(QMessageBox::Information); + + QState *s1 = new QState(); + + QState *s2 = new QState(); + s2->assignProperty(button, "geometry", QRectF(0, 0, 50, 50)); + connect(s2, &QState::entered, messageBox, SLOT(exec())); + + s1->addTransition(button, &QPushButton::clicked, s2); +//![5] + } + + { + QMainWindow *mainWindow = 0; + +//![6] + QMessageBox *messageBox = new QMessageBox(mainWindow); + messageBox->addButton(QMessageBox::Ok); + messageBox->setText("Button geometry has been set!"); + messageBox->setIcon(QMessageBox::Information); + + QState *s1 = new QState(); + + QState *s2 = new QState(); + s2->assignProperty(button, "geometry", QRectF(0, 0, 50, 50)); + + QState *s3 = new QState(); + connect(s3, &QState::entered, messageBox, SLOT(exec())); + + s1->addTransition(button, &QPushButton::clicked, s2); + s2->addTransition(s2, &QState::propertiesAssigned, s3); +//![6] + + } + + { + +//![7] + QState *s1 = new QState(); + QState *s2 = new QState(); + + s2->assignProperty(object, "fooBar", 2.0); + s1->addTransition(s2); + + QStateMachine machine; + machine.setInitialState(s1); + machine.addDefaultAnimation(new QPropertyAnimation(object, "fooBar")); +//![7] + + } + + + + return app.exec(); +} + diff --git a/src/statemachine/doc/src/images/moveblocks-example.png b/src/statemachine/doc/src/images/moveblocks-example.png Binary files differnew file mode 100644 index 0000000..56353d1 --- /dev/null +++ b/src/statemachine/doc/src/images/moveblocks-example.png diff --git a/src/statemachine/doc/src/images/rogue-example.png b/src/statemachine/doc/src/images/rogue-example.png Binary files differnew file mode 100644 index 0000000..7aeb0e5 --- /dev/null +++ b/src/statemachine/doc/src/images/rogue-example.png diff --git a/src/statemachine/doc/src/images/trafficlight-example.png b/src/statemachine/doc/src/images/trafficlight-example.png Binary files differnew file mode 100644 index 0000000..3431542 --- /dev/null +++ b/src/statemachine/doc/src/images/trafficlight-example.png |