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

PyQt6启动时执行两个QPropertyAnimation控件动画仅一个生效如何解决

问题根因

你遇到的启动时仅单个控件执行动画的问题,核心是两个错误叠加导致的:

  • 动画对象被覆盖:两个动画方法都把QPropertyAnimation实例赋值给同一个self.animation属性,后调用的animate_listwidget3会把前一个动画实例覆盖,Python的垃圾回收机制会销毁未被引用的第一个动画对象,导致listWidget_2的动画直接中断。
  • 布局未完成就读取宽度:在setupUi阶段直接调用动画方法,此时窗口还未完成布局渲染,listWidget的宽度值尚未计算正确,部分场景下会导致动画起始值错误。

修复方案

首先修改动画的存储逻辑,把不同控件的动画分开存储,避免实例覆盖;其次调整启动时动画的触发时机,等窗口初始化完成后再执行。另外可以把重复的动画逻辑抽成通用方法,减少冗余代码。
下面是修复后的完整代码:

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

class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(640, 480)
        # 新增字典存储不同控件的动画实例
        self.animations = {}
        
        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)

        # 用QTimer延迟触发启动动画,等待布局完成
        QTimer.singleShot(0, lambda: [self.animate_listwidget(self.listWidget_2), self.animate_listwidget(self.listWidget_3)])

        self.pushButton_2.clicked.connect(lambda: self.animate_listwidget(self.listWidget_2))
        self.pushButton_3.clicked.connect(lambda: self.animate_listwidget(self.listWidget_3))

    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_listwidget(self, target_widget):
        width = target_widget.width()
        width1 = 0 if width != 0 else 350
        # 用控件objectName作为key存储动画,避免覆盖
        self.animations[target_widget.objectName()] = QPropertyAnimation(target_widget, b'maximumWidth')
        animation = self.animations[target_widget.objectName()]
        animation.setDuration(800)
        animation.setStartValue(width)
        animation.setEndValue(width1)
        animation.setEasingCurve(QtCore.QEasingCurve.Type.Linear)
        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())

补充说明

如果不需要启动时的动画过程,只是想默认隐藏两个控件,可以直接在setupUi阶段设置self.listWidget_2.setMaximumWidth(0)和self.listWidget_3.setMaximumWidth(0),后续触发动画的时候再读取当前宽度切换即可,逻辑更简单。

内容的提问来源于stack exchange,提问作者Jack Zero

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.10 06:09:02