1#!/usr/bin/env python
2
3
4#############################################################################
5##
6## Copyright (C) 2013 Riverbank Computing Limited.
7## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
8## All rights reserved.
9##
10## This file is part of the examples of PyQt.
11##
12## $QT_BEGIN_LICENSE:BSD$
13## You may use this file under the terms of the BSD license as follows:
14##
15## "Redistribution and use in source and binary forms, with or without
16## modification, are permitted provided that the following conditions are
17## met:
18##   * Redistributions of source code must retain the above copyright
19##     notice, this list of conditions and the following disclaimer.
20##   * Redistributions in binary form must reproduce the above copyright
21##     notice, this list of conditions and the following disclaimer in
22##     the documentation and/or other materials provided with the
23##     distribution.
24##   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
25##     the names of its contributors may be used to endorse or promote
26##     products derived from this software without specific prior written
27##     permission.
28##
29## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
40## $QT_END_LICENSE$
41##
42#############################################################################
43
44
45from PyQt5.QtCore import (QAbstractTransition, QEasingCurve, QEvent,
46        QParallelAnimationGroup, QPropertyAnimation, qrand, QRect,
47        QSequentialAnimationGroup, qsrand, QState, QStateMachine, Qt, QTime,
48        QTimer)
49from PyQt5.QtWidgets import (QApplication, QGraphicsScene, QGraphicsView,
50        QGraphicsWidget)
51
52
53class StateSwitchEvent(QEvent):
54    StateSwitchType = QEvent.User + 256
55
56    def __init__(self, rand=0):
57        super(StateSwitchEvent, self).__init__(StateSwitchEvent.StateSwitchType)
58
59        self.m_rand = rand
60
61    def rand(self):
62        return self.m_rand
63
64
65class QGraphicsRectWidget(QGraphicsWidget):
66    def paint(self, painter, option, widget):
67        painter.fillRect(self.rect(), Qt.blue)
68
69
70class StateSwitchTransition(QAbstractTransition):
71    def __init__(self, rand):
72        super(StateSwitchTransition, self).__init__()
73
74        self.m_rand = rand
75
76    def eventTest(self, event):
77        return (event.type() == StateSwitchEvent.StateSwitchType and
78                event.rand() == self.m_rand)
79
80    def onTransition(self, event):
81        pass
82
83
84class StateSwitcher(QState):
85    def __init__(self, machine):
86        super(StateSwitcher, self).__init__(machine)
87
88        self.m_stateCount = 0
89        self.m_lastIndex = 0
90
91    def onEntry(self, event):
92        n = qrand() % self.m_stateCount + 1
93        while n == self.m_lastIndex:
94            n = qrand() % self.m_stateCount + 1
95
96        self.m_lastIndex = n
97        self.machine().postEvent(StateSwitchEvent(n))
98
99    def onExit(self, event):
100        pass
101
102    def addState(self, state, animation):
103        self.m_stateCount += 1
104        trans = StateSwitchTransition(self.m_stateCount)
105        trans.setTargetState(state)
106        self.addTransition(trans)
107        trans.addAnimation(animation)
108
109
110def createGeometryState(w1, rect1, w2, rect2, w3, rect3, w4, rect4, parent):
111    result = QState(parent)
112
113    result.assignProperty(w1, 'geometry', rect1)
114    result.assignProperty(w1, 'geometry', rect1)
115    result.assignProperty(w2, 'geometry', rect2)
116    result.assignProperty(w3, 'geometry', rect3)
117    result.assignProperty(w4, 'geometry', rect4)
118
119    return result
120
121
122if __name__ == '__main__':
123
124    import sys
125
126    app = QApplication(sys.argv)
127
128    button1 = QGraphicsRectWidget()
129    button2 = QGraphicsRectWidget()
130    button3 = QGraphicsRectWidget()
131    button4 = QGraphicsRectWidget()
132    button2.setZValue(1)
133    button3.setZValue(2)
134    button4.setZValue(3)
135
136    scene = QGraphicsScene(0, 0, 300, 300)
137    scene.setBackgroundBrush(Qt.black)
138    scene.addItem(button1)
139    scene.addItem(button2)
140    scene.addItem(button3)
141    scene.addItem(button4)
142
143    window = QGraphicsView(scene)
144    window.setFrameStyle(0)
145    window.setAlignment(Qt.AlignLeft | Qt.AlignTop)
146    window.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
147    window.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
148
149    machine = QStateMachine()
150
151    group = QState()
152    timer = QTimer()
153    timer.setInterval(1250)
154    timer.setSingleShot(True)
155    group.entered.connect(timer.start)
156
157    state1 = createGeometryState(button1, QRect(100, 0, 50, 50), button2,
158            QRect(150, 0, 50, 50), button3, QRect(200, 0, 50, 50), button4,
159            QRect(250, 0, 50, 50), group)
160
161    state2 = createGeometryState(button1, QRect(250, 100, 50, 50), button2,
162            QRect(250, 150, 50, 50), button3, QRect(250, 200, 50, 50), button4,
163            QRect(250, 250, 50, 50), group)
164
165    state3 = createGeometryState(button1, QRect(150, 250, 50, 50), button2,
166            QRect(100, 250, 50, 50), button3, QRect(50, 250, 50, 50), button4,
167            QRect(0, 250, 50, 50), group)
168
169    state4 = createGeometryState(button1, QRect(0, 150, 50, 50), button2,
170            QRect(0, 100, 50, 50), button3, QRect(0, 50, 50, 50), button4,
171            QRect(0, 0, 50, 50), group)
172
173    state5 = createGeometryState(button1, QRect(100, 100, 50, 50), button2,
174            QRect(150, 100, 50, 50), button3, QRect(100, 150, 50, 50), button4,
175            QRect(150, 150, 50, 50), group)
176
177    state6 = createGeometryState(button1, QRect(50, 50, 50, 50), button2,
178            QRect(200, 50, 50, 50), button3, QRect(50, 200, 50, 50), button4,
179            QRect(200, 200, 50, 50), group)
180
181    state7 = createGeometryState(button1, QRect(0, 0, 50, 50), button2,
182            QRect(250, 0, 50, 50), button3, QRect(0, 250, 50, 50), button4,
183            QRect(250, 250, 50, 50), group)
184
185    group.setInitialState(state1)
186
187    animationGroup = QParallelAnimationGroup()
188    anim = QPropertyAnimation(button4, b'geometry')
189    anim.setDuration(1000)
190    anim.setEasingCurve(QEasingCurve.OutElastic)
191    animationGroup.addAnimation(anim)
192
193    subGroup = QSequentialAnimationGroup(animationGroup)
194    subGroup.addPause(100)
195    anim = QPropertyAnimation(button3, b'geometry')
196    anim.setDuration(1000)
197    anim.setEasingCurve(QEasingCurve.OutElastic)
198    subGroup.addAnimation(anim)
199
200    subGroup = QSequentialAnimationGroup(animationGroup)
201    subGroup.addPause(150)
202    anim = QPropertyAnimation(button2, b'geometry')
203    anim.setDuration(1000)
204    anim.setEasingCurve(QEasingCurve.OutElastic)
205    subGroup.addAnimation(anim)
206
207    subGroup = QSequentialAnimationGroup(animationGroup)
208    subGroup.addPause(200)
209    anim = QPropertyAnimation(button1, b'geometry')
210    anim.setDuration(1000)
211    anim.setEasingCurve(QEasingCurve.OutElastic)
212    subGroup.addAnimation(anim)
213
214    stateSwitcher = StateSwitcher(machine)
215    group.addTransition(timer.timeout, stateSwitcher)
216    stateSwitcher.addState(state1, animationGroup)
217    stateSwitcher.addState(state2, animationGroup)
218    stateSwitcher.addState(state3, animationGroup)
219    stateSwitcher.addState(state4, animationGroup)
220    stateSwitcher.addState(state5, animationGroup)
221    stateSwitcher.addState(state6, animationGroup)
222    stateSwitcher.addState(state7, animationGroup)
223
224    machine.addState(group)
225    machine.setInitialState(group)
226    machine.start()
227
228    window.resize(300, 300)
229    window.show()
230
231    qsrand(QTime(0, 0, 0).secsTo(QTime.currentTime()))
232
233    sys.exit(app.exec_())
234