Search code examples
pythonpyqt5qpropertyanimation

PyQt5: prevent QPropertyAnimation jumping to different animation after another animation is set


I am running into a problem with the QPropertyAnimation class where if I call the animate method more then once, it skips all of the calls to self.animate and executes the last animation instead of all of them.

I have tried to clear all the Key values of the animation once it's finished with an empty dictionary like this:

self.animation.setKeyValues({})

And I have tried to add a time.sleep() function inbetween the calls of self.animate().

Here is my code so far:

from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5 import QtCore
import sys
from threading import Thread
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.animate(
      self.startUpWindow,
      2000,
      (
        QtCore.QRect(
          self.startupLocationX,
          -100,
          300,
          SIZEY
        ),
        QtCore.QRect(
          self.startupLocationX,
          self.startupLocationY,
          300,
          SIZEY
        )
      ),
      QtCore.QEasingCurve.InOutBack,
    ) # Create animation for notification to come down from top right

    self.animate(
      self.startUpWindow,
      2000,
      (
        QtCore.QRect(
          self.startupLocationX,
          self.startupLocationY,
          300,
          SIZEY
        ),
        QtCore.QRect(
          self.startupLocationX,
          -100,
          300,
          SIZEY
        )
      ),
      QtCore.QEasingCurve.InOutBack,
    ) # Make Window go back off the screen 

    sys.exit(self.startUpApplication.exec_())

  def animate(self, parent, duration: int, startToEndPosition: tuple, easingCurve: QtCore.QEasingCurve=None): 
    self.animation = QtCore.QPropertyAnimation(parent, b"geometry")
    self.animation.setStartValue(startToEndPosition[0])
    self.animation.setEndValue(startToEndPosition[1])
    self.animation.setDuration(duration)
    self.animation.setEasingCurve(easingCurve)
    self.animation.start()


if __name__ == "__main__": 
  StartUp()

Solution

  • It looks like you need to use a sequential animation group. I can't test your example code, but something like this should work:

    class StartUp:
      def __init__(self) -> None:
        ...
        
        self.animationGroup = QtCore.QSequentialAnimationGroup(self)
    
        self.animate(
        ...
        ) # Create animation for notification to come down from top right
    
        self.animate(
        ...
        ) # Make Window go back off the screen
    
        self.animationGroup.start()
    
      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)
        self.animationGroup.addAnimation(animation)