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
相关产品推荐
相关产品推荐

