summaryrefslogtreecommitdiffstats
path: root/src/scxml/doc/qtscxml-instantiating-state-machines.qdoc
blob: c78ef2b06ac04a24b919702a4999feabcf9a63f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only

/*!
    \page qtscxml-instantiating-state-machines.html
    \title Instantiating State Machines
    \brief Instantiating dynamically created and compiled state machines in C++
    and QML

    Both the dynamically created and the compiled state machines behave in the
    same way, have the same properties, states, data model, and so on. They only
    differ in the way they are instantiated. To dynamically create one in C++
    from an SCXML file, you can use:

    \code
    auto *stateMachine = QScxmlStateMachine::fromFile("MyStatemachine.scxml");
    \endcode

    Or, in QML:

    \qml
    import QtScxml

    Item {
        property StateMachine stateMachine: scxmlLoader.stateMachine

        StateMachineLoader {
            id: scxmlLoader
            source: "statemachine.scxml"
        }
    }
    \endqml

    A compiled state machine can be instantiated the same way as any C++
    object:

    \code
    auto *stateMachine = new MyStatemachine;
    \endcode

    Or:

    \code
    MyStatemachine stateMachine;
    \endcode

    To use a compiled state machine in QML, you can register it as a QML type:

    \code
    struct MyStateMachineRegistration {
        Q_GADGET
        QML_NAMED_ELEMENT(MyStateMachine)
        QML_FOREIGN(MyStateMachine)
        QML_ADDED_IN_VERSION(1, 0)
    };
    \endcode

    Then you can instantiate it in QML, like this:

    \qml
    import MyStateMachine 1.0

    MyStateMachine {
        id: stateMachine
    }
    \endqml

    To compile a state machine, the following lines have to be added to the
    project build file:

    When using cmake:

    \include qtscxml-module-use.qdocinc cmakebuild
    \include qtscxml-module-use.qdocinc cmakestatecharts

    When using qmake:

    \include qtscxml-module-use.qdocinc qmakebuild
    \include qtscxml-module-use.qdocinc qmakestatecharts

    This will tell qmake to run \e qscxmlc which generates MyStatemachine.h
    and MyStatemachine.cpp, and adds them to appropriately to the project
    headers and sources. By default, the generated files are saved in
    the build directory. The qmake \e QSCXMLC_DIR or cmake \e OUTPUT_DIRECTORY
    variable can be set to specify another directory. The qmake
    \e QSCXMLC_NAMESPACE or cmake \e NAMESPACE variable can be set to put the
    state machine code into a C++ namespace.

    After instantiating a state machine, you can connect to any state's
    active property as follows. For example, if the state machine for a
    traffic light has a state indicating that the light is red (which has the
    convenient id "red" in the scxml file), you can write:

    \code
    stateMachine->connectToState("red", [](bool active) {
        qDebug() << (active ? "entered" : "exited") << "the red state";
    \endcode

    And in QML:

    \qml
    Light {
        id: greenLight
        color: "green"
        visible: stateMachine.green
    }
    \endqml

    If you want to be notified when a state machine sends out an event, you
    can connect to the corresponding signal. For example, for a media player
    state machine which indicates that playback has stopped by sending an
    event, you can write:

    \code
    stateMachine->connectToEvent("playbackStopped", [](const QScxmlEvent &){
        qDebug() << "Stopped!";
    });
    \endcode

    And in QML:

    \qml
    import QtScxml

    EventConnection {
        stateMachine: stateMachine
        events: ["playbackStopped"]
        onOccurred: console.log("Stopped!")
    }
    \endqml

    Sending events to a state machine is equally simple:

    \code
    stateMachine->submitEvent("tap", QVariantMap({
        { "artist", "Fatboy Slim" },
        { "title", "The Rockafeller Skank" }
    });
    \endcode

    This will generate a "tap" event with the map contents available in
    _event.data inside the state machine. In QML:

    \code
    stateMachine.submitEvent("tap", {
        "artist": "Fatboy Slim"
        "title": "The Rockafeller Skank"
    })
    \endcode

    \note A state machine needs a \c QEventLoop to work correctly. The event loop is used
    to implement the \c delay attribute for events and to schedule the processing of a state machine
    when events are received from nested (or parent) state machines. A QML application or a widget
    application will always have an event loop running, so nothing extra is needed. For other
    applications, \c QApplication::run will have to be called to start the event loop processing.
*/