Qt中QWidget析构函数内执行QPropertyAnimation动画不生效问题咨询
问题根因
你的动画无法播放是析构函数的执行逻辑和Qt异步动画的特性冲突导致的,具体有三个核心问题:
- 你将
QPropertyAnimation的父对象设为了当前正在销毁的this,QObject析构时会先删除所有子对象,你刚创建的动画实例还没来得及运行就被直接销毁了 - Qt动画是异步执行的,调用
start()后只是给事件循环投递了动画启动任务,析构函数不会等待动画执行,会立刻继续销毁当前按钮对象,动画的目标对象在第一帧渲染前就已经失效 - QObject进入析构流程后,会先断开所有关联的信号槽连接,且不再接收任何事件投递,动画的属性更新请求根本无法被处理
推荐解决方案
不要将销毁动画逻辑放到析构函数中,新增一个独立的公开槽函数触发动画,等动画播放完成后再自动销毁对象,实现代码如下:
首先在AnimatedButton头文件中新增槽声明:
class AnimatedButton : public QPushButton { Q_OBJECT // 原有代码... public slots: void animateAndDelete(); };
槽函数实现:
void AnimatedButton::animateAndDelete() { // 动画不要设置父对象为this,避免被提前销毁 QPropertyAnimation *animation = new QPropertyAnimation(this, "geometry"); animation->setDuration(5000); animation->setEasingCurve(QEasingCurve::OutExpo); QRect startGeo = geometry(); animation->setStartValue(startGeo); QRect endGeo = startGeo; endGeo.setWidth(0); endGeo.setHeight(0); animation->setEndValue(endGeo); // 动画结束后自动销毁自身,同时触发按钮的延迟销毁 connect(animation, &QPropertyAnimation::finished, this, &AnimatedButton::deleteLater); animation->start(QAbstractAnimation::DeleteWhenStopped); }
你需要销毁按钮时,不要直接调用delete,而是调用该槽函数即可:
// 销毁按钮的正确写法 // delete btn; 不要这么写 btn->animateAndDelete();
不推荐的兼容方案
如果必须在析构中执行动画,可以通过手动阻塞事件循环的方式实现,但该方案可能导致界面卡顿、父控件销毁时出现悬挂指针等问题,仅做原理展示:
AnimatedButton::~AnimatedButton() { qInfo() << "Called!"; // 用栈变量创建动画,避免被提前销毁 QPropertyAnimation animation(this, "geometry"); animation.setDuration(5000); animation.setEasingCurve(QEasingCurve::OutExpo); QRect geometry = this->geometry(); animation.setStartValue(geometry); geometry.setWidth(0); geometry.setHeight(0); animation.setEndValue(geometry); animation.start(); // 手动启动局部事件循环等待动画播放完成 QEventLoop loop; connect(&animation, &QPropertyAnimation::finished, &loop, &QEventLoop::quit); loop.exec(); }
内容的提问来源于stack exchange,提问作者M. Abdul Rehman
相关产品推荐
相关产品推荐

