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

QMovie所在部件Resize时的正确重建方法及技术疑问

承载QMovie的部件调整大小时的重建方案及相关疑问解答

问题描述

当承载QMovie的部件被调整大小时,重建QMovie的“正确”方式是什么?现有实现代码如下:

现有实现代码

class Gif : public QPushButton
{
    Q_OBJECT

public:

    QMovie* movie = nullptr;
    QTimer *timer = new QTimer(this);

    int widget_width = 0;
    int widget_height = 0;

    Gif(QWidget* parent = 0) : QPushButton(parent) { }



    void paintEvent(QPaintEvent* p)
    {
        // Check if the widget has been resized, if so 
        // delete the QLabel/QMovie and recreate them.
        if (widget_width)
        {
            if (widget_width != width())
            {
                QLabel* label = this->findChild<QLabel*>("label");
                label->deleteLater();
                movie->deleteLater();
                movie = nullptr;
            }
        }



        // Load the gif into the QMovie/QLabel.
        if (!movie)
        {
            auto StyleSheet = styleSheet();

            QVector<QString> matches;
            QRegularExpression re(R"(image:\s*url\((.*)\);)");
            QRegularExpressionMatch m = re.match(StyleSheet);

            for (int i = 0; i <= m.lastCapturedIndex(); i++)
                matches.append(m.captured(i));

            movie = new QMovie(matches[1]);
            if (!movie->isValid())
                qDebug() << "failed to create the QMovie.";

            QLabel* label = new QLabel(this);
            label->setObjectName("label");

            widget_width = width();
            widget_height = height();

            label->setGeometry(0, 0, widget_width, widget_height);
            label->setMovie(movie);
            label->show();

            movie->setScaledSize(QSize().scaled(widget_width, widget_height, Qt::IgnoreAspectRatio));
            movie->setSpeed(150);
            movie->start();
        }



        // Pause for 2 seconds after getting into the 
        // last frame.
        if (movie->currentFrameNumber() == (movie->frameCount() -1))
        {
            movie->stop();
            connect(timer, SIGNAL(timeout()), this, SLOT(resume()));
            timer->start(2000);
        }


    }


public slots:

    void resume()
    {
        movie->start();
    }
    
};

疑问解答

1. 调用deleteLater()后将movie赋值为nullptr是否安全,会不会导致内存泄漏或未定义行为?

  • 赋值nullptr是安全的:deleteLater()只是向Qt事件队列发送对象销毁请求,对象在当前事件循环结束前仍存在,但后续代码中movie被置为nullptr,能避免悬空指针访问的问题,不会触发未定义行为。
  • 不会内存泄漏:deleteLater()会确保对象在合适时机被Qt事件循环销毁,内存会正常释放,不存在泄漏问题。
  • 代码隐患:当前代码中findChild<QLabel*>("label")可能返回nullptr(比如第一次执行paintEvent时还未创建label),直接调用label->deleteLater()会导致程序崩溃,建议先判断label是否非空再执行销毁操作。

2. 有没有QMovie的替代方案?因为播放中大型GIF时它占用的CPU过高。

可以尝试以下几种方案降低CPU占用:

  • Qt Multimedia框架:使用QMediaPlayer搭配QVideoWidget播放GIF,该框架会利用硬件加速能力,大幅降低CPU消耗。需要确保Qt环境配置了对应的多媒体解码支持。
  • 自行解码GIF帧:借助第三方库(如giflib、SDL_image)手动解码GIF的每帧图像,然后在部件的paintEvent中直接绘制缩放后的帧。这种方式可以自主控制渲染逻辑、帧率和缓存策略,优化性能。
  • 优化QMovie的使用:如果继续使用QMovie,可以提前缓存缩放后的帧。比如在部件resize时,将GIF的所有帧预缩放到目标尺寸并存储在QPixmapCache中,播放时直接读取缓存的图像,避免重复缩放计算。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 05:05:19