aboutsummaryrefslogtreecommitdiffstats
path: root/examples/statemachine
diff options
context:
space:
mode:
Diffstat (limited to 'examples/statemachine')
-rw-r--r--examples/statemachine/moveblocks/doc/moveblocks.pngbin0 -> 4532 bytes
-rw-r--r--examples/statemachine/moveblocks/doc/moveblocks.rst10
-rw-r--r--examples/statemachine/moveblocks/moveblocks.py222
-rw-r--r--examples/statemachine/moveblocks/moveblocks.pyproject3
-rw-r--r--examples/statemachine/ping_pong/ping_pong.py70
-rw-r--r--examples/statemachine/ping_pong/ping_pong.pyproject3
-rw-r--r--examples/statemachine/rogue/rogue.py179
-rw-r--r--examples/statemachine/rogue/rogue.pyproject3
-rw-r--r--examples/statemachine/trafficlight/doc/trafficlight.pngbin0 -> 4786 bytes
-rw-r--r--examples/statemachine/trafficlight/doc/trafficlight.rst10
-rw-r--r--examples/statemachine/trafficlight/trafficlight.py117
-rw-r--r--examples/statemachine/trafficlight/trafficlight.pyproject3
12 files changed, 620 insertions, 0 deletions
diff --git a/examples/statemachine/moveblocks/doc/moveblocks.png b/examples/statemachine/moveblocks/doc/moveblocks.png
new file mode 100644
index 000000000..56353d181
--- /dev/null
+++ b/examples/statemachine/moveblocks/doc/moveblocks.png
Binary files differ
diff --git a/examples/statemachine/moveblocks/doc/moveblocks.rst b/examples/statemachine/moveblocks/doc/moveblocks.rst
new file mode 100644
index 000000000..4cffd3b85
--- /dev/null
+++ b/examples/statemachine/moveblocks/doc/moveblocks.rst
@@ -0,0 +1,10 @@
+Move Blocks Example
+===================
+
+The Move Blocks example shows how to animate items in a QGraphicsScene
+using a QStateMachine with a custom transition.
+
+
+.. image:: moveblocks.png
+ :width: 400
+ :alt: Move Blocks Screenshot
diff --git a/examples/statemachine/moveblocks/moveblocks.py b/examples/statemachine/moveblocks/moveblocks.py
new file mode 100644
index 000000000..0d52c5b00
--- /dev/null
+++ b/examples/statemachine/moveblocks/moveblocks.py
@@ -0,0 +1,222 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the examples/statemachine/moveblocks example from Qt v6.x"""
+
+import sys
+
+from PySide6.QtCore import (QAbstractAnimation, QEasingCurve, QEvent, QObject,
+ QParallelAnimationGroup, QPropertyAnimation,
+ QRandomGenerator, QRect, QSequentialAnimationGroup,
+ Qt, QTimer)
+from PySide6.QtGui import QPainter, QResizeEvent
+from PySide6.QtWidgets import (QApplication, QGraphicsView, QGraphicsScene,
+ QGraphicsWidget, QStyleOptionGraphicsItem,
+ QWidget)
+from PySide6.QtStateMachine import (QAbstractTransition, QState, QStateMachine)
+
+
+StateSwitchType = QEvent.Type(QEvent.Type.User + 256)
+
+
+class StateSwitchEvent(QEvent):
+ def __init__(self, rand: int = 0) -> None:
+ super().__init__(StateSwitchType)
+ self._rand = rand
+
+ def rand(self) -> int:
+ return self._rand
+
+
+class QGraphicsRectWidget(QGraphicsWidget):
+ def __init__(self):
+ super().__init__()
+
+ def paint(self, painter: QPainter,
+ option: QStyleOptionGraphicsItem, widget: QWidget | None = None):
+ painter.fillRect(self.rect(), Qt.blue)
+
+
+class StateSwitchTransition(QAbstractTransition):
+ def __init__(self, rand: int = 0) -> None:
+ super().__init__()
+ self._rand = rand
+
+ def eventTest(self, event: QEvent) -> bool:
+ return event.type() == StateSwitchType and event.rand() == self._rand
+
+ def onTransition(self, event: QEvent):
+ pass
+
+
+class StateSwitcher(QState):
+ def __init__(self, machine: QStateMachine) -> None:
+ super().__init__(machine)
+ self._state_count = 0
+ self._last_index = 0
+ self.rg = QRandomGenerator.global_()
+
+ def onEntry(self, event: QEvent) -> None:
+ while True:
+ n = int(self.rg.bounded(self._state_count)) + 1
+ if n != self._last_index:
+ break
+ self._last_index = n
+ self.event = StateSwitchEvent(n)
+ self.machine().postEvent(self.event)
+
+ def onExit(self, event: QEvent) -> None:
+ pass
+
+ def addState(self, state: QState, animation: QAbstractAnimation) -> None:
+ self._state_count += 1
+ trans = StateSwitchTransition(self._state_count)
+ trans.setTargetState(state)
+ self.addTransition(trans)
+ trans.addAnimation(animation)
+
+
+def createGeometryState(w1: QObject, rect1: QRect,
+ w2: QObject, rect2: QRect,
+ w3: QObject, rect3: QRect,
+ w4: QObject, rect4: QRect, parent: QState) -> QState:
+ result = QState(parent)
+ result.assignProperty(w1, "geometry", rect1)
+ result.assignProperty(w2, "geometry", rect2)
+ result.assignProperty(w3, "geometry", rect3)
+ result.assignProperty(w4, "geometry", rect4)
+
+ return result
+
+
+class GraphicsView(QGraphicsView):
+ def __init__(self, scene: QGraphicsScene, parent: QWidget | None = None):
+ super().__init__(scene, parent)
+
+ def resizeEvent(self, event: QResizeEvent) -> None:
+ self.fitInView(self.sceneRect())
+ super().resizeEvent(event)
+
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+
+ button1, button2 = QGraphicsRectWidget(), QGraphicsRectWidget()
+ button3, button4 = QGraphicsRectWidget(), QGraphicsRectWidget()
+
+ button2.setZValue(1)
+ button3.setZValue(2)
+ button4.setZValue(3)
+
+ scene = QGraphicsScene(0, 0, 300, 300)
+ scene.setBackgroundBrush(Qt.black)
+ scene.addItem(button1)
+ scene.addItem(button2)
+ scene.addItem(button3)
+ scene.addItem(button4)
+
+ window = GraphicsView(scene)
+ window.setFrameStyle(0)
+ window.setAlignment(Qt.AlignLeft | Qt.AlignTop)
+ window.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ window.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+
+ machine = QStateMachine()
+
+ group = QState()
+ group.setObjectName("group")
+ timer = QTimer()
+ timer.setInterval(1250)
+ timer.setSingleShot(True)
+
+ group.entered.connect(timer.start)
+
+ state1, state2, state3 = QState(), QState(), QState()
+ state4, state5, state6 = QState(), QState(), QState()
+ state7 = QState()
+
+ state1 = createGeometryState(button1, QRect(100, 0, 50, 50),
+ button2, QRect(150, 0, 50, 50),
+ button3, QRect(200, 0, 50, 50),
+ button4, QRect(250, 0, 50, 50),
+ group)
+ state2 = createGeometryState(button1, QRect(250, 100, 50, 50),
+ button2, QRect(250, 150, 50, 50),
+ button3, QRect(250, 200, 50, 50),
+ button4, QRect(250, 250, 50, 50),
+ group)
+ state3 = createGeometryState(button1, QRect(150, 250, 50, 50),
+ button2, QRect(100, 250, 50, 50),
+ button3, QRect(50, 250, 50, 50),
+ button4, QRect(0, 250, 50, 50),
+ group)
+ state4 = createGeometryState(button1, QRect(0, 150, 50, 50),
+ button2, QRect(0, 100, 50, 50),
+ button3, QRect(0, 50, 50, 50),
+ button4, QRect(0, 0, 50, 50),
+ group)
+ state5 = createGeometryState(button1, QRect(100, 100, 50, 50),
+ button2, QRect(150, 100, 50, 50),
+ button3, QRect(100, 150, 50, 50),
+ button4, QRect(150, 150, 50, 50),
+ group)
+ state6 = createGeometryState(button1, QRect(50, 50, 50, 50),
+ button2, QRect(200, 50, 50, 50),
+ button3, QRect(50, 200, 50, 50),
+ button4, QRect(200, 200, 50, 50),
+ group)
+ state7 = createGeometryState(button1, QRect(0, 0, 50, 50),
+ button2, QRect(250, 0, 50, 50),
+ button3, QRect(0, 250, 50, 50),
+ button4, QRect(250, 250, 50, 50),
+ group)
+ group.setInitialState(state1)
+
+ animation_group = QParallelAnimationGroup()
+ sub_group = QSequentialAnimationGroup()
+
+ anim = QPropertyAnimation(button4, b"geometry")
+ anim.setDuration(1000)
+ anim.setEasingCurve(QEasingCurve.OutElastic)
+ animation_group.addAnimation(anim)
+
+ sub_group = QSequentialAnimationGroup(animation_group)
+ sub_group.addPause(100)
+ anim = QPropertyAnimation(button3, b"geometry")
+ anim.setDuration(1000)
+ anim.setEasingCurve(QEasingCurve.OutElastic)
+ sub_group.addAnimation(anim)
+
+ sub_group = QSequentialAnimationGroup(animation_group)
+ sub_group.addPause(150)
+ anim = QPropertyAnimation(button2, b"geometry")
+ anim.setDuration(1000)
+ anim.setEasingCurve(QEasingCurve.OutElastic)
+ sub_group.addAnimation(anim)
+
+ sub_group = QSequentialAnimationGroup(animation_group)
+ sub_group.addPause(200)
+ anim = QPropertyAnimation(button1, b"geometry")
+ anim.setDuration(1000)
+ anim.setEasingCurve(QEasingCurve.OutElastic)
+ sub_group.addAnimation(anim)
+
+ state_switcher = StateSwitcher(machine)
+ state_switcher.setObjectName("state_switcher")
+ group.addTransition(timer.timeout, state_switcher)
+ state_switcher.addState(state1, animation_group)
+ state_switcher.addState(state2, animation_group)
+ state_switcher.addState(state3, animation_group)
+ state_switcher.addState(state4, animation_group)
+ state_switcher.addState(state5, animation_group)
+ state_switcher.addState(state6, animation_group)
+ state_switcher.addState(state7, animation_group)
+
+ machine.addState(group)
+ machine.setInitialState(group)
+ machine.start()
+
+ window.resize(300, 300)
+ window.show()
+
+ sys.exit(app.exec())
diff --git a/examples/statemachine/moveblocks/moveblocks.pyproject b/examples/statemachine/moveblocks/moveblocks.pyproject
new file mode 100644
index 000000000..8c3eff508
--- /dev/null
+++ b/examples/statemachine/moveblocks/moveblocks.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["moveblocks.py"]
+}
diff --git a/examples/statemachine/ping_pong/ping_pong.py b/examples/statemachine/ping_pong/ping_pong.py
new file mode 100644
index 000000000..d5c18eb28
--- /dev/null
+++ b/examples/statemachine/ping_pong/ping_pong.py
@@ -0,0 +1,70 @@
+# Copyright (C) 2010 velociraptor Genjix <aphidia@hotmail.com>
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+
+from PySide6.QtCore import QCoreApplication, QEvent
+from PySide6.QtStateMachine import QAbstractTransition, QState, QStateMachine
+
+
+class PingEvent(QEvent):
+ def __init__(self):
+ super().__init__(QEvent.Type(QEvent.User + 2))
+
+
+class PongEvent(QEvent):
+ def __init__(self):
+ super().__init__(QEvent.Type(QEvent.User + 3))
+
+
+class Pinger(QState):
+ def __init__(self, parent):
+ super().__init__(parent)
+
+ def onEntry(self, e):
+ self.p = PingEvent()
+ self.machine().postEvent(self.p)
+ print('ping?')
+
+
+class PongTransition(QAbstractTransition):
+ def eventTest(self, e):
+ return e.type() == QEvent.User + 3
+
+ def onTransition(self, e):
+ self.p = PingEvent()
+ machine.postDelayedEvent(self.p, 500)
+ print('ping?')
+
+
+class PingTransition(QAbstractTransition):
+ def eventTest(self, e):
+ return e.type() == QEvent.User + 2
+
+ def onTransition(self, e):
+ self.p = PongEvent()
+ machine.postDelayedEvent(self.p, 500)
+ print('pong!')
+
+
+if __name__ == '__main__':
+ app = QCoreApplication(sys.argv)
+
+ machine = QStateMachine()
+ group = QState(QState.ParallelStates)
+ group.setObjectName('group')
+
+ pinger = Pinger(group)
+ pinger.setObjectName('pinger')
+ pinger.addTransition(PongTransition())
+
+ ponger = QState(group)
+ ponger.setObjectName('ponger')
+ ponger.addTransition(PingTransition())
+
+ machine.addState(group)
+ machine.setInitialState(group)
+ machine.start()
+
+ sys.exit(app.exec())
diff --git a/examples/statemachine/ping_pong/ping_pong.pyproject b/examples/statemachine/ping_pong/ping_pong.pyproject
new file mode 100644
index 000000000..7fb430352
--- /dev/null
+++ b/examples/statemachine/ping_pong/ping_pong.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["ping_pong.py"]
+}
diff --git a/examples/statemachine/rogue/rogue.py b/examples/statemachine/rogue/rogue.py
new file mode 100644
index 000000000..f0ce9c28c
--- /dev/null
+++ b/examples/statemachine/rogue/rogue.py
@@ -0,0 +1,179 @@
+# Copyright (C) 2010 velociraptor Genjix <aphidia@hotmail.com>
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+
+from PySide6.QtCore import (QEvent, QPoint, QRandomGenerator, QSize, Qt,
+ Property)
+from PySide6.QtGui import QFont, QFontMetrics, QFontDatabase, QPainter
+from PySide6.QtWidgets import QApplication, QMainWindow
+from PySide6.QtStateMachine import (QEventTransition, QFinalState,
+ QKeyEventTransition, QState, QStateMachine)
+
+
+class MovementTransition(QEventTransition):
+ def __init__(self, window):
+ super().__init__(window, QEvent.KeyPress)
+ self.window = window
+
+ def eventTest(self, event):
+ if (event.type() == QEvent.StateMachineWrapped
+ and event.event().type() == QEvent.KeyPress):
+ key = event.event().key()
+ return (key == Qt.Key_2 or key == Qt.Key_8
+ or key == Qt.Key_6 or key == Qt.Key_4)
+ return False
+
+ def onTransition(self, event):
+ key = event.event().key()
+ if key == Qt.Key_4:
+ self.window.move_player(self.window.left)
+ if key == Qt.Key_8:
+ self.window.move_player(self.window.Up)
+ if key == Qt.Key_6:
+ self.window.move_player(self.window.right)
+ if key == Qt.Key_2:
+ self.window.move_player(self.window.down)
+
+
+class Custom(QState):
+ def __init__(self, parent, mw):
+ super().__init__(parent)
+ self.mw = mw
+
+ def onEntry(self, e):
+ print(self.mw.status)
+
+
+class MainWindow(QMainWindow):
+ def __init__(self):
+ super().__init__()
+ self.pX = 5
+ self.pY = 5
+ self.width = 35
+ self.height = 20
+ self._status_str = ''
+
+ font = QFont()
+ if 'Monospace' in QFontDatabase.families():
+ font = QFont('Monospace', 12)
+ else:
+ for family in QFontDatabase.families():
+ if QFontDatabase.isFixedPitch(family):
+ font = QFont(family, 12)
+ self.setFont(font)
+
+ self.setup_map()
+ self.build_machine()
+ self.show()
+
+ def setup_map(self):
+ self.map = []
+ generator = QRandomGenerator().global_()
+ for x in range(self.width):
+ column = []
+ for y in range(self.height):
+ if (x == 0 or x == self.width - 1 or y == 0
+ or y == self.height - 1 or generator.bounded(0, 40) == 0):
+ column.append('#')
+ else:
+ column.append('.')
+ self.map.append(column)
+
+ def build_machine(self):
+ machine = QStateMachine(self)
+
+ input_state = Custom(machine, self)
+ # this line sets the status
+ self.status = 'hello!'
+ # however this line does not
+ input_state.assignProperty(self, 'status', 'Move the rogue with 2, 4, 6, and 8')
+
+ machine.setInitialState(input_state)
+ machine.start()
+
+ transition = MovementTransition(self)
+ input_state.addTransition(transition)
+
+ quit_state = QState(machine)
+ quit_state.assignProperty(self, 'status', 'Really quit(y/n)?')
+
+ yes_transition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Y)
+ self._final_state = QFinalState(machine)
+ yes_transition.setTargetState(self._final_state)
+ quit_state.addTransition(yes_transition)
+
+ no_transition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_N)
+ no_transition.setTargetState(input_state)
+ quit_state.addTransition(no_transition)
+
+ quit_transition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Q)
+ quit_transition.setTargetState(quit_state)
+ input_state.addTransition(quit_transition)
+
+ machine.setInitialState(input_state)
+ machine.finished.connect(qApp.quit) # noqa: F821
+ machine.start()
+
+ def sizeHint(self):
+ metrics = QFontMetrics(self.font())
+ return QSize(metrics.horizontalAdvance('X') * self.width,
+ metrics.height() * (self.height + 1))
+
+ def paintEvent(self, event):
+ metrics = QFontMetrics(self.font())
+ with QPainter(self) as painter:
+ font_height = metrics.height()
+ font_width = metrics.horizontalAdvance('X')
+
+ painter.fillRect(self.rect(), Qt.black)
+ painter.setPen(Qt.white)
+
+ y_pos = font_height
+ painter.drawText(QPoint(0, y_pos), self.status)
+ for y in range(self.height):
+ y_pos += font_height
+ x_pos = 0
+ for x in range(self.width):
+ if y == self.pY and x == self.pX:
+ x_pos += font_width
+ continue
+ painter.drawText(QPoint(x_pos, y_pos), self.map[x][y])
+ x_pos += font_width
+ painter.drawText(QPoint(self.pX * font_width, (self.pY + 2) * font_height), '@')
+
+ def move_player(self, direction):
+ if direction == self.left:
+ if self.map[self.pX - 1][self.pY] != '#':
+ self.pX -= 1
+ elif direction == self.right:
+ if self.map[self.pX + 1][self.pY] != '#':
+ self.pX += 1
+ elif direction == self.Up:
+ if self.map[self.pX][self.pY - 1] != '#':
+ self.pY -= 1
+ elif direction == self.down:
+ if self.map[self.pX][self.pY + 1] != '#':
+ self.pY += 1
+ self.repaint()
+
+ def get_status(self):
+ return self._status_str
+
+ def set_status(self, status):
+ self._status_str = status
+ self.repaint()
+ status = Property(str, get_status, set_status)
+ Up = 0
+ down = 1
+ left = 2
+ right = 3
+ width = 35
+ height = 20
+
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ main_win = MainWindow()
+ sys.exit(app.exec())
diff --git a/examples/statemachine/rogue/rogue.pyproject b/examples/statemachine/rogue/rogue.pyproject
new file mode 100644
index 000000000..b8baf9802
--- /dev/null
+++ b/examples/statemachine/rogue/rogue.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["rogue.py"]
+}
diff --git a/examples/statemachine/trafficlight/doc/trafficlight.png b/examples/statemachine/trafficlight/doc/trafficlight.png
new file mode 100644
index 000000000..24d40beec
--- /dev/null
+++ b/examples/statemachine/trafficlight/doc/trafficlight.png
Binary files differ
diff --git a/examples/statemachine/trafficlight/doc/trafficlight.rst b/examples/statemachine/trafficlight/doc/trafficlight.rst
new file mode 100644
index 000000000..57d369465
--- /dev/null
+++ b/examples/statemachine/trafficlight/doc/trafficlight.rst
@@ -0,0 +1,10 @@
+Traffic Light Example
+=====================
+
+The Traffic Light example shows how to use The State Machine Framework to
+implement the control flow of a traffic light.
+
+
+.. image:: trafficlight.png
+ :width: 400
+ :alt: Traffic Light Screenshot
diff --git a/examples/statemachine/trafficlight/trafficlight.py b/examples/statemachine/trafficlight/trafficlight.py
new file mode 100644
index 000000000..1e58384f9
--- /dev/null
+++ b/examples/statemachine/trafficlight/trafficlight.py
@@ -0,0 +1,117 @@
+# Copyright (C) 2010 velociraptor Genjix <aphidia@hotmail.com>
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+
+from PySide6.QtCore import QTimer, Qt, Property, Slot
+from PySide6.QtGui import QPainter, QPalette
+from PySide6.QtWidgets import QApplication, QVBoxLayout, QWidget
+from PySide6.QtStateMachine import QFinalState, QState, QStateMachine
+
+
+class LightWidget(QWidget):
+ def __init__(self, color):
+ super().__init__()
+ self.color = color
+ self._on_val = False
+
+ def is_on(self):
+ return self._on_val
+
+ def set_on(self, on):
+ if self._on_val == on:
+ return
+ self._on_val = on
+ self.update()
+
+ @Slot()
+ def turn_off(self):
+ self.set_on(False)
+
+ @Slot()
+ def turn_on(self):
+ self.set_on(True)
+
+ def paintEvent(self, e):
+ if not self._on_val:
+ return
+ with QPainter(self) as painter:
+ painter.setRenderHint(QPainter.Antialiasing)
+ painter.setBrush(self.color)
+ painter.drawEllipse(0, 0, self.width(), self.height())
+
+ on = Property(bool, is_on, set_on)
+
+
+class TrafficLightWidget(QWidget):
+ def __init__(self):
+ super().__init__()
+ vbox = QVBoxLayout(self)
+ self._red_light = LightWidget(Qt.red)
+ vbox.addWidget(self._red_light)
+ self._yellow_light = LightWidget(Qt.yellow)
+ vbox.addWidget(self._yellow_light)
+ self._green_light = LightWidget(Qt.green)
+ vbox.addWidget(self._green_light)
+ pal = QPalette()
+ pal.setColor(QPalette.Window, Qt.black)
+ self.setPalette(pal)
+ self.setAutoFillBackground(True)
+
+
+def create_light_state(light, duration, parent=None):
+ light_state = QState(parent)
+ timer = QTimer(light_state)
+ timer.setInterval(duration)
+ timer.setSingleShot(True)
+ timing = QState(light_state)
+ timing.entered.connect(light.turn_on)
+ timing.entered.connect(timer.start)
+ timing.exited.connect(light.turn_off)
+ done = QFinalState(light_state)
+ timing.addTransition(timer.timeout, done)
+ light_state.setInitialState(timing)
+ return light_state
+
+
+class TrafficLight(QWidget):
+ def __init__(self):
+ super().__init__()
+ vbox = QVBoxLayout(self)
+ widget = TrafficLightWidget()
+ vbox.addWidget(widget)
+ vbox.setContentsMargins(0, 0, 0, 0)
+
+ machine = QStateMachine(self)
+ red_going_yellow = create_light_state(widget._red_light, 1000)
+ red_going_yellow.setObjectName('redGoingYellow')
+ yellow_going_green = create_light_state(widget._red_light, 1000)
+ yellow_going_green.setObjectName('yellowGoingGreen')
+ red_going_yellow.addTransition(red_going_yellow.finished,
+ yellow_going_green)
+ green_going_yellow = create_light_state(widget._yellow_light, 3000)
+ green_going_yellow.setObjectName('greenGoingYellow')
+ yellow_going_green.addTransition(yellow_going_green.finished,
+ green_going_yellow)
+ yellow_going_red = create_light_state(widget._green_light, 1000)
+ yellow_going_red.setObjectName('yellowGoingRed')
+ green_going_yellow.addTransition(green_going_yellow.finished,
+ yellow_going_red)
+ yellow_going_red.addTransition(yellow_going_red.finished,
+ red_going_yellow)
+
+ machine.addState(red_going_yellow)
+ machine.addState(yellow_going_green)
+ machine.addState(green_going_yellow)
+ machine.addState(yellow_going_red)
+ machine.setInitialState(red_going_yellow)
+ machine.start()
+
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ widget = TrafficLight()
+ widget.resize(110, 300)
+ widget.show()
+ sys.exit(app.exec())
diff --git a/examples/statemachine/trafficlight/trafficlight.pyproject b/examples/statemachine/trafficlight/trafficlight.pyproject
new file mode 100644
index 000000000..912472693
--- /dev/null
+++ b/examples/statemachine/trafficlight/trafficlight.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["trafficlight.py"]
+}