Traffic Light Example | Qt Widgets 5.15.0

- Qt 5.15
- Qt Widgets
- Traffic Light Example
- Qt 5.15.0 Reference Documentation

# Traffic Light Example

The Traffic Light example shows how to use The State Machine Framework to implement the control flow of a traffic light.

In this example we write a TrafficLightWidget class. The traffic light has three lights: Red, yellow and green. The traffic light transitions from one light to another (red to yellow to green to yellow to red again) at certain intervals.

     class LightWidget : public QWidget
     {
         Q_OBJECT
         Q_PROPERTY(bool on READ isOn WRITE setOn)
     public:
         LightWidget(const QColor &color, QWidget *parent = nullptr)
             : QWidget(parent), m_color(color), m_on(false) {}

         bool isOn() const
             { return m_on; }
         void setOn(bool on)
         {
             if (on == m_on)
                 return;
             m_on = on;
             update();
         }

     public slots:
         void turnOff() { setOn(false); }
         void turnOn() { setOn(true); }

     protected:
         void paintEvent(QPaintEvent *) override
         {
             if (!m_on)
                 return;
             QPainter painter(this);
             painter.setRenderHint(QPainter::Antialiasing);
             painter.setBrush(m_color);
             painter.drawEllipse(0, 0, width(), height());
         }

     private:
         QColor m_color;
         bool m_on;
     };

The LightWidget class represents a single light of the traffic light. It provides an `on` property and two slots, turnOn() and turnOff(), to turn the light on and off, respectively. The widget paints itself in the color that's passed to the constructor.

     class TrafficLightWidget : public QWidget
     {
     public:
         TrafficLightWidget(QWidget *parent = nullptr)
             : QWidget(parent)
         {
             QVBoxLayout *vbox = new QVBoxLayout(this);
             m_red = new LightWidget(Qt::red);
             vbox->addWidget(m_red);
             m_yellow = new LightWidget(Qt::yellow);
             vbox->addWidget(m_yellow);
             m_green = new LightWidget(Qt::green);
             vbox->addWidget(m_green);
             QPalette pal = palette();
             pal.setColor(QPalette::Window, Qt::black);
             setPalette(pal);
             setAutoFillBackground(true);
         }

         LightWidget *redLight() const
             { return m_red; }
         LightWidget *yellowLight() const
             { return m_yellow; }
         LightWidget *greenLight() const
             { return m_green; }

     private:
         LightWidget *m_red;
         LightWidget *m_yellow;
         LightWidget *m_green;
     };

The TrafficLightWidget class represents the visual part of the traffic light; it's a widget that contains three lights arranged vertically, and provides accessor functions for these.

     QState *createLightState(LightWidget *light, int duration, QState *parent = nullptr)
     {
         QState *lightState = new QState(parent);
         QTimer *timer = new QTimer(lightState);
         timer->setInterval(duration);
         timer->setSingleShot(true);
         QState *timing = new QState(lightState);
         QObject::connect(timing, &QAbstractState::entered, light, &LightWidget::turnOn);
         QObject::connect(timing, &QAbstractState::entered, timer, QOverload<>::of(&QTimer::start));
         QObject::connect(timing, &QAbstractState::exited, light, &LightWidget::turnOff);
         QFinalState *done = new QFinalState(lightState);
         timing->addTransition(timer, &QTimer::timeout, done);
         lightState->setInitialState(timing);
         return lightState;
     }

The createLightState() function creates a state that turns a light on when the state is entered, and off when the state is exited. The state uses a timer, and as we shall see the timeout is used to transition from one LightState to another. Here is the statechart for the light state:

     class TrafficLight : public QWidget
     {
     public:
         TrafficLight(QWidget *parent = nullptr)
             : QWidget(parent)
         {
             QVBoxLayout *vbox = new QVBoxLayout(this);
             TrafficLightWidget *widget = new TrafficLightWidget;
             vbox->addWidget(widget);
             vbox->setContentsMargins(QMargins());

             QStateMachine *machine = new QStateMachine(this);
             QState *redGoingYellow = createLightState(widget->redLight(), 3000);
             redGoingYellow->setObjectName("redGoingYellow");
             QState *yellowGoingGreen = createLightState(widget->yellowLight(), 1000);
             yellowGoingGreen->setObjectName("yellowGoingGreen");
             redGoingYellow->addTransition(redGoingYellow, &QState::finished, yellowGoingGreen);
             QState *greenGoingYellow = createLightState(widget->greenLight(), 3000);
             greenGoingYellow->setObjectName("greenGoingYellow");
             yellowGoingGreen->addTransition(yellowGoingGreen, &QState::finished, greenGoingYellow);
             QState *yellowGoingRed = createLightState(widget->yellowLight(), 1000);
             yellowGoingRed->setObjectName("yellowGoingRed");
             greenGoingYellow->addTransition(greenGoingYellow, &QState::finished, yellowGoingRed);
             yellowGoingRed->addTransition(yellowGoingRed, &QState::finished, redGoingYellow);

             machine->addState(redGoingYellow);
             machine->addState(yellowGoingGreen);
             machine->addState(greenGoingYellow);
             machine->addState(yellowGoingRed);
             machine->setInitialState(redGoingYellow);
             machine->start();
         }
     };

The TrafficLight class combines the TrafficLightWidget with a state machine. The state graph has four states: red-to-yellow, yellow-to-green, green-to-yellow and yellow-to-red. The initial state is red-to-yellow; when the state's timer times out, the state machine transitions to yellow-to-green. The same process repeats through the other states. This is what the statechart looks like:

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

         TrafficLight widget;
         widget.resize(110, 300);
         widget.show();

         return app.exec();
     }

The main() function constructs a TrafficLight and shows it.

Example project @ code.qt.io