forked from PyQt5/PyQt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSlidingStackedWidget.py
238 lines (205 loc) · 7.82 KB
/
SlidingStackedWidget.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Created on 2018年11月24日
author: Irony
site: https://pyqt5.com , https://github.com/892768447
email: [email protected]
file:
description: 参考 http://qt.shoutwiki.com/wiki/Extending_QStackedWidget_for_sliding_page_animations_in_Qt
"""
from PyQt5.QtCore import Qt, pyqtProperty, QEasingCurve, QPoint, \
QPropertyAnimation, QParallelAnimationGroup, QTimer
from PyQt5.QtWidgets import QStackedWidget
__Author__ = """By: Irony
QQ: 892768447
Email: [email protected]"""
__Copyright__ = 'Copyright (c) 2018 Irony'
__Version__ = 1.0
class SlidingStackedWidget(QStackedWidget):
LEFT2RIGHT, RIGHT2LEFT, TOP2BOTTOM, BOTTOM2TOP, AUTOMATIC = range(5)
def __init__(self, *args, **kwargs):
super(SlidingStackedWidget, self).__init__(*args, **kwargs)
self._pnow = QPoint(0, 0)
# 动画速度
self._speed = 500
# 当前索引
self._now = 0
# 自动模式的当前索引
self._current = 0
# 下一个索引
self._next = 0
# 是否激活
self._active = 0
# 动画方向(默认是横向)
self._orientation = Qt.Horizontal
# 动画曲线类型
self._easing = QEasingCurve.Linear
# 初始化动画
self._initAnimation()
def setSpeed(self, speed=500):
"""设置动画速度
:param speed: 速度值,默认值为500
:type speed: int
"""
self._speed = speed
@pyqtProperty(int, fset=setSpeed)
def speed(self):
return self._speed
def setOrientation(self, orientation=Qt.Horizontal):
"""设置动画的方向(横向和纵向)
:param orientation: 方向(Qt.Horizontal或Qt.Vertical)
:type orientation: http://doc.qt.io/qt-5/qt.html#Orientation-enum
"""
self._orientation = orientation
@pyqtProperty(int, fset=setOrientation)
def orientation(self):
return self._orientation
def setEasing(self, easing=QEasingCurve.OutBack):
"""设置动画的曲线类型
:param easing: 默认为QEasingCurve.OutBack
:type easing: http://doc.qt.io/qt-5/qeasingcurve.html#Type-enum
"""
self._easing = easing
@pyqtProperty(int, fset=setEasing)
def easing(self):
return self._easing
def slideInNext(self):
"""滑动到下一页"""
now = self.currentIndex()
if now < self.count() - 1:
self.slideInIdx(now + 1)
self._current = now + 1
def slideInPrev(self):
"""滑动到上一页"""
now = self.currentIndex()
if now > 0:
self.slideInIdx(now - 1)
self._current = now - 1
def slideInIdx(self, idx, direction=4):
"""滑动到指定序号
:param idx: 序号
:type idx: int
:param direction: 方向,默认是自动AUTOMATIC=4
:type direction: int
"""
if idx > self.count() - 1:
direction = self.TOP2BOTTOM if self._orientation == Qt.Vertical else self.RIGHT2LEFT
idx = idx % self.count()
elif idx < 0:
direction = self.BOTTOM2TOP if self._orientation == Qt.Vertical else self.LEFT2RIGHT
idx = (idx + self.count()) % self.count()
self.slideInWgt(self.widget(idx), direction)
def slideInWgt(self, widget, direction):
"""滑动到指定的widget
:param widget: QWidget, QLabel, etc...
:type widget: QWidget Base Class
:param direction: 方向
:type direction: int
"""
if self._active:
return
self._active = 1
_now = self.currentIndex()
_next = self.indexOf(widget)
if _now == next:
self._active = 0
return
w_now = self.widget(_now)
w_next = self.widget(_next)
# 自动判断方向
if _now < _next:
directionhint = self.TOP2BOTTOM if self._orientation == Qt.Vertical else self.RIGHT2LEFT
else:
directionhint = self.BOTTOM2TOP if self._orientation == Qt.Vertical else self.LEFT2RIGHT
if direction == self.AUTOMATIC:
direction = directionhint
# 计算偏移量
offsetX = self.frameRect().width()
offsetY = self.frameRect().height()
w_next.setGeometry(0, 0, offsetX, offsetY)
if direction == self.BOTTOM2TOP:
offsetX = 0
offsetY = -offsetY
elif direction == self.TOP2BOTTOM:
offsetX = 0
elif direction == self.RIGHT2LEFT:
offsetX = -offsetX
offsetY = 0
elif direction == self.LEFT2RIGHT:
offsetY = 0
# 重新定位显示区域外部/旁边的下一个窗口小部件
pnext = w_next.pos()
pnow = w_now.pos()
self._pnow = pnow
# 移动到指定位置并显示
w_next.move(pnext.x() - offsetX, pnext.y() - offsetY)
w_next.show()
w_next.raise_()
self._animnow.setTargetObject(w_now)
self._animnow.setDuration(self._speed)
self._animnow.setEasingCurve(self._easing)
self._animnow.setStartValue(QPoint(pnow.x(), pnow.y()))
self._animnow.setEndValue(
QPoint(offsetX + pnow.x(), offsetY + pnow.y()))
self._animnext.setTargetObject(w_next)
self._animnext.setDuration(self._speed)
self._animnext.setEasingCurve(self._easing)
self._animnext.setStartValue(
QPoint(-offsetX + pnext.x(), offsetY + pnext.y()))
self._animnext.setEndValue(QPoint(pnext.x(), pnext.y()))
self._next = _next
self._now = _now
self._active = 1
self._animgroup.start()
def _initAnimation(self):
"""初始化当前页和下一页的动画变量"""
# 当前页的动画
self._animnow = QPropertyAnimation(
self, propertyName=b'pos', duration=self._speed,
easingCurve=self._easing)
# 下一页的动画
self._animnext = QPropertyAnimation(
self, propertyName=b'pos', duration=self._speed,
easingCurve=self._easing)
# 并行动画组
self._animgroup = QParallelAnimationGroup(
self, finished=self.animationDoneSlot)
self._animgroup.addAnimation(self._animnow)
self._animgroup.addAnimation(self._animnext)
def setCurrentIndex(self, index):
# 覆盖该方法实现的动画切换
# super(SlidingStackedWidget, self).setCurrentIndex(index)
# 坚决不能调用上面的函数,否则动画失效
self.slideInIdx(index)
def setCurrentWidget(self, widget):
# 覆盖该方法实现的动画切换
super(SlidingStackedWidget, self).setCurrentWidget(widget)
# 坚决不能调用上面的函数,否则动画失效
self.setCurrentIndex(self.indexOf(widget))
def animationDoneSlot(self):
"""动画结束处理函数"""
# 由于重写了setCurrentIndex方法所以这里要用父类本身的方法
# self.setCurrentIndex(self._next)
QStackedWidget.setCurrentIndex(self, self._next)
w = self.widget(self._now)
w.hide()
w.move(self._pnow)
self._active = 0
def autoStop(self):
"""停止自动播放"""
if hasattr(self, '_autoTimer'):
self._autoTimer.stop()
def autoStart(self, msec=3000):
"""自动轮播
:param time: 时间, 默认3000, 3秒
"""
if not hasattr(self, '_autoTimer'):
self._autoTimer = QTimer(self, timeout=self._autoStart)
self._autoTimer.stop()
self._autoTimer.start(msec)
def _autoStart(self):
if self._current == self.count():
self._current = 0
self._current += 1
self.setCurrentIndex(self._current)