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

# 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()