qt的update与repaint


一直搞不明白,qt的update与repaint的区别。虽然文档上说得貌似很明了了。但是,不看看源码,用着用着就迷糊了。今天看了下下相关的源码,记下自己的一点心得的。
1、update到paintEvent的调用
唉,貌似道路很曲折呀~

void QWidget::update(const QRect &rect)
 {
     if (!isVisible() || !updatesEnabled() || rect.isEmpty())
         return;
     if (testAttribute(Qt::WA_WState_InPaintEvent)) {
         QApplication::postEvent(this, new QUpdateLaterEvent(rect));
         return;
     }
     if (hasBackingStoreSupport()) {//这个函数在windows下总是返回true
         QTLWExtra *tlwExtra = window()->d_func()->maybeTopData();
         if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore)
             tlwExtra->backingStore->markDirty(rect, this); // 这里只是标志了一下无效区。
     } else {
         d_func()->repaint_sys(rect);
     }
 }
 如上,qt 的update函数只是通过markDirty()把无效区放到了一个叫做QWidgetBackingStore对象里面去了。其实呢,markDirty这个函数里面是大有文章的,看一下就知道 
 void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool updateImmediately,
                                     bool invalidateBuffer)
 {
 // 略去与代码分析无关的东东了 #ifndef QT_NO_GRAPHICSEFFECT
     widget->d_func()->invalidateGraphicsEffectsRecursively();// 这个东西有什么用呢?还没去细看。
 #endif //QT_NO_GRAPHICSEFFECT
 // 某些特殊情况下,直接UpdateRequest新事件。注意下QWidget的这个属性WA_PaintOnScreen
     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;
     }
  // 如果总是要整体更新的话,就没有必要缓存无效区了吧。直接发UpdateRequest事件的
     if (fullUpdatePending) {
         if (updateImmediately)
             sendUpdateRequest(tlw, updateImmediately);
         return;
     }
  
     if (!windowSurface->hasPartialUpdateSupport()) {
         fullUpdatePending = true;
         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,已经是无效的了,即刻发UpdateRequest事件的
     }
  // 隐藏窗口时,此值为true
     if (invalidateBuffer) {
         const bool eventAlreadyPosted = !dirty.isEmpty();
         dirty += translatedRect;
         if (!eventAlreadyPosted || updateImmediately)
             sendUpdateRequest(tlw, updateImmediately);
         return;
     }
  // 如果之前没有无效窗口,把窗口加上去,并发一个UpdateRequest事件
     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);// 加上一个无效窗口
     }
  // 如果需要立即更新的话,就发UpdateRequest事件的。这里就是update与repaint的区别所在了。再看下面repaint函数, 其传进来的参数是为true的。
     if (updateImmediately)
         sendUpdateRequest(tlw, updateImmediately);
 }
 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()) {
         QTLWExtra *tlwExtra = window()->d_func()->maybeTopData();
         if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) {
             tlwExtra->inRepaint = true;
             tlwExtra->backingStore->markDirty(rect, this, true);// 第三个参数为真,于是可以立即通过markDirty函数发送一个
                                                                                              //UpdateRequest事件出去的             tlwExtra->inRepaint = false;
         }
     } else {
         d->repaint_sys(rect);
     }
 }



好吧,看到这里才知道,所谓的更新,也只不过是发个更新事件而已,如下:

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);
     }
 }



至于事件是怎么调到paintEvent的,这个就是事件响应机制的问题了。好吧,先打住的,貌似已经贴了不少代码的。
后面更新,是依次调用 了QWidgetPrivate::syncBackingStore(const QRegion &region) >> QWidgetBackingStore::sync()。于是,把缓存的无效区给绘制出来了。sync()函数里面的东西也不少,累了,以后再细看的。