移动块示例#
移动块示例展示了如何使用自定义过渡的 QStateMachine 在 QGraphicsScene 中对项目进行动画处理。
# 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())