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
:
explicit
LightWidget(const
QColor &
amp;color, QWidget *
parent =
nullptr
)
:
QWidget(parent), m_color(color)
{
}
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(rect());
}
private
:
QColor m_color;
bool
m_on =
false
;
}
;
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
{
Q_OBJECT
public
:
explicit
TrafficLightWidget(QWidget *
parent =
nullptr
) : QWidget(parent)
{
auto
vbox =
new
QVBoxLayout(this
);
m_red =
new
LightWidget(Qt::
red);
vbox-&
gt;addWidget(m_red);
m_yellow =
new
LightWidget(Qt::
yellow);
vbox-&
gt;addWidget(m_yellow);
m_green =
new
LightWidget(Qt::
green);
vbox-&
gt;addWidget(m_green);
auto
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
)
{
auto
lightState =
new
QState(parent);
auto
timer =
new
QTimer(lightState);
timer-&
gt;setInterval(duration);
timer-&
gt;setSingleShot(true
);
auto
timing =
new
QState(lightState);
QObject::
connect(timing, &
amp;QAbstractState::
entered, light, &
amp;LightWidget::
turnOn);
QObject::
connect(timing, &
amp;QAbstractState::
entered, timer, QOverload&
lt;&
gt;::
of(&
amp;QTimer::
start));
QObject::
connect(timing, &
amp;QAbstractState::
exited, light, &
amp;LightWidget::
turnOff);
auto
done =
new
QFinalState(lightState);
timing-&
gt;addTransition(timer, &
amp;QTimer::
timeout, done);
lightState-&
gt;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
{
Q_OBJECT
public
:
explicit
TrafficLight(QWidget *
parent =
nullptr
) : QWidget(parent)
{
auto
vbox =
new
QVBoxLayout(this
);
auto
widget =
new
TrafficLightWidget;
vbox-&
gt;addWidget(widget);
vbox-&
gt;setContentsMargins(QMargins());
auto
machine =
new
QStateMachine(this
);
auto
redGoingYellow =
createLightState(widget-&
gt;redLight(), 3000
);
redGoingYellow-&
gt;setObjectName("redGoingYellow"
);
auto
yellowGoingGreen =
createLightState(widget-&
gt;yellowLight(), 1000
);
yellowGoingGreen-&
gt;setObjectName("yellowGoingGreen"
);
redGoingYellow-&
gt;addTransition(redGoingYellow, &
amp;QState::
finished, yellowGoingGreen);
auto
greenGoingYellow =
createLightState(widget-&
gt;greenLight(), 3000
);
greenGoingYellow-&
gt;setObjectName("greenGoingYellow"
);
yellowGoingGreen-&
gt;addTransition(yellowGoingGreen, &
amp;QState::
finished, greenGoingYellow);
auto
yellowGoingRed =
createLightState(widget-&
gt;yellowLight(), 1000
);
yellowGoingRed-&
gt;setObjectName("yellowGoingRed"
);
greenGoingYellow-&
gt;addTransition(greenGoingYellow, &
amp;QState::
finished, yellowGoingRed);
yellowGoingRed-&
gt;addTransition(yellowGoingRed, &
amp;QState::
finished, redGoingYellow);
machine-&
gt;addState(redGoingYellow);
machine-&
gt;addState(yellowGoingGreen);
machine-&
gt;addState(greenGoingYellow);
machine-&
gt;addState(yellowGoingRed);
machine-&
gt;setInitialState(redGoingYellow);
machine-&
gt;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.