PyQt5问题:如何避免QPropertyAnimation被后续动画覆盖
解决QPropertyAnimation多次调用仅执行最后一次的问题
问题核心
每次调用animate方法时,你都重新给self.animation赋值新的QPropertyAnimation对象,直接覆盖了之前的动画实例,导致前一个动画被终止;同时两个动画被同时启动,后一个动画会立刻修改目标控件的geometry属性,完全覆盖前一个动画的执行逻辑。
可行解决方案
方案1:使用QSequentialAnimationGroup按顺序执行动画
动画组可以管理多个动画,让它们按顺序依次执行,避免属性冲突。修改后代码如下:
from PyQt5.QtWidgets import QApplication, QMainWindow from PyQt5 import QtCore import sys import win32api class StartUp: def __init__(self) -> None: screenWidth, screenHeight = win32api.EnumDisplayMonitors()[0][2][2:] SIZEX, SIZEY = 200, 100 self.startupLocationX, self.startupLocationY = (screenWidth * .8, screenHeight * .05) if not isinstance(self.startupLocationX, int) or not isinstance(self.startupLocationY, int): self.startupLocationX = round(self.startupLocationX) self.startupLocationY = round(self.startupLocationY) self.startUpApplication = QApplication(sys.argv) self.startUpWindow = QMainWindow() self.startUpWindow.setWindowTitle("AssistantStarted") self.startUpWindow.setGeometry(self.startupLocationX, self.startupLocationY, SIZEX, -100) self.startUpWindow.setWindowFlag(QtCore.Qt.FramelessWindowHint) self.startUpWindow.setWindowFlag(QtCore.Qt.Tool) self.startUpWindow.show() # 创建顺序动画组,管理动画执行顺序 self.animation_group = QtCore.QSequentialAnimationGroup() # 创建并添加第一个动画 anim1 = self.create_animation( self.startUpWindow, 2000, ( QtCore.QRect( self.startupLocationX, -100, 300, SIZEY ), QtCore.QRect( self.startupLocationX, self.startupLocationY, 300, SIZEY ) ), QtCore.QEasingCurve.InOutBack, ) self.animation_group.addAnimation(anim1) # 创建并添加第二个动画 anim2 = self.create_animation( self.startUpWindow, 2000, ( QtCore.QRect( self.startupLocationX, self.startupLocationY, 300, SIZEY ), QtCore.QRect( self.startupLocationX, -100, 300, SIZEY ) ), QtCore.QEasingCurve.InOutBack, ) self.animation_group.addAnimation(anim2) # 启动动画组 self.animation_group.start() sys.exit(self.startUpApplication.exec_()) def create_animation(self, parent, duration: int, startToEndPosition: tuple, easingCurve: QtCore.QEasingCurve=None): animation = QtCore.QPropertyAnimation(parent, b"geometry") animation.setStartValue(startToEndPosition[0]) animation.setEndValue(startToEndPosition[1]) animation.setDuration(duration) animation.setEasingCurve(easingCurve) return animation if __name__ == "__main__": StartUp()
方案2:监听动画finished信号,逐个启动动画
如果不想使用动画组,可以在第一个动画结束后,通过信号触发启动第二个动画:
from PyQt5.QtWidgets import QApplication, QMainWindow from PyQt5 import QtCore import sys import win32api class StartUp: def __init__(self) -> None: screenWidth, screenHeight = win32api.EnumDisplayMonitors()[0][2][2:] SIZEX, SIZEY = 200, 100 self.startupLocationX, self.startupLocationY = (screenWidth * .8, screenHeight * .05) if not isinstance(self.startupLocationX, int) or not isinstance(self.startupLocationY, int): self.startupLocationX = round(self.startupLocationX) self.startupLocationY = round(self.startupLocationY) self.startUpApplication = QApplication(sys.argv) self.startUpWindow = QMainWindow() self.startUpWindow.setWindowTitle("AssistantStarted") self.startUpWindow.setGeometry(self.startupLocationX, self.startupLocationY, SIZEX, -100) self.startUpWindow.setWindowFlag(QtCore.Qt.FramelessWindowHint) self.startUpWindow.setWindowFlag(QtCore.Qt.Tool) self.startUpWindow.show() # 启动第一个动画,绑定结束信号 anim1 = self.animate( self.startUpWindow, 2000, ( QtCore.QRect( self.startupLocationX, -100, 300, SIZEY ), QtCore.QRect( self.startupLocationX, self.startupLocationY, 300, SIZEY ) ), QtCore.QEasingCurve.InOutBack, ) anim1.finished.connect(self.start_second_animation) sys.exit(self.startUpApplication.exec_()) def start_second_animation(self): self.animate( self.startUpWindow, 2000, ( QtCore.QRect( self.startupLocationX, self.startupLocationY, 300, SIZEY ), QtCore.QRect( self.startupLocationX, -100, 300, SIZEY ) ), QtCore.QEasingCurve.InOutBack, ) def animate(self, parent, duration: int, startToEndPosition: tuple, easingCurve: QtCore.QEasingCurve=None): animation = QtCore.QPropertyAnimation(parent, b"geometry") animation.setStartValue(startToEndPosition[0]) animation.setEndValue(startToEndPosition[1]) animation.setDuration(duration) animation.setEasingCurve(easingCurve) animation.start() return animation if __name__ == "__main__": StartUp()
为什么之前的方案无效
self.animation.setKeyValues({}):每次调用animate都会创建新的动画对象,这个操作对之前的动画实例没有任何影响,反而直接覆盖了self.animation变量,导致旧动画失去引用被终止。time.sleep():Qt的事件循环运行在主线程,sleep会阻塞事件循环,导致动画根本无法正常启动,同时也无法解决动画属性冲突的核心问题。
内容的提问来源于stack exchange,提问作者AnotherUsername
相关产品推荐
相关产品推荐

