pythoncustomfluentfluent-designfluentuiguimodernpyqt5pyqt6pyside2pyside6qtqt5qt6softwareuiwidgetswin11winuiwinui3
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.
211 lines
6.4 KiB
211 lines
6.4 KiB
# coding:utf-8 |
|
from typing import List |
|
|
|
from PyQt5.QtCore import (QAbstractAnimation, QEasingCurve, QPoint, QPropertyAnimation, |
|
pyqtSignal) |
|
from PyQt5.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 = pyqtSignal() |
|
aniStart = pyqtSignal() |
|
|
|
def __init__(self, parent=None): |
|
super().__init__(parent) |
|
self.aniInfos = [] # type: List[PopUpAniInfo] |
|
self.isAnimationEnabled = True |
|
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 removeWidget(self, widget: QWidget): |
|
index = self.indexOf(widget) |
|
if index == -1: |
|
return |
|
|
|
self.aniInfos.pop(index) |
|
super().removeWidget(widget) |
|
|
|
def setAnimationEnabled(self, isEnabled: bool): |
|
"""set whether the pop animation is enabled""" |
|
self.isAnimationEnabled = isEnabled |
|
|
|
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(): |
|
return |
|
|
|
if index == self.currentIndex(): |
|
return |
|
|
|
if not self.isAnimationEnabled: |
|
return super().setCurrentIndex(index) |
|
|
|
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(), 0), 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.disconnect() |
|
super().setCurrentIndex(self._nextIndex) |
|
self.aniFinished.emit()
|
|
|