Instantiating State Machines

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:

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

Or, in QML:

import QtScxml 5.7

Item {
    property QtObject stateMachine: scxmlLoader.stateMachine

    StateMachineLoader {
        id: scxmlLoader
        filename: "statemachine.scxml"
    }
}

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

auto *stateMachine = new MyStatemachine;

Or:

MyStatemachine stateMachine;

To use a compiled state machine in QML, you can assign it to a context property:

MyStatemachine stateMachine;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("stateMachine", &stateMachine);

To compile a state machine, the following lines have to be added to a .pro file:

QT += scxml
STATECHARTS = MyStatemachine.scxml

This will tell qmake to run qscxmlc which generates MyStatemachine.h and MyStatemachine.cpp, and adds them to HEADERS and SOURCES variables.

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:

QObject::connect(stateMachine, &TrafficLightStateMachine::redChanged,
    [](bool active) {
   qDebug() << (active ? "entered" : "exited") << "the red state";

And in QML:

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

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:

QObject::connect(stateMachine, &MediaPlayer::playbackStopped, [](){
    qDebug() << "Stopped!";
});

And in QML:

Connections {
    target: stateMachine
    onPlaybackStopped: console.log("Stopped!")
}

Sending events to a state machine is equally simple. You can call (or connect to) the slot:

stateMachine->tap(QVariantMap({
    std::make_pair("artist", "Fatboy Slim"),
    std::make_pair("title", "The Rockafeller Skank")
});

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

stateMacine.tap({
    "artist": "Fatboy Slim"
    "title": "The Rockafeller Skank"
})

Any invoked state machine with a name property will also show up as a property on its parent state machine.

© 2016 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.