如何强制重绘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
相关产品推荐
相关产品推荐

