SCXML Traffic Light (Dynamic, QML)

A Qt Quick application that uses a dynamically loaded state machine to implement a traffic light.

Traffic light with green light illuminated

Traffic Light QML demonstrates how to connect to the active properties of a state in a dynamically loaded state machine.

The UI is created using Qt Quick.

Running the Example

You can run the example from:

Dynamically Loading the State Machine

We link against the Qt SCXML module by adding the following lines to the example's build files.

To .pro when using qmake:

 QT += qml scxml

To CMakeLists.txt when using cmake:

 find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Scxml)
 target_link_libraries(trafficlight-qml-dynamic PRIVATE
     Qt6::Core
     Qt6::Gui
     Qt6::Qml
     Qt6::Scxml
 )

We dynamically create the state machine in the main QML file:

 import QtScxml
 import TrafficLightApplication

 Window {
     width: lights.width
     height: lights.height
     visible: true

     Lights {
         id: lights
         stateMachine: loader.stateMachine
         // Suppress qmllint warning, dynamic statemachine properties not known at compile-time
         // qmllint disable missing-property
         button.source: stateMachine.working ? "pause.png" : "play.png"
         button.onClicked: stateMachine.submitEvent(stateMachine.working ? "smash" : "repair");
         // qmllint enable missing-property
     }

     StateMachineLoader {
         id: loader
         source: Qt.resolvedUrl("statemachine.scxml")
     }
 }

Connecting to States

In the SCXML file, we specify states for each light: red, yellow, and green. In the <onentry> element, we specify the event to send when entering the state and the delay in seconds before sending the event. In the <transition> element, we specify the event that triggers the transition to the state specified by the target attribute:

         <state id="red">
             <onentry>
                 <send event="startGoingGreen" delay="3s"/>
             </onentry>
             <transition event="startGoingGreen" target="redGoingGreen"/>
         </state>

         <state id="yellow" initial="greenGoingRed">
             <state id="redGoingGreen">
                 <onentry>
                     <send event="goGreen" delay="1s"/>
                 </onentry>
                 <transition event="goGreen" target="green"/>
             </state>

             <state id="greenGoingRed">
                 <onentry>
                     <send event="goRed" delay="1s"/>
                 </onentry>
                 <transition event="goRed" target="red"/>
             </state>
         </state>

         <state id="green">
             <onentry>
                 <send event="startGoingRed" delay="3s"/>
             </onentry>
             <transition event="startGoingRed" target="greenGoingRed"/>
         </state>

We connect to the states as follows:

     states: [
         // Suppress qmllint warning, dynamic statemachine properties not known at compile-time
         // qmllint disable missing-property
         State {
             name: "Red"
             when: lights.stateMachine.red

             PropertyChanges { redLight.opacity: 1 }
         },
         State {
             name: "RedGoingGreen"
             when: lights.stateMachine.redGoingGreen

             PropertyChanges { redLight.opacity: 1 }
             PropertyChanges { yellowLight.opacity: 1 }
         },
         State {
             name: "Yellow"
             when: lights.stateMachine.yellow || lights.stateMachine.blinking

             PropertyChanges { yellowLight.opacity: 1 }
         },
         State {
             name: "Green"
             when: lights.stateMachine.green

             PropertyChanges { greenLight.opacity: 1 }
         }
         // qmllint enable missing-property
     ]

Example project @ code.qt.io