TaskTree Traffic Light
The TaskTree Traffic Light example demonstrates how to implement state machine behavior using TaskTree through a traffic light simulation. The example is an alternative implementation for the Traffic Light examples in Qt SCXML and Qt State Machine.

The application consists of three main components:
- A
TrafficLightwidget that displays the current state. - A
TaskTreerecipe that controls state transitions. - A
GlueInterfacethat synchronizes the UI with the state machine.
The following sections describe more details of the example's code.
Helper Glue Class
The GlueInterface class provides a clean separation between the UI and operational logic.
enum class Light { Off = 0, Red = 1 << 0, Yellow = 1 << 1, Green = 1 << 2, }; Q_DECLARE_FLAGS(Lights, Light) Q_DECLARE_OPERATORS_FOR_FLAGS(Lights) class GlueInterface final : public QObject { Q_OBJECT public: // operational logic -> GUI void setLights(Lights lights) { emit lightsChanged(lights); } // GUI -> operational logic void smash() { emit smashed(); } void repair() { emit repaired(); } signals: void lightsChanged(Lights lights); void smashed(); void repaired(); };
The GlueInterface class provides the following interface methods:
| Method | Purpose |
|---|---|
setLights() | Updates traffic light state from TaskTree recipe |
lightsChanged() | Notifies UI when light states change |
smash()/ repair() | Handles fault simulation and recovery actions |
When users interact with the UI:
- Clicking pause triggers
smash(), simulating a fault. - Clicking play triggers
repair(), restoring normal operation.
Implementing the state machine
The state machine logic is implemented through a TaskTree recipe. The recipe() method creates this logic using a reference to GlueInterface:
ExecutableItem recipe(GlueInterface &iface) { return Forever { Group { // "working" state stopOnSuccess, parallel, signalAwaiterTask(&iface, &GlueInterface::smashed), // transitions to the "broken" state Forever { switchLightsTask(&iface, Light::Red, 3s), // "red" state switchLightsTask(&iface, Light::Red | Light::Yellow, 1s), // "redGoingGreen" state switchLightsTask(&iface, Light::Green, 3s), // "green" state switchLightsTask(&iface, Light::Yellow, 1s), // "greenGoingRed" state } }, Group { // "broken" state stopOnSuccess, parallel, signalAwaiterTask(&iface, &GlueInterface::repaired), // transitions to the "working" state Forever { switchLightsTask(&iface, Light::Yellow, 1s), // "blinking" state switchLightsTask(&iface, Light::Off, 1s), // "unblinking" state } } }; }
Root state management
The top level Forever item executes its children in an infinite loop. It contains two subgroups representing working and broken states.
State transition rules:
- States execute in sequence, because the default execution mode is sequential.
- When one state completes, the other begins.
- Transitions are triggered when a user clicks the pause or play button in the UI, emitting the corresponding signal.
The "working" operation state
The working Group consists of two tasks running in parallel:
- The signalAwaiterTask connects to the
GlueInterface::smashedsignal. If the signal is triggered, the task immediately finishes with DoneResult::Success. This causes the working Group to stop executing, and the recipe transitions to the broken state. - The nested Forever task consist of four
switchLightsTasktasks ("red", "redGoingGreen", "green", and "greenGoingRed") executed sequentially in an infinite loop.
The stopOnSuccess item of each group instructs it to stop executing when any of its child tasks finishes with DoneResult::Success.
The switchLightsTask implementation:
- Updates light states through
GlueInterface::setLights(). - Creates a timed delay using timeoutTask.
- Maintains state for the specified duration
timeout.
static Group switchLightsTask(GlueInterface *iface, Lights lights, const milliseconds &timeout) { return { onGroupSetup([iface, lights] { iface->setLights(lights); }), timeoutTask(timeout, DoneResult::Success) }; }
The "broken" operation state
The broken Group is similar to the working Group. The only difference is that it connects to the GlueInterface::repaired signal, and the lights cycle only between 2 states toggling the yellow light on and off.
Application setup
The main application setup creates and connects all components:
int main(int argc, char **argv) { QApplication app(argc, argv); GlueInterface iface; QTaskTree taskTree({recipe(iface)}); TrafficLight widget(iface); widget.show(); taskTree.start(); return app.exec(); }
The main() function instantiates QApplication, GlueInterface, QTaskTree and TrafficLight objects.
To construct the taskTree object, we pass a recipe() constructed with the iface object. The widget also uses the passed iface object.
Before executing the application, the main() function shows the TrafficLight widget and starts the task tree.
Running the Example
To run the example from Qt Creator, open the Welcome mode and select the example from Examples. For more information, see Qt Creator: Tutorial: Build and run.
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.