pythonpyqt5qpropertyanimation

Working with 2D arrays with QPropertyAnimation in PyQt5 Python


When I work with QPropertyAnimation with 1D array, there is no problem. It accepts all the animation orders. However, I want to clearify my code, because I'll work many images. So I tried to use 2D array for images and its' animations.

Same logic with 1D array (not in the code):

self.animG = []
for i in range(3):
  self.animG.append(QPropertyAnimation(self.welcome, b"geometry")) 
...
...
...  
for i in range(len(self.animG)):
  if i == 0:
    self.animG[i].setDuration(1000)
    self.animG[i].setStartValue(QRect(-1920, 0, 1920, 1080))
    self.animG[i].setEndValue(QRect(0, 0, 1920, 1080))
  elif i == 1:
    self.animG[i].setDuration(2000)
    self.animG[i].setStartValue(QRect(0, 0, 1920, 1080))
    self.animG[i].setEndValue(QRect(0, 0, 1920, 1080))
  else:
    self.animG[i].setDuration(1000)
    self.animG[i].setStartValue(QRect(0, 0, 1920, 1080))
    self.animG[i].setEndValue(QRect(1920, 0, 1920, 1080))
...
...
  for i in range(3):
    self.anim_group.addAnimation(self.animG[i]) 

The code above is works perfectly, but I dont want to create these theme for every images. So I think something like below (all code but needed part pointed):

from PyQt5 import QtGui, QtWidgets, QtCore
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QFrame
from PyQt5.QtCore import QPropertyAnimation, QRect, QPoint
import sys, time


class Window(QMainWindow):
    def __init__(self):
        super().__init__()

        self.title = "Image Slider with PyQt5"
        self.InitWindow()

    def InitWindow(self):
        self.setWindowTitle(self.title)
        
        screenx = QtWidgets.QDesktopWidget().screenGeometry()
        print(screenx.width(), screenx.height())
        
        self.welcome = QtWidgets.QLabel(self)
        self.pixmapW = QtGui.QPixmap('Raspberry/WELCOME.jpg')
        self.welcome.setPixmap(self.pixmapW)

        self.label = QtWidgets.QLabel(self)
        self.pixmap = QtGui.QPixmap('Raspberry/image.jpg')
        self.label.setPixmap(self.pixmap)
        
        self.label2 = QtWidgets.QLabel(self)
        self.pixmap2 = QtGui.QPixmap('Raspberry/image2.jpg')
        self.label2.setPixmap(self.pixmap2)
        
        self.label.setGeometry(0, -1080, self.pixmap.width(), self.pixmap.height())
        self.label2.setGeometry(0, -1080, self.pixmap2.width(), self.pixmap2.height())
        self.welcome.setGeometry(0, 0, self.pixmapW.width(), self.pixmapW.height())        


        self.button = QPushButton("Start", self)
        self.button.setGeometry(((screenx.width() - 120)//2), ((screenx.height() - 80)//2), 120, 80)
        # QPropertyAnimation decleration
        self.propAnim = []
        self.propAnim.append(QPropertyAnimation(self.welcome, b"geometry"))
        self.propAnim.append(QPropertyAnimation(self.label, b"geometry"))
        self.propAnim.append(QPropertyAnimation(self.label2, b"geometry"))
        
        # Defining 2D array for images, and to embed QPropertAnimations
        self.imageCount, self.animCount = 3, 3
        self.animations = [[0 for x in range(self.imageCount)] for y in range(self.animCount)]
        
        # Embed 3 same animations scene to all images
        for i in range(self.imageCount):
            for j in range(self.animCount):
                self.animations[i][j] = (self.propAnim[i])
        
        self.anim_group = QtCore.QSequentialAnimationGroup()

        self.button.clicked.connect(self.doAni)
        self.anim_group.finished.connect(self.doAni)
        
        self.showMaximized()
        
    def doAni(self):
        self.label.setGeometry(0, -1080, self.pixmap.width(), self.pixmap.height())
        self.label2.setGeometry(0, -1080, self.pixmap2.width(), self.pixmap2.height())
        self.welcome.setGeometry(0, -1080, self.pixmapW.width(), self.pixmapW.height())
        
        for i in range(self.imageCount):
            for j in range(self.animCount):
                if j == 0:
                    self.animations[i][j].setDuration(2000)
                    self.animations[i][j].setStartValue(QRect(-1920, 0, 1920, 1080))
                    self.animations[i][j].setEndValue(QRect(0, 0, 1920, 1080))
                elif j == 1:
                    self.animations[i][j].setDuration(2000)
                    self.animations[i][j].setStartValue(QRect(0, 0, 1920, 1080))
                    self.animations[i][j].setEndValue(QRect(0, 0, 1920, 1080))
                elif j == 2:
                    self.animations[i][j].setDuration(2000)
                    self.animations[i][j].setStartValue(QRect(0, 0, 1920, 1080))
                    self.animations[i][j].setEndValue(QRect(1920, 0, 1920, 1080))
                else:
                    pass
        
        # HERE IS THE PROBLEMATIC PART, IT ONLY AND ONLY ACCEPTS 3rd ANIMATIONS FOR ALL IMAGES
        # NORMALLY IT SHOULD MAKE 9 MOVEMENTS TO COMPLETE SCENE
        # HOWEVER IT MAKES ONLY 3 MOVEMENTS

        for i in range(self.imageCount):
            for j in range(self.animCount):
                self.anim_group.addAnimation(self.animations[i][j])
       
        self.anim_group.start()

if __name__ == '__main__':
    App = QApplication(sys.argv)
    window = Window()
    sys.exit(App.exec())

As you can see in the last comment part of the code, it have to make 9 animation movements, but it makes only 3 which is last animation of all images!


Solution

  • If you want to have 9 animations, you have to create 9 animations. You're just using 3 and constantly changing their start/end values.

    Instead of creating unnecessary lists, just create a single list for the animations, and append them to each item. You also don't need to use instance attributes for the list counts, and you can use a predefined list of values along with zip() to cycle through both animations and values.

            self.animations = []
            for i, widget in enumerate((self.welcome, self.label, self.label2)):
                widgetAnimations = []
                for j in range(self.animCount):
                    widgetAnimations.append(QPropertyAnimation(widget, b"geometry"))
                self.animations.append(widgetAnimations)
    
        # ...
        def doAni(self):
            values = (
                (QRect(-1920, 0, 1920, 1080), QRect(0, 0, 1920, 1080)), 
                (QRect(0, 0, 1920, 1080), QRect(0, 0, 1920, 1080)), 
                (QRect(0, 0, 1920, 1080), QRect(1920, 0, 1920, 1080)), 
            )
    
            for widgetAnimations in self.animations:
                for animation, (start, end) in zip(widgetAnimations, values):
                    animation.setStartValue(start)
                    animation.setEndValue(end)
                    animation.setDuration(2000)
                    self.anim_group.addAnimation(animation)
    

    Remember that if you want to add a "paused" animation, you should use QPauseAnimation.