You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 23:31:36