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

如何强制重绘QWidget?为何update/repaint调用未达预期效果?

QWidget强制重绘失效的解决办法

问题背景

现有两段Qt代码,分别实现主窗口和自定义Widget:

主窗口代码(MainWindow.cpp)

#include <QApplication>

#include "MainWindow.h"

namespace ui {
MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        child(this) {
    this->resize(425, 450);
    this->child.move(10, 10);
    this->child.resize(400, 400);
}

MainWindow::~MainWindow() {}

void MainWindow::showEvent(QShowEvent* event) {
    QWidget::showEvent(event);

    qDebug() << "Updating.";
    this->child.update();
    this->child.update();
    this->child.update();
    this->child.update();
    this->child.repaint();
    this->child.repaint();
    this->child.repaint();
    this->child.repaint();
    QApplication::processEvents();
} 
}  // namespace ui

自定义Widget代码(Widget.cpp)

#include <QPainter>

#include "Widget.h"

namespace ui {
Widget::Widget(QWidget *parent) :
        QWidget(parent) { }

void Widget::paintEvent(QPaintEvent *event) {
    qDebug() << "paintEvent called.";
    QWidget::paintEvent(event);
}
}  // namespace ui

预期控制台至少输出4次paintEvent called.,但实际仅输出:

Updating.
paintEvent called.
paintEvent called.

即便移除所有update()和repaint()调用,输出结果也完全一致。需要解决的是:当update()和repaint()无法触发预期的重绘次数时,如何强制重绘QWidget?

原因分析

Qt为了性能优化,会合并重复的重绘请求:

  • update()是异步调用,多次调用会被事件循环合并,最终仅触发一次paintEvent;
  • repaint()是同步调用,但在窗口显示流程(比如showEvent)中,Qt仍会优化重复的重绘请求;
  • 窗口首次显示本身就会触发一次paintEvent,加上代码中repaint触发的一次,就出现了两次输出的结果。

解决办法

方法1:用定时器延迟拆分重绘请求

将重绘任务放到事件队列尾部,让Qt处理完当前显示事件后再逐个处理重绘,避免合并:

void MainWindow::showEvent(QShowEvent* event) {
    QWidget::showEvent(event);

    qDebug() << "Updating.";
    QTimer::singleShot(0, [this](){
        // 每次重绘后立即处理事件,避免合并
        this->child.repaint();
        QApplication::processEvents();
        this->child.repaint();
        QApplication::processEvents();
        this->child.repaint();
        QApplication::processEvents();
        this->child.repaint();
        QApplication::processEvents();
    });
}

方法2:指定不同的重绘区域

每次调用update()时传入不同的小区域,Qt会认为这些是不同的重绘需求,不会合并请求:

void MainWindow::showEvent(QShowEvent* event) {
    QWidget::showEvent(event);

    qDebug() << "Updating.";
    child.update(QRect(0, 0, 1, 1));
    QApplication::processEvents();
    child.update(QRect(1, 0, 1, 1));
    QApplication::processEvents();
    child.update(QRect(0, 1, 1, 1));
    QApplication::processEvents();
    child.update(QRect(1, 1, 1, 1));
    QApplication::processEvents();
}

方法3:直接调用paintEvent(不推荐)

跳过Qt事件机制,直接手动调用paintEvent,但这种方式可能导致绘制状态异常,仅适合调试场景:

void MainWindow::showEvent(QShowEvent* event) {
    QWidget::showEvent(event);

    qDebug() << "Updating.";
    QPaintEvent paintEvent(child.rect());
    child.paintEvent(&paintEvent);
    child.paintEvent(&paintEvent);
    child.paintEvent(&paintEvent);
    child.paintEvent(&paintEvent);
}

注意事项

Qt的重绘优化是为了提升性能,实际开发中除非是调试、测试绘制逻辑等特殊需求,否则不建议强制触发多次重绘,应遵循Qt的事件机制来处理重绘需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 11:40:07