Search code examples
pythonanimationpyqt6qpropertyanimation

When two widget animation commands run at start only one gets animated


I am trying hide two list widgets upon start by animating the maximum width to zero, then two buttons to toggle each widget to appear and disappear, this works fine, but not at start, only one of the two widget gets animated, the other just stays on, but the same command works fine when signaled by the push buttons.

Please suggest if there is any better approach to achieve the same effect or the reason behind it.

from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt6.QtCore import QPropertyAnimation

class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(640, 480)
        self.horizontalLayout = QtWidgets.QHBoxLayout(Form)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.frame = QtWidgets.QFrame(Form)
        self.frame.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
        self.frame.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
        self.frame.setObjectName("frame")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.frame)
        self.verticalLayout.setObjectName("verticalLayout")
        self.pushButton = QtWidgets.QPushButton(self.frame)
        self.pushButton.setObjectName("pushButton")
        self.verticalLayout.addWidget(self.pushButton)
        self.pushButton_2 = QtWidgets.QPushButton(self.frame)
        self.pushButton_2.setObjectName("pushButton_2")
        self.verticalLayout.addWidget(self.pushButton_2)
        self.pushButton_3 = QtWidgets.QPushButton(self.frame)
        self.pushButton_3.setObjectName("pushButton_3")
        self.verticalLayout.addWidget(self.pushButton_3)
        spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
        self.verticalLayout.addItem(spacerItem)
        self.horizontalLayout.addWidget(self.frame)
        self.listWidget = QtWidgets.QListWidget(Form)
        self.listWidget.setObjectName("listWidget")
        self.horizontalLayout.addWidget(self.listWidget)
        self.listWidget_2 = QtWidgets.QListWidget(Form)
        self.listWidget_2.setObjectName("listWidget_2")
        self.horizontalLayout.addWidget(self.listWidget_2)
        self.listWidget_3 = QtWidgets.QListWidget(Form)
        self.listWidget_3.setObjectName("listWidget_3")
        self.horizontalLayout.addWidget(self.listWidget_3)
        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)
        
        
        # Trying to Hide both widgets upon start, but one gets hidden
        self.animate_listwidget2()
        self.animate_listwidget3()

        self.pushButton_2.clicked.connect(self.animate_listwidget2)
        self.pushButton_3.clicked.connect(self.animate_listwidget3)



    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.pushButton.setText(_translate("Form", "PushButton"))
        self.pushButton_2.setText(_translate("Form", "toggle lw2"))
        self.pushButton_3.setText(_translate("Form", "toggle lw3"))

    def animate_listwidget2(self):
        width = self.listWidget_2.width()
        if width != 0:
            width1 = 0
        else:
            width1 = 350
        self.animation = QPropertyAnimation(self.listWidget_2, b'maximumWidth')
        self.animation.setDuration(800)
        self.animation.setStartValue(width)
        self.animation.setEndValue(width1)
        self.animation.setEasingCurve(QtCore.QEasingCurve.Type.Linear)
        self.animation.start()


    def animate_listwidget3(self):
        width = self.listWidget_3.width()
        if width != 0:
            width1 = 0
        else:
            width1 = 350
        self.animation = QPropertyAnimation(self.listWidget_3, b'maximumWidth')
        self.animation.setDuration(800)
        self.animation.setStartValue(width)
        self.animation.setEndValue(width1)
        self.animation.setEasingCurve(QtCore.QEasingCurve.Type.Linear)
        self.animation.start()


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Form = QtWidgets.QWidget()
    ui = Ui_Form()
    ui.setupUi(Form)
    Form.show()
    sys.exit(app.exec())

Solution

  • First of all you should not modify the code generated by QtDesigner since that could cause other bugs so to apply my solutions you have to restore the file and call it gui.py

    The problem is that using the same attribute is destroying the previous animation.

    There are several solutions such as changing the name of the attribute assigned to one of them (for example, changing the name of the second to self.animation2) but the implementation can still be improved since with the current one, animations are always being created unnecessarily since one is enough single for each QListWidget.

    from PyQt6.QtCore import QEasingCurve, QPropertyAnimation
    from PyQt6.QtWidgets import QApplication, QWidget
    
    from gui import Ui_Form
    
    
    class Form(QWidget):
        def __init__(self, parent=None):
            super().__init__(parent)
            self.ui = Ui_Form()
            self.ui.setupUi(self)
    
            self.ui.pushButton_2.clicked.connect(self.animate_listwidget2)
            self.ui.pushButton_3.clicked.connect(self.animate_listwidget3)
    
            self.animation1 = self.build_animation(self.ui.listWidget_2)
            self.animation2 = self.build_animation(self.ui.listWidget_3)
    
            self.animate_listwidget2()
            self.animate_listwidget3()
    
        def build_animation(self, listwidget):
            animation = QPropertyAnimation(listwidget, b"maximumWidth")
            animation.setDuration(800)
            animation.setEasingCurve(QEasingCurve.Type.Linear)
            return animation
    
        def start_animation(self, animation):
            width = animation.targetObject().width()
            animation.stop()
            animation.setStartValue(width)
            animation.setEndValue(0 if width != 0 else 350)
            animation.start()
    
        def animate_listwidget2(self):
            self.start_animation(self.animation1)
    
        def animate_listwidget3(self):
            self.start_animation(self.animation2)
    
    
    if __name__ == "__main__":
        import sys
    
        app = QApplication(sys.argv)
        w = Form()
        w.show()
        sys.exit(app.exec())