aboutsummaryrefslogtreecommitdiffstats
path: root/examples/statemachine
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2022-09-15 13:21:53 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2022-09-16 10:30:36 +0200
commitcf32b66adbfb489cd6e5d5c0bf3f741b59ba204c (patch)
tree44be69c9487f5d4db1092d061a555bd6001c1ab4 /examples/statemachine
parentb20d6f6906f91f9df608d7800f4e27f7a7160abe (diff)
Move examples around
Change the directory structure to closer match that of Qt. Task-number: PYSIDE-841 Change-Id: I87aca346b6654aafe94dd1fb83c184c182ceb2e6 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
Diffstat (limited to 'examples/statemachine')
-rw-r--r--examples/statemachine/eventtrans/eventtrans.py57
-rw-r--r--examples/statemachine/eventtrans/eventtrans.pyproject3
-rw-r--r--examples/statemachine/factstates/factstates.py89
-rw-r--r--examples/statemachine/factstates/factstates.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 -> 79 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
-rw-r--r--examples/statemachine/twowaybutton/twowaybutton.py33
-rw-r--r--examples/statemachine/twowaybutton/twowaybutton.pyproject3
14 files changed, 573 insertions, 0 deletions
diff --git a/examples/statemachine/eventtrans/eventtrans.py b/examples/statemachine/eventtrans/eventtrans.py
new file mode 100644
index 000000000..b1c74a21f
--- /dev/null
+++ b/examples/statemachine/eventtrans/eventtrans.py
@@ -0,0 +1,57 @@
+# 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, QRect, Qt
+from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton
+from PySide6.QtStateMachine import QEventTransition, QState, QStateMachine
+
+
+class MainWindow(QMainWindow):
+ def __init__(self):
+ super().__init__()
+ button = QPushButton(self)
+ button.setGeometry(QRect(100, 100, 100, 100))
+
+ machine = QStateMachine(self)
+ s1 = QState()
+ s1.assignProperty(button, 'text', 'Outside')
+ s2 = QState()
+ s2.assignProperty(button, 'text', 'Inside')
+
+ enter_transition = QEventTransition(button, QEvent.Enter)
+ enter_transition.setTargetState(s2)
+ s1.addTransition(enter_transition)
+
+ leave_transition = QEventTransition(button, QEvent.Leave)
+ leave_transition.setTargetState(s1)
+ s2.addTransition(leave_transition)
+
+ s3 = QState()
+ s3.assignProperty(button, 'text', 'Pressing...')
+
+ press_transition = QEventTransition(button, QEvent.MouseButtonPress)
+ press_transition.setTargetState(s3)
+ s2.addTransition(press_transition)
+
+ release_transition = QEventTransition(button, QEvent.MouseButtonRelease)
+ release_transition.setTargetState(s2)
+ s3.addTransition(release_transition)
+
+ machine.addState(s1)
+ machine.addState(s2)
+ machine.addState(s3)
+
+ machine.setInitialState(s1)
+ machine.start()
+
+ self.setCentralWidget(button)
+ self.show()
+
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ main_win = MainWindow()
+ sys.exit(app.exec())
diff --git a/examples/statemachine/eventtrans/eventtrans.pyproject b/examples/statemachine/eventtrans/eventtrans.pyproject
new file mode 100644
index 000000000..b2f660a8f
--- /dev/null
+++ b/examples/statemachine/eventtrans/eventtrans.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["eventtrans.py"]
+}
diff --git a/examples/statemachine/factstates/factstates.py b/examples/statemachine/factstates/factstates.py
new file mode 100644
index 000000000..aacf8f19b
--- /dev/null
+++ b/examples/statemachine/factstates/factstates.py
@@ -0,0 +1,89 @@
+# 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, QObject, Qt, Property, Signal
+from PySide6.QtStateMachine import (QFinalState, QSignalTransition, QState,
+ QStateMachine)
+
+
+class Factorial(QObject):
+ x_changed = Signal(int)
+
+ def __init__(self):
+ super().__init__()
+ self.xval = -1
+ self.facval = 1
+
+ def get_x(self):
+ return self.xval
+
+ def set_x(self, x):
+ if self.xval == x:
+ return
+ self.xval = x
+ self.x_changed.emit(x)
+ x = Property(int, get_x, set_x)
+
+ def get_fact(self):
+ return self.facval
+
+ def set_fact(self, fac):
+ self.facval = fac
+
+ fac = Property(int, get_fact, set_fact)
+
+
+class FactorialLoopTransition(QSignalTransition):
+ def __init__(self, fact):
+ super().__init__(fact.x_changed)
+ self.fact = fact
+
+ def eventTest(self, e):
+ if not super(FactorialLoopTransition, self).eventTest(e):
+ return False
+ return e.arguments()[0] > 1
+
+ def onTransition(self, e):
+ x = e.arguments()[0]
+ fac = self.fact.fac
+ self.fact.fac = x * fac
+ self.fact.x = x - 1
+
+
+class FactorialDoneTransition(QSignalTransition):
+ def __init__(self, fact):
+ super().__init__(fact.x_changed)
+ self.fact = fact
+
+ def eventTest(self, e):
+ if not super(FactorialDoneTransition, self).eventTest(e):
+ return False
+ return e.arguments()[0] <= 1
+
+ def onTransition(self, e):
+ print(self.fact.fac)
+
+
+if __name__ == '__main__':
+ app = QCoreApplication(sys.argv)
+ factorial = Factorial()
+ machine = QStateMachine()
+
+ compute = QState(machine)
+ compute.assignProperty(factorial, 'fac', 1)
+ compute.assignProperty(factorial, 'x', 6)
+ compute.addTransition(FactorialLoopTransition(factorial))
+
+ done = QFinalState(machine)
+ done_transition = FactorialDoneTransition(factorial)
+ done_transition.setTargetState(done)
+ compute.addTransition(done_transition)
+
+ machine.setInitialState(compute)
+ machine.finished.connect(app.quit)
+ machine.start()
+
+ sys.exit(app.exec())
diff --git a/examples/statemachine/factstates/factstates.pyproject b/examples/statemachine/factstates/factstates.pyproject
new file mode 100644
index 000000000..751a5005b
--- /dev/null
+++ b/examples/statemachine/factstates/factstates.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["factstates.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..a43d4d1bc
--- /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)
+ 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..ec88a8e8b
--- /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"]
+}
diff --git a/examples/statemachine/twowaybutton/twowaybutton.py b/examples/statemachine/twowaybutton/twowaybutton.py
new file mode 100644
index 000000000..35a582f93
--- /dev/null
+++ b/examples/statemachine/twowaybutton/twowaybutton.py
@@ -0,0 +1,33 @@
+# 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.QtWidgets import QApplication, QPushButton
+from PySide6.QtStateMachine import QState, QStateMachine
+
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ button = QPushButton()
+ machine = QStateMachine()
+
+ off = QState()
+ off.assignProperty(button, 'text', 'Off')
+ off.setObjectName('off')
+
+ on = QState()
+ on.setObjectName('on')
+ on.assignProperty(button, 'text', 'On')
+
+ off.addTransition(button.clicked, on)
+ on.addTransition(button.clicked, off)
+
+ machine.addState(off)
+ machine.addState(on)
+ machine.setInitialState(off)
+ machine.start()
+ button.resize(100, 50)
+ button.show()
+ sys.exit(app.exec())
diff --git a/examples/statemachine/twowaybutton/twowaybutton.pyproject b/examples/statemachine/twowaybutton/twowaybutton.pyproject
new file mode 100644
index 000000000..223a51e32
--- /dev/null
+++ b/examples/statemachine/twowaybutton/twowaybutton.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["twowaybutton.py"]
+}