- Qt 5.5
- Qt Widgets
- Ping Pong States Example
- Qt 5.5.1 Reference Documentation

# Ping Pong States Example

This example implements a statechart where two states communicate by posting events to the state machine. The state chart looks as follows:

The `pinger` and `ponger` states are parallel states, i.e. they are entered simultaneously and will take transitions independently of eachother.

The `pinger` state will post the first `ping` event upon entry; the `ponger` state will respond by posting a `pong` event; this will cause the `pinger` state to post a new `ping` event; and so on.

    class PingEvent : public QEvent
    {
    public:
        PingEvent() : QEvent(QEvent::Type(QEvent::User+2))
            {}
    };

    class PongEvent : public QEvent
    {
    public:
        PongEvent() : QEvent(QEvent::Type(QEvent::User+3))
            {}
    };

Two custom events are defined, `PingEvent` and `PongEvent`.

    class Pinger : public QState
    {
    public:
        Pinger(QState *parent)
            : QState(parent) {}

    protected:
        virtual void onEntry(QEvent *) Q_DECL_OVERRIDE
        {
            machine()->postEvent(new PingEvent());
            fprintf(stdout, "ping?\n");
        }
    };

The `Pinger` class defines a state that posts a `PingEvent` to the state machine when the state is entered.

    class PingTransition : public QAbstractTransition
    {
    public:
        PingTransition() {}

    protected:
        virtual bool eventTest(QEvent *e) Q_DECL_OVERRIDE {
            return (e->type() == QEvent::User+2);
        }
        virtual void onTransition(QEvent *) Q_DECL_OVERRIDE
        {
            machine()->postDelayedEvent(new PongEvent(), 500);
            fprintf(stdout, "pong!\n");
        }
    };

The `PingTransition` class defines a transition that is triggered by events of type `PingEvent`, and that posts a `PongEvent` (with a delay of 500 milliseconds) to the state machine when the transition is triggered.

    class PongTransition : public QAbstractTransition
    {
    public:
        PongTransition() {}

    protected:
        virtual bool eventTest(QEvent *e) Q_DECL_OVERRIDE {
            return (e->type() == QEvent::User+3);
        }
        virtual void onTransition(QEvent *) Q_DECL_OVERRIDE
        {
            machine()->postDelayedEvent(new PingEvent(), 500);
            fprintf(stdout, "ping?\n");
        }
    };

The `PongTransition` class defines a transition that is triggered by events of type `PongEvent`, and that posts a `PingEvent` (with a delay of 500 milliseconds) to the state machine when the transition is triggered.

    int main(int argc, char **argv)
    {
        QCoreApplication app(argc, argv);

        QStateMachine machine;
        QState *group = new QState(QState::ParallelStates);
        group->setObjectName("group");

The main() function begins by creating a state machine and a parallel state group.

        Pinger *pinger = new Pinger(group);
        pinger->setObjectName("pinger");
        pinger->addTransition(new PongTransition());

        QState *ponger = new QState(group);
        ponger->setObjectName("ponger");
        ponger->addTransition(new PingTransition());

Next, the `pinger` and `ponger` states are created, with the parallel state group as their parent state. Note that the transitions are targetless. When such a transition is triggered, the source state won't be exited and re-entered; only the transition's onTransition() function will be called, and the state machine's configuration will remain the same, which is precisely what we want in this case.

        machine.addState(group);
        machine.setInitialState(group);
        machine.start();

        return app.exec();
    }

Finally, the group is added to the state machine, the machine is started, and the application event loop is entered.

Files:

- statemachine/pingpong/main.cpp
- statemachine/pingpong/pingpong.pro