You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
196 lines
6.0 KiB
196 lines
6.0 KiB
# coding:utf-8
|
|
from typing import List
|
|
|
|
from PySide6.QtCore import (QAbstractAnimation, QEasingCurve, QPoint, QPropertyAnimation,
|
|
Signal)
|
|
from PySide6.QtWidgets import QGraphicsOpacityEffect, QStackedWidget, QWidget
|
|
|
|
|
|
class OpacityAniStackedWidget(QStackedWidget):
|
|
""" Stacked widget with fade in and fade out animation """
|
|
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent=parent)
|
|
self.__nextIndex = 0
|
|
self.__effects = [] # type:List[QPropertyAnimation]
|
|
self.__anis = [] # type:List[QPropertyAnimation]
|
|
|
|
def addWidget(self, w: QWidget):
|
|
super().addWidget(w)
|
|
|
|
effect = QGraphicsOpacityEffect(self)
|
|
effect.setOpacity(1)
|
|
ani = QPropertyAnimation(effect, b'opacity', self)
|
|
ani.setDuration(220)
|
|
ani.finished.connect(self.__onAniFinished)
|
|
self.__anis.append(ani)
|
|
self.__effects.append(effect)
|
|
w.setGraphicsEffect(effect)
|
|
|
|
def setCurrentIndex(self, index: int):
|
|
index_ = self.currentIndex()
|
|
if index == index_:
|
|
return
|
|
|
|
if index > index_:
|
|
ani = self.__anis[index]
|
|
ani.setStartValue(0)
|
|
ani.setEndValue(1)
|
|
super().setCurrentIndex(index)
|
|
else:
|
|
ani = self.__anis[index_]
|
|
ani.setStartValue(1)
|
|
ani.setEndValue(0)
|
|
|
|
self.widget(index_).show()
|
|
self.__nextIndex = index
|
|
ani.start()
|
|
|
|
def setCurrentWidget(self, w: QWidget):
|
|
self.setCurrentIndex(self.indexOf(w))
|
|
|
|
def __onAniFinished(self):
|
|
super().setCurrentIndex(self.__nextIndex)
|
|
|
|
|
|
class PopUpAniInfo:
|
|
""" Pop up ani info """
|
|
|
|
def __init__(self, widget: QWidget, deltaX: int, deltaY, ani: QPropertyAnimation):
|
|
self.widget = widget
|
|
self.deltaX = deltaX
|
|
self.deltaY = deltaY
|
|
self.ani = ani
|
|
|
|
|
|
class PopUpAniStackedWidget(QStackedWidget):
|
|
""" Stacked widget with pop up animation """
|
|
|
|
aniFinished = Signal()
|
|
aniStart = Signal()
|
|
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent)
|
|
self.aniInfos = [] # type: List[PopUpAniInfo]
|
|
self._nextIndex = None
|
|
self._ani = None
|
|
|
|
def addWidget(self, widget, deltaX=0, deltaY=76):
|
|
""" add widget to window
|
|
|
|
Parameters
|
|
-----------
|
|
widget:
|
|
widget to be added
|
|
|
|
deltaX: int
|
|
the x-axis offset from the beginning to the end of animation
|
|
|
|
deltaY: int
|
|
the y-axis offset from the beginning to the end of animation
|
|
"""
|
|
super().addWidget(widget)
|
|
|
|
self.aniInfos.append(PopUpAniInfo(
|
|
widget=widget,
|
|
deltaX=deltaX,
|
|
deltaY=deltaY,
|
|
ani=QPropertyAnimation(widget, b'pos'),
|
|
))
|
|
|
|
def setCurrentIndex(self, index: int, needPopOut: bool = False, showNextWidgetDirectly: bool = True,
|
|
duration: int = 250, easingCurve=QEasingCurve.OutQuad):
|
|
""" set current window to display
|
|
|
|
Parameters
|
|
----------
|
|
index: int
|
|
the index of widget to display
|
|
|
|
isNeedPopOut: bool
|
|
need pop up animation or not
|
|
|
|
showNextWidgetDirectly: bool
|
|
whether to show next widget directly when animation started
|
|
|
|
duration: int
|
|
animation duration
|
|
|
|
easingCurve: QEasingCurve
|
|
the interpolation mode of animation
|
|
"""
|
|
if index < 0 or index >= self.count():
|
|
raise Exception(f'The index `{index}` is illegal')
|
|
|
|
if index == self.currentIndex():
|
|
return
|
|
|
|
if self._ani and self._ani.state() == QAbstractAnimation.Running:
|
|
self._ani.stop()
|
|
self.__onAniFinished()
|
|
|
|
# get the index of widget to be displayed
|
|
self._nextIndex = index
|
|
|
|
# get animation
|
|
nextAniInfo = self.aniInfos[index]
|
|
currentAniInfo = self.aniInfos[self.currentIndex()]
|
|
|
|
currentWidget = self.currentWidget()
|
|
nextWidget = nextAniInfo.widget
|
|
ani = currentAniInfo.ani if needPopOut else nextAniInfo.ani
|
|
self._ani = ani
|
|
|
|
if needPopOut:
|
|
deltaX, deltaY = currentAniInfo.deltaX, currentAniInfo.deltaY
|
|
pos = currentWidget.pos() + QPoint(deltaX, deltaY)
|
|
self.__setAnimation(ani, currentWidget.pos(), pos, duration, easingCurve)
|
|
nextWidget.setVisible(showNextWidgetDirectly)
|
|
else:
|
|
deltaX, deltaY = nextAniInfo.deltaX, nextAniInfo.deltaY
|
|
pos = nextWidget.pos() + QPoint(deltaX, deltaY)
|
|
self.__setAnimation(ani, pos, QPoint(nextWidget.x(), self.y()), duration, easingCurve)
|
|
super().setCurrentIndex(index)
|
|
|
|
# start animation
|
|
ani.finished.connect(self.__onAniFinished)
|
|
ani.start()
|
|
self.aniStart.emit()
|
|
|
|
def setCurrentWidget(self, widget, needPopOut: bool = False, showNextWidgetDirectly: bool = True,
|
|
duration: int = 250, easingCurve=QEasingCurve.OutQuad):
|
|
""" set currect widget
|
|
|
|
Parameters
|
|
----------
|
|
widget:
|
|
the widget to be displayed
|
|
|
|
isNeedPopOut: bool
|
|
need pop up animation or not
|
|
|
|
showNextWidgetDirectly: bool
|
|
whether to show next widget directly when animation started
|
|
|
|
duration: int
|
|
animation duration
|
|
|
|
easingCurve: QEasingCurve
|
|
the interpolation mode of animation
|
|
"""
|
|
self.setCurrentIndex(
|
|
self.indexOf(widget), needPopOut, showNextWidgetDirectly, duration, easingCurve)
|
|
|
|
def __setAnimation(self, ani, startValue, endValue, duration, easingCurve=QEasingCurve.Linear):
|
|
""" set the config of animation """
|
|
ani.setEasingCurve(easingCurve)
|
|
ani.setStartValue(startValue)
|
|
ani.setEndValue(endValue)
|
|
ani.setDuration(duration)
|
|
|
|
def __onAniFinished(self):
|
|
""" animation finished slot """
|
|
self._ani.finished.disconnect()
|
|
super().setCurrentIndex(self._nextIndex)
|
|
self.aniFinished.emit()
|