Qt Reference Documentation

# Ping Pong States Example

Files:

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

The Ping Pong States example shows how to use parallel states together with custom events and transitions in The State Machine Framework.

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 *)
         {
             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) {
             return (e->type() == QEvent::User+2);
         }
         virtual void onTransition(QEvent *)
         {
             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) {
             return (e->type() == QEvent::User+3);
         }
         virtual void onTransition(QEvent *)
         {
             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.