Qt 5.9 | Qt Widgets | Ping Pong States Example

Qt 5.9.9 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:
          void onEntry(QEvent *) 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:
          bool eventTest(QEvent *e) override {
              return (e->type() == QEvent::User+2);
          }
          void onTransition(QEvent *) 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:
          bool eventTest(QEvent *e) override {
              return (e->type() == QEvent::User+3);
          }
          void onTransition(QEvent *) 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