1
0
Fork 0
mirror of synced 2024-05-19 12:02:54 +12:00
Rare/rare/widgets/sliding_stack.py
2022-06-22 16:53:26 +03:00

139 lines
4 KiB
Python

from PyQt5.QtCore import (
pyqtSlot,
QEvent,
QEasingCurve,
QParallelAnimationGroup,
QAbstractAnimation,
QPropertyAnimation,
Qt,
QPoint,
)
from PyQt5.QtWidgets import QStackedWidget, QGestureEvent, QSwipeGesture
class SlidingStackedWidget(QStackedWidget):
"""
Taken from: https://stackoverflow.com/a/52597972
"""
def __init__(self, parent=None):
super(SlidingStackedWidget, self).__init__(parent)
self.m_direction = Qt.Horizontal
self.m_speed = 500
self.m_animationtype = QEasingCurve.OutBack
self.m_now = 0
self.m_next = 0
self.m_wrap = False
self.m_pnow = QPoint(0, 0)
self.m_active = False
def setDirection(self, direction: Qt.Orientation) -> None:
self.m_direction = direction
def setSpeed(self, speed: int) -> None:
self.m_speed = speed
def setAnimation(self, animationtype: QEasingCurve.Type) -> None:
self.m_animationtype = animationtype
def setWrap(self, wrap: bool) -> None:
self.m_wrap = wrap
@pyqtSlot()
def slideInPrev(self):
now = self.currentIndex()
if self.m_wrap or now > 0:
self.slideInIndex(now - 1)
@pyqtSlot()
def slideInNext(self):
now = self.currentIndex()
if self.m_wrap or now < (self.count() - 1):
self.slideInIndex(now + 1)
def slideInIndex(self, idx):
if idx > (self.count() - 1):
idx = idx % self.count()
elif idx < 0:
idx = (idx + self.count()) % self.count()
self.slideInWidget(self.widget(idx))
def slideInWidget(self, newwidget):
if self.m_active:
return
self.m_active = True
_now = self.currentIndex()
_next = self.indexOf(newwidget)
if _now == _next:
self.m_active = False
return
offsetx, offsety = self.frameRect().width(), self.frameRect().height()
self.widget(_next).setGeometry(self.frameRect())
if not self.m_direction == Qt.Horizontal:
if _now < _next:
offsetx, offsety = 0, -offsety
else:
offsetx = 0
else:
if _now < _next:
offsetx, offsety = -offsetx, 0
else:
offsety = 0
pnext = self.widget(_next).pos()
pnow = self.widget(_now).pos()
self.m_pnow = pnow
offset = QPoint(offsetx, offsety)
self.widget(_next).move(pnext - offset)
self.widget(_next).show()
self.widget(_next).raise_()
animgroup = QParallelAnimationGroup(self, finished=self.animationDoneSlot)
for index, start, end in zip((_now, _next), (pnow, pnext - offset), (pnow + offset, pnext)):
animation = QPropertyAnimation(
self.widget(index),
b"pos",
duration=self.m_speed,
easingCurve=self.m_animationtype,
startValue=start,
endValue=end,
)
animgroup.addAnimation(animation)
self.m_next = _next
self.m_now = _now
self.m_active = True
animgroup.start(QAbstractAnimation.DeleteWhenStopped)
@pyqtSlot()
def animationDoneSlot(self):
self.setCurrentIndex(self.m_next)
self.widget(self.m_now).hide()
self.widget(self.m_now).move(self.m_pnow)
self.m_active = False
def event(self, e: QEvent):
if e.type() == QEvent.Gesture:
return self.gestureEvent(QGestureEvent(e))
return super(SlidingStackedWidget, self).event(e)
def gestureEvent(self, e: QGestureEvent):
if swipe := e.gesture(Qt.SwipeGesture):
self.swipeTriggered(swipe)
return True
def swipeTriggered(self, g: QSwipeGesture):
if g.state() == Qt.GestureFinished:
if g.horizontalDirection() == QSwipeGesture.Left:
self.slideInPrev()
else:
self.slideInNext()