Qt中update()和repaint()的源码分析

我们在使用Qt的时候,在刷新界面到底是使用update()还是repaint()?这两者有什么区别?

update()会在多次调用以后,系统会在合适的时机重绘一次,而repaint()系统会立即响应,调用多少次repaint就会重绘多少次。

以下源码来自Qt5.1.1版本

update()源码:

void QWidget::update()
{
    update(rect());
}

/*! \fn void QWidget::update(int x, int y, int w, int h)
    \overload

    This version updates a rectangle (\a x, \a y, \a w, \a h) inside
    the widget.
*/

/*!
    \overload

    This version updates a rectangle \a rect inside the widget.
*/
void QWidget::update(const QRect &rect)
{
    if (!isVisible() || !updatesEnabled())
        return;

    QRect r = rect & QWidget::rect();

    if (r.isEmpty())
        return;

    if (testAttribute(Qt::WA_WState_InPaintEvent)) {
        QApplication::postEvent(this, new QUpdateLaterEvent(r));
        return;
    }

    if (hasBackingStoreSupport()) {
#ifdef Q_WS_MAC
        if (qt_widget_private(this)->isInUnifiedToolbar) {
            qt_widget_private(this)->unifiedSurface->renderToolbar(this, true);
            return;
        }
#endif // Q_WS_MAC
        QTLWExtra *tlwExtra = window()->d_func()->maybeTopData();
        if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore)
           tlwExtra->backingStoreTracker->markDirty(r, this);
    } else {
        d_func()->repaint_sys(r);
    }
}
主要关注
tlwExtra->backingStoreTracker->markDirty(r, this);

repaint()源码:

void QWidget::repaint(const QRect &rect)
{
    Q_D(QWidget);

    if (testAttribute(Qt::WA_WState_ConfigPending)) {
        update(rect);
        return;
    }

    if (!isVisible() || !updatesEnabled() || rect.isEmpty())
        return;

    if (hasBackingStoreSupport()) {
#ifdef Q_WS_MAC
        if (qt_widget_private(this)->isInUnifiedToolbar) {
            qt_widget_private(this)->unifiedSurface->renderToolbar(this, true);
            return;
        }
#endif // Q_WS_MAC
        QTLWExtra *tlwExtra = window()->d_func()->maybeTopData();
        if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) {
            tlwExtra->inRepaint = true;
            tlwExtra->backingStoreTracker->markDirty(rect, this, true);
            tlwExtra->inRepaint = false;
        }
    } else {
        d->repaint_sys(rect);
    }
}

主要关注

tlwExtra->backingStoreTracker->markDirty(rect, this, true);


可以看到update()和()函数都会修改顶层父窗口的绘制区域,那么让我们来看看markDirty都干了什么

void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool updateImmediately,
                                    bool invalidateBuffer)
{
    Q_ASSERT(tlw->d_func()->extra);
    Q_ASSERT(tlw->d_func()->extra->topextra);
    Q_ASSERT(!tlw->d_func()->extra->topextra->inTopLevelResize);
    Q_ASSERT(widget->isVisible() && widget->updatesEnabled());
    Q_ASSERT(widget->window() == tlw);
    Q_ASSERT(!rect.isEmpty());

#ifndef QT_NO_GRAPHICSEFFECT
    widget->d_func()->invalidateGraphicsEffectsRecursively();
#endif //QT_NO_GRAPHICSEFFECT

    if (widget->d_func()->paintOnScreen()) {
        if (widget->d_func()->dirty.isEmpty()) {
            widget->d_func()->dirty = QRegion(rect);
            sendUpdateRequest(widget, updateImmediately);
            return;
        } else if (qt_region_strictContains(widget->d_func()->dirty, rect)) {
            if (updateImmediately)
                sendUpdateRequest(widget, updateImmediately);
            return; // Already dirty.
        }

        const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty();
        widget->d_func()->dirty += rect;
        if (!eventAlreadyPosted || updateImmediately)
            sendUpdateRequest(widget, updateImmediately);
        return;
    }

    if (fullUpdatePending) {
        if (updateImmediately)
            sendUpdateRequest(tlw, updateImmediately);
        return;
    }

    const QRect widgetRect = widget->d_func()->effectiveRectFor(rect);
    const QRect translatedRect(widgetRect.translated(widget->mapTo(tlw, QPoint())));
    if (qt_region_strictContains(dirty, translatedRect)) {
        if (updateImmediately)
            sendUpdateRequest(tlw, updateImmediately);
        return; // Already dirty
    }

    if (invalidateBuffer) {
        const bool eventAlreadyPosted = !dirty.isEmpty();
        dirty += translatedRect;
        if (!eventAlreadyPosted || updateImmediately)
            sendUpdateRequest(tlw, updateImmediately);
        return;
    }

   if (dirtyWidgets.isEmpty()) {
        addDirtyWidget(widget, rect);
        sendUpdateRequest(tlw, updateImmediately);
        return;
    }

    if (widget->d_func()->inDirtyList) {
        if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect))
            widget->d_func()->dirty += widgetRect;
    } else {
        addDirtyWidget(widget, rect);
    }

    if (updateImmediately)
        sendUpdateRequest(tlw, updateImmediately);
}
在markDirty() 主要是干两件事:

(1)保存dirty区域:

addDirtyWidget(widget, rect);
widget->d_func()->dirty += widgetRect;

 

(2)发送UpdateRquest事件:

sendUpdateRequest(tlw, updateImmediately);

其中有一个非常重要的参数updateImmediately:在update()中调用markDirty()给的是默认参数false,而在repaint()中调用markDirty()时传递的是true,这就导致最终发送事件的不同。

在sendUpdateRequest()中究竟发生了什么?

static inline void sendUpdateRequest(QWidget *widget, bool updateImmediately)
{
    if (!widget)
        return;

    if (updateImmediately) {
        QEvent event(QEvent::UpdateRequest);
        QApplication::sendEvent(widget, &event);
    } else {
        QApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
    }
}

我们看到 updateImmediately参数最终影响到了事件发送的机制:

update:postEvent(),而且优先级为Qt::LowEventPriority

repiant:sendEvent()

这样看来,repaint系统会立即响应并重绘控件,但是update系统不会立即响应,而是要找合适的时机再刷新



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值