QT5.9.8 update()源码剖析

1 update调用     

  在QT中,所有的GUI最终都继承自QWidget,因此所的调用update()都是基类QWidget的update()。

        在QWidget中,路径:Qt\Qt5.9.8\5.9.8\Src\qtbase\src\widgets\kernel\qwidget.h

public Q_SLOTS:
    void update();
    void repaint();

public:
    inline void update(int x, int y, int w, int h);
    void update(const QRect&);
    void update(const QRegion&);

        默认调用的是无参的update(),功能:更新窗口部件,当Qt回到主事件中时,它规划了所要处理的绘制事件。这样允许Qt进行优化从而得到比调用repaint()更快的速度和

更少的闪烁。 几次调用update()的结果通常仅仅是一次paintEvent()调用。 Qt通常在paintEvent()调用之前擦除这个窗口部件的区域,仅仅只有在WRepaintNoErase窗口部件标记被设置的时候才不会。repaint()是立即调用paintEvent(),而update()是几次执行才调用一次paintEvent()。

       它默认刷新控件的整个区域,调用如下重载的update函数:

/*!
    Updates the widget unless updates are disabled or the widget is
    hidden.

    This function does not cause an immediate repaint; instead it
    schedules a paint event for processing when Qt returns to the main
    event loop. This permits Qt to optimize for more speed and less
    flicker than a call to repaint() does.

    Calling update() several times normally results in just one
    paintEvent() call.

    Qt normally erases the widget's area before the paintEvent() call.
    If the Qt::WA_OpaquePaintEvent widget attribute is set, the widget is
    responsible for painting all its pixels with an opaque color.

    \sa repaint(), paintEvent(), setUpdatesEnabled(), {Analog Clock Example}
*/
void QWidget::update()
{
    update(rect());
}

         Q_D:Q_Q的作用https://blog.csdn.net/qq_41399894/article/details/100659953?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&dist_request_id=ffce0f8f-fdbc-4bb3-8feb-86549400d95e&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control

https://blog.csdn.net/u010155023/article/details/50826102

        Q_D指针一般用来访问"Private"类中的数据,Q_Q指针则是通过Private类访问"Public"类.private类就是装钱的钱包,public类就是口袋.Qt里面如此封装,应该是尽量降低对OO的破坏吧!毕竟friend本身就违背类OO原则.

Window 中窗口的层次关系以及窗口的属性

顶层窗口:

template <typename T>
void QWidgetPrivate::update(T r)
{
    Q_Q(QWidget);
    
    //1 如果控件是隐藏或者刷新被精致则直接返回
    if (!q->isVisible() || !q->updatesEnabled())
        return;

    T clipped = r & q->rect();

    //2控件大小和传递的更新区域没有交集则直接返回
    if (clipped.isEmpty())
        return;

    if (q->testAttribute(Qt::WA_WState_InPaintEvent)) {
        QApplication::postEvent(q, new QUpdateLaterEvent(clipped));
        return;
    }

    //3 如果支持BackingStore(默认支持) 则标脏该控件所属的顶层窗口(TLW:topLevelWideet)
    //即调用markDirty
    QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
    if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore)
        tlwExtra->backingStoreTracker->markDirty(clipped, q);
}

mariDirty

/*!
    Marks the region of the widget as dirty (if not already marked as dirty) and
    posts an UpdateRequest event to the top-level widget (if not already posted).

    If updateTime is UpdateNow, the event is sent immediately instead of posted.

    If bufferState is BufferInvalid, all widgets intersecting with the region will be dirty.

    If the widget paints directly on screen, the event is sent to the widget
    instead of the top-level widget, and bufferState is completely ignored.

    ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
*/
void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget,
                                    UpdateTime updateTime, BufferState bufferState)
{
    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(!rgn.isEmpty());

#if QT_CONFIG(graphicseffect)
    widget->d_func()->invalidateGraphicsEffectsRecursively();
#endif // QT_CONFIG(graphicseffect)

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

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

    const QPoint offset = widget->mapTo(tlw, QPoint());

    if (QWidgetPrivate::get(widget)->renderToTexture) {
        if (!widget->d_func()->inDirtyList)
            addDirtyRenderToTextureWidget(widget);
        if (!updateRequestSent || updateTime == UpdateNow)
            sendUpdateRequest(tlw, updateTime);
        return;
    }

    const QRect widgetRect = widget->d_func()->effectiveRectFor(widget->rect());
    if (qt_region_strictContains(dirty, widgetRect.translated(offset))) {
        if (updateTime == UpdateNow)
            sendUpdateRequest(tlw, updateTime);
        return; // Already dirty.
    }

    if (bufferState == BufferInvalid) {
        const bool eventAlreadyPosted = !dirty.isEmpty() || updateRequestSent;
#if QT_CONFIG(graphicseffect)
        if (widget->d_func()->graphicsEffect)
            dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect()).translated(offset);
        else
#endif // QT_CONFIG(graphicseffect)
            dirty += rgn.translated(offset);
        if (!eventAlreadyPosted || updateTime == UpdateNow)
            sendUpdateRequest(tlw, updateTime);
        return;
    }


    //将控件加入到addDirtyWidget容器中
    //通知tlw进程刷新sendUpdateRequest
    if (dirtyWidgets.isEmpty()) {
        addDirtyWidget(widget, rgn);
        sendUpdateRequest(tlw, updateTime);
        return;
    }

    if (widget->d_func()->inDirtyList) {
        if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect)) {
#if QT_CONFIG(graphicseffect)
            if (widget->d_func()->graphicsEffect)
                widget->d_func()->dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect());
            else
#endif // QT_CONFIG(graphicseffect)
                widget->d_func()->dirty += rgn;
        }
    } else {
        addDirtyWidget(widget, rgn);
    }

    if (updateTime == UpdateNow)
        sendUpdateRequest(tlw, updateTime);
}

sendUpdateRequest:发送更新事件


void QWidgetBackingStore::sendUpdateRequest(QWidget *widget, UpdateTime updateTime)
{
    if (!widget)
        return;

#ifndef QT_NO_OPENGL
    // Having every repaint() leading to a sync/flush is bad as it causes
    // compositing and waiting for vsync each and every time. Change to
    // UpdateLater, except for approx. once per frame to prevent starvation in
    // case the control does not get back to the event loop.
    QWidget *w = widget->window();
    if (updateTime == UpdateNow && w && w->windowHandle() && QWindowPrivate::get(w->windowHandle())->compositing) {
        int refresh = 60;
        QScreen *ws = w->windowHandle()->screen();
        if (ws)
            refresh = ws->refreshRate();
        QWindowPrivate *wd = QWindowPrivate::get(w->windowHandle());
        if (wd->lastComposeTime.isValid()) {
            const qint64 elapsed = wd->lastComposeTime.elapsed();
            if (elapsed <= qint64(1000.0f / refresh))
                updateTime = UpdateLater;
       }
    }
#endif

    switch (updateTime) {
    case UpdateLater://异步 UpdateRequest接收者widget是tlw
        updateRequestSent = true;
        QApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
        break;
    case UpdateNow: { //sendEvent 同步  立即刷新
        QEvent event(QEvent::UpdateRequest);
        QApplication::sendEvent(widget, &event);
        break;
        }
    }
}

update是异步的,因为它调用的是postEvent,将一个消息放入消息循环中。repaint是同步的,因为它调用的是sendEvent,直接发送到对方

postEvent

postEvent介绍

qt事件循环需要维护一个事件队列,在Qt的main函数中最后一般调用QApplication::exec()成员函数来保持程序对事件队列的处理,exec()的实质是不停调用processEvent()函数从队列中获取事件,并处理,然后删除,postEvent的作用就是发送一个事件到此队列中,由于删除队列中事件调用delete运算符,所以,postEvent()传递的事件一定要是动态分配的。

sendEvent()函数直接跳过事件循环队列,直接调用notify()函数发送事件到目标对象,并等待事件处理结果,所以其传递的事件直接在栈上分配即可,(postEvent()函数只负责添加事件到队列,不等待事件处理结果)。

2 进入消息通知流程

调用bool QApplication::notify(QObject *receiver, QEvent *e)      notify的作用

在QApplication类中,真正负责事件分发处理的是QApplication类的notify方法(函数),该方法负责向接收者发送事件,返回接收事件对象的处理程序返回的值.

因此从QApplication类派生自定义类并重写notify方法可以截获应用接收到的所有事件。

重写notify的语法:
notify(QObject receiver, QEvent event)
其中:
1、参数receiver表示将事件发送给谁;
2、event就是事件参数
3、返回值为receiver的事件处理方法的返回值,如果返回值是False表示不阻拦事件,将事件信息继续向下传递,如果返回True表示消费了事件。

事件的产生


事件的两种来源:
       一种是系统产生的;通常是window system把从系统得到的消息,比如鼠标按键,键盘按键等, 放入系统的消息队列中. Qt事件循环的时候读取这些事件,转化为QEvent,再依次处理.
       一种是由Qt应用程序程序自身产生的.程序产生事件有两种方式, 一种是调用QApplication::postEvent(). 例如QWidget::update()函数,当需要重新绘制屏幕时,程序调用update()函数,new出来一个paintEvent,调用QApplication::postEvent(),将其放入Qt的消息队列中,等待依次被处理. 另一种方式是调用sendEvent()函数. 这时候事件不会放入队列, 而是直接被派发和处理, QWidget::repaint()函数用的就是这种方式.

事件的调度


两种调度方式,一种是同步的, 一种是异步.
Qt的事件循环是异步的,当调用QApplication::exec()时,就进入了事件循环. 该循环可以简化的描述为如下的代码:
while ( !app_exit_loop ) {
       while( !postedEvents ) {             processPostedEvents()       }
       while( !qwsEvnts ){            qwsProcessEvents();   }
       while( !postedEvents ) {             processPostedEvents()       }
}
        先处理Qt事件队列中的事件, 直至为空. 再处理系统消息队列中的消息, 直至为空, 在处理系统消息的时候会产生新的Qt事件, 需要对其再次进行处理.
        调用QApplication::sendEvent的时候, 消息会立即被处理,是同步的. 实际上QApplication::sendEvent()是通过调用QApplication::notify(), 直接进入了事件的派发和处理环节.


事件的派发和处理


        首先说明Qt中事件过滤器的概念. 事件过滤器是Qt中一个独特的事件处理机制, 功能强大而且使用起来灵活方便. 通过它, 可以让一个对象侦听拦截另外一个对象的事件. 事件过滤器是这样实现的: 在所有Qt对象的基类: QObject中有一个类型为QObjectList的成员变量,名字为eventFilters。

voidQObject::installEventFilter ( const QObject * obj )
安装事件过滤器obj到这个对象。

        事件过滤器eventFilter的使用
        Qt中,事件的派发是从QApplication::notify() 开始的, 因为QAppliction也是继承自QObject, 所以先检查QAppliation对象,

(1)如果有事件过滤器安装在qApp上, 先调用这些事件过滤器.

(2)接下来QApplication::notify() 会过滤或合并一些事件(比如失效widget的鼠标事件会被过滤掉, 而同一区域重复的绘图事件会被合并).

(3)事件被送到reciver::event() 处理.同样, 在reciver::event()中, 先检查有无事件过滤器安装在reciever上. 若有, 则调用之.

(4)接下来,根据QEvent的类型, 调用相应的特定事件处理函数. 一些常见的事件都有特定事件处理函数, 比如:mousePressEvent(), focusOutEvent(),  resizeEvent(), paintEvent(), resizeEvent()等等. 在实际应用中, 经常需要重载这些特定事件处理函数在处理事件. 但对于那些不常见的事件, 是没有相对应的特定事件处理函数的. 如果要处理这些事件, 就需要使用别的办法, 比如重载event() 函数, 或是安装事件过滤器.


事件的转发


       对于某些类别的事件, 如果在整个事件的派发过程结束后还没有被处理, 那么这个事件将会向上转发给它的父widget, 直到最顶层窗口. 如图所示, 事件最先发送给QCheckBox, 如果QCheckBox没有处理, 那么由QGroupBox接着处理, 如果QGroupBox没有处理, 再送到QDialog, 因为QDialog已经是最顶层widget, 所以如果QDialog不处理, QEvent将停止转发.
        如何判断一个事件是否被处理了呢? Qt中和事件相关的函数通过两种方式相互通信. QApplication::notify(), QObject::eventFilter(), QObject::event() 通过返回bool值来表示是否已处理. “真”表示已经处理, “假”表示事件需要继续传递. 另一种是调用QEvent::ignore() 或 QEvent::accept() 对事件进行标识. 这种方式只用于event() 函数和特定事件处理函数之间的沟通. 而且只有用在某些类别事件上是有意义的, 这些事件就是上面提到的那些会被转发的事件, 包括: 鼠标, 滚轮, 按键等事件.

Notify

路径:C:\Qt\Qt5.9.8\5.9.8\Src\qtbase\src\widgets\kernel\qapplication.h

bool QApplication::notify(QObject *receiver, QEvent *e) 没有对QEvent::UpdateRequset事件进行处理,调用

bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)

进行事件处理:一共分三步:

bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)
{
    // send to all application event filters
    //1 首先发送到application的事件处理器处理,如果处理且返回true就 返回了,不会走下面
    if (threadRequiresCoreApplication()
        && receiver->d_func()->threadData->thread == mainThread()
        && sendThroughApplicationEventFilters(receiver, e))
        return true;

    if (receiver->isWidgetType()) {
        QWidget *widget = static_cast<QWidget *>(receiver);

#if !defined(QT_NO_CURSOR)
        // toggle HasMouse widget state on enter and leave
        if ((e->type() == QEvent::Enter || e->type() == QEvent::DragEnter) &&
            (!QApplication::activePopupWidget() || QApplication::activePopupWidget() == widget->window()))
            widget->setAttribute(Qt::WA_UnderMouse, true);
        else if (e->type() == QEvent::Leave || e->type() == QEvent::DragLeave)
            widget->setAttribute(Qt::WA_UnderMouse, false);
#endif

        if (QLayout *layout=widget->d_func()->layout) {
            layout->widgetEvent(e);
        }
    }

    // send to all receiver event filters
    // 2 receiver的事件过滤器处理
    if (sendThroughObjectEventFilters(receiver, e))
        return true;

    // deliver the event
   // 3调用receiver的event函数进行处理
    bool consumed = receiver->event(e);
    QCoreApplicationPrivate::setEventSpontaneous(e, false);
    return consumed;
}

对事件过滤器的调用:

bool QCoreApplicationPrivate::sendThroughApplicationEventFilters(QObject *receiver, QEvent *event)
{
    // We can't access the application event filters outside of the main thread (race conditions)
    Q_ASSERT(receiver->d_func()->threadData->thread == mainThread());

    if (extraData) {
        // application event filters are only called for objects in the GUI thread
        for (int i = 0; i < extraData->eventFilters.size(); ++i) {
            QObject *obj = extraData->eventFilters.at(i);
            if (!obj)
                continue;
            if (obj->d_func()->threadData != threadData) {
                qWarning("QCoreApplication: Application event filter cannot be in a different thread.");
                continue;
            }
            if (obj->eventFilter(receiver, event))
                return true;
        }
    }
    return false;
}

bool QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject *receiver, QEvent *event)
{
    if (receiver != QCoreApplication::instance() && receiver->d_func()->extraData) {
        for (int i = 0; i < receiver->d_func()->extraData->eventFilters.size(); ++i) {
            QObject *obj = receiver->d_func()->extraData->eventFilters.at(i);
            if (!obj)
                continue;
            if (obj->d_func()->threadData != receiver->d_func()->threadData) {
                qWarning("QCoreApplication: Object event filter cannot be in a different thread.");
                continue;
            }
            if (obj->eventFilter(receiver, event))
                return true;
        }
    }
    return false;
}

receive中event的调用

在基类QWidget中

bool QWidget::event(QEvent *event)
{
    Q_D(QWidget);

    // ignore mouse and key events when disabled
    if (!isEnabled()) {
        switch(event->type()) {
        case QEvent::TabletPress:
        case QEvent::TabletRelease:
        case QEvent::TabletMove:
        case QEvent::MouseButtonPress:
        case QEvent::MouseButtonRelease:
        case QEvent::MouseButtonDblClick:
        case QEvent::MouseMove:
        case QEvent::TouchBegin:
        case QEvent::TouchUpdate:
        case QEvent::TouchEnd:
        case QEvent::TouchCancel:
        case QEvent::ContextMenu:
        case QEvent::KeyPress:
        case QEvent::KeyRelease:
#if QT_CONFIG(wheelevent)
        case QEvent::Wheel:
#endif
            return false;
        default:
            break;
        }
    }
    switch (event->type()) {
    case QEvent::MouseMove:
        mouseMoveEvent((QMouseEvent*)event);
        break;

    case QEvent::MouseButtonPress:
        mousePressEvent((QMouseEvent*)event);
        break;

    case QEvent::MouseButtonRelease:
        mouseReleaseEvent((QMouseEvent*)event);
        break;

    case QEvent::MouseButtonDblClick:
        mouseDoubleClickEvent((QMouseEvent*)event);
        break;
#if QT_CONFIG(wheelevent)
    case QEvent::Wheel:
        wheelEvent((QWheelEvent*)event);
        break;
#endif
#if QT_CONFIG(tabletevent)
    case QEvent::TabletMove:
        if (static_cast<QTabletEvent *>(event)->buttons() == Qt::NoButton && !testAttribute(Qt::WA_TabletTracking))
            break;
        Q_FALLTHROUGH();
    case QEvent::TabletPress:
    case QEvent::TabletRelease:
        tabletEvent((QTabletEvent*)event);
        break;
#endif
    case QEvent::KeyPress: {
        QKeyEvent *k = (QKeyEvent *)event;
        bool res = false;
        if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) {  //### Add MetaModifier?
            if (k->key() == Qt::Key_Backtab
                || (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier)))
                res = focusNextPrevChild(false);
            else if (k->key() == Qt::Key_Tab)
                res = focusNextPrevChild(true);
            if (res)
                break;
        }
        keyPressEvent(k);
#ifdef QT_KEYPAD_NAVIGATION
        if (!k->isAccepted() && QApplication::keypadNavigationEnabled()
            && !(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::ShiftModifier))) {
            if (QApplication::navigationMode() == Qt::NavigationModeKeypadTabOrder) {
                if (k->key() == Qt::Key_Up)
                    res = focusNextPrevChild(false);
                else if (k->key() == Qt::Key_Down)
                    res = focusNextPrevChild(true);
            } else if (QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional) {
                if (k->key() == Qt::Key_Up)
                    res = QWidgetPrivate::navigateToDirection(QWidgetPrivate::DirectionNorth);
                else if (k->key() == Qt::Key_Right)
                    res = QWidgetPrivate::navigateToDirection(QWidgetPrivate::DirectionEast);
                else if (k->key() == Qt::Key_Down)
                    res = QWidgetPrivate::navigateToDirection(QWidgetPrivate::DirectionSouth);
                else if (k->key() == Qt::Key_Left)
                    res = QWidgetPrivate::navigateToDirection(QWidgetPrivate::DirectionWest);
            }
            if (res) {
                k->accept();
                break;
            }
        }
#endif
#if QT_CONFIG(whatsthis)
        if (!k->isAccepted()
            && k->modifiers() & Qt::ShiftModifier && k->key() == Qt::Key_F1
            && d->whatsThis.size()) {
            QWhatsThis::showText(mapToGlobal(inputMethodQuery(Qt::ImCursorRectangle).toRect().center()), d->whatsThis, this);
            k->accept();
        }
#endif
    }
        break;

    case QEvent::KeyRelease:
        keyReleaseEvent((QKeyEvent*)event);
        Q_FALLTHROUGH();
    case QEvent::ShortcutOverride:
        break;

    case QEvent::InputMethod:
        inputMethodEvent((QInputMethodEvent *) event);
        break;

    case QEvent::InputMethodQuery:
        if (testAttribute(Qt::WA_InputMethodEnabled)) {
            QInputMethodQueryEvent *query = static_cast<QInputMethodQueryEvent *>(event);
            Qt::InputMethodQueries queries = query->queries();
            for (uint i = 0; i < 32; ++i) {
                Qt::InputMethodQuery q = (Qt::InputMethodQuery)(int)(queries & (1<<i));
                if (q) {
                    QVariant v = inputMethodQuery(q);
                    if (q == Qt::ImEnabled && !v.isValid() && isEnabled())
                        v = QVariant(true); // special case for Qt4 compatibility
                    query->setValue(q, v);
                }
            }
            query->accept();
        }
        break;

    case QEvent::PolishRequest:
        ensurePolished();
        break;

    case QEvent::Polish: {
        style()->polish(this);
        setAttribute(Qt::WA_WState_Polished);
        if (!QApplication::font(this).isCopyOf(QApplication::font()))
            d->resolveFont();
        if (!QApplication::palette(this).isCopyOf(QApplication::palette()))
            d->resolvePalette();
    }
        break;

    case QEvent::ApplicationWindowIconChange:
        if (isWindow() && !testAttribute(Qt::WA_SetWindowIcon)) {
            d->setWindowIcon_sys();
            d->setWindowIcon_helper();
        }
        break;
    case QEvent::FocusIn:
        focusInEvent((QFocusEvent*)event);
        d->updateWidgetTransform(event);
        break;

    case QEvent::FocusOut:
        focusOutEvent((QFocusEvent*)event);
        break;

    case QEvent::Enter:
#if QT_CONFIG(statustip)
        if (d->statusTip.size()) {
            QStatusTipEvent tip(d->statusTip);
            QApplication::sendEvent(const_cast<QWidget *>(this), &tip);
        }
#endif
        enterEvent(event);
        break;

    case QEvent::Leave:
#if QT_CONFIG(statustip)
        if (d->statusTip.size()) {
            QString empty;
            QStatusTipEvent tip(empty);
            QApplication::sendEvent(const_cast<QWidget *>(this), &tip);
        }
#endif
        leaveEvent(event);
        break;

    case QEvent::HoverEnter:
    case QEvent::HoverLeave:
        update();
        break;

    case QEvent::Paint:
        // At this point the event has to be delivered, regardless
        // whether the widget isVisible() or not because it
        // already went through the filters
        paintEvent((QPaintEvent*)event);
        break;

    case QEvent::Move:
        moveEvent((QMoveEvent*)event);
        d->updateWidgetTransform(event);
        break;

    case QEvent::Resize:
        resizeEvent((QResizeEvent*)event);
        d->updateWidgetTransform(event);
        break;

    case QEvent::Close:
        closeEvent((QCloseEvent *)event);
        break;

#ifndef QT_NO_CONTEXTMENU
    case QEvent::ContextMenu:
        switch (data->context_menu_policy) {
        case Qt::PreventContextMenu:
            break;
        case Qt::DefaultContextMenu:
            contextMenuEvent(static_cast<QContextMenuEvent *>(event));
            break;
        case Qt::CustomContextMenu:
            emit customContextMenuRequested(static_cast<QContextMenuEvent *>(event)->pos());
            break;
#if QT_CONFIG(menu)
        case Qt::ActionsContextMenu:
            if (d->actions.count()) {
                QMenu::exec(d->actions, static_cast<QContextMenuEvent *>(event)->globalPos(),
                            0, this);
                break;
            }
            Q_FALLTHROUGH();
#endif
        default:
            event->ignore();
            break;
        }
        break;
#endif // QT_NO_CONTEXTMENU

#ifndef QT_NO_DRAGANDDROP
    case QEvent::Drop:
        dropEvent((QDropEvent*) event);
        break;

    case QEvent::DragEnter:
        dragEnterEvent((QDragEnterEvent*) event);
        break;

    case QEvent::DragMove:
        dragMoveEvent((QDragMoveEvent*) event);
        break;

    case QEvent::DragLeave:
        dragLeaveEvent((QDragLeaveEvent*) event);
        break;
#endif

    case QEvent::Show:
        showEvent((QShowEvent*) event);
        break;

    case QEvent::Hide:
        hideEvent((QHideEvent*) event);
        break;

    case QEvent::ShowWindowRequest:
        if (!isHidden())
            d->show_sys();
        break;

    case QEvent::ApplicationFontChange:
        d->resolveFont();
        break;
    case QEvent::ApplicationPaletteChange:
        if (!(windowType() == Qt::Desktop))
            d->resolvePalette();
        break;

    case QEvent::ToolBarChange:
    case QEvent::ActivationChange:
    case QEvent::EnabledChange:
    case QEvent::FontChange:
    case QEvent::StyleChange:
    case QEvent::PaletteChange:
    case QEvent::WindowTitleChange:
    case QEvent::IconTextChange:
    case QEvent::ModifiedChange:
    case QEvent::MouseTrackingChange:
    case QEvent::TabletTrackingChange:
    case QEvent::ParentChange:
    case QEvent::LocaleChange:
    case QEvent::MacSizeChange:
    case QEvent::ContentsRectChange:
    case QEvent::ThemeChange:
    case QEvent::ReadOnlyChange:
        changeEvent(event);
        break;

    case QEvent::WindowStateChange: {
        const bool wasMinimized = static_cast<const QWindowStateChangeEvent *>(event)->oldState() & Qt::WindowMinimized;
        if (wasMinimized != isMinimized()) {
            QWidget *widget = const_cast<QWidget *>(this);
            if (wasMinimized) {
                // Always send the spontaneous events here, otherwise it can break the application!
                if (!d->childrenShownByExpose) {
                    // Show widgets only when they are not yet shown by the expose event
                    d->showChildren(true);
                    QShowEvent showEvent;
                    QCoreApplication::sendSpontaneousEvent(widget, &showEvent);
                }
                d->childrenHiddenByWState = false; // Set it always to "false" when window is restored
            } else {
                QHideEvent hideEvent;
                QCoreApplication::sendSpontaneousEvent(widget, &hideEvent);
                d->hideChildren(true);
                d->childrenHiddenByWState = true;
            }
            d->childrenShownByExpose = false; // Set it always to "false" when window state changes
        }
        changeEvent(event);
    }
        break;

    case QEvent::WindowActivate:
    case QEvent::WindowDeactivate: {
        if (isVisible() && !palette().isEqual(QPalette::Active, QPalette::Inactive))
            update();
        QList<QObject*> childList = d->children;
        for (int i = 0; i < childList.size(); ++i) {
            QWidget *w = qobject_cast<QWidget *>(childList.at(i));
            if (w && w->isVisible() && !w->isWindow())
                QApplication::sendEvent(w, event);
        }
        break; }

    case QEvent::LanguageChange:
        changeEvent(event);
        {
            QList<QObject*> childList = d->children;
            for (int i = 0; i < childList.size(); ++i) {
                QObject *o = childList.at(i);
                if (o)
                    QApplication::sendEvent(o, event);
            }
        }
        update();
        break;

    case QEvent::ApplicationLayoutDirectionChange:
        d->resolveLayoutDirection();
        break;

    case QEvent::LayoutDirectionChange:
        if (d->layout)
            d->layout->invalidate();
        update();
        changeEvent(event);
        break;
    case QEvent::UpdateRequest:
        d->syncBackingStore();
        break;
    case QEvent::UpdateLater:
        update(static_cast<QUpdateLaterEvent*>(event)->region());
        break;
    case QEvent::StyleAnimationUpdate:
        if (isVisible() && !window()->isMinimized()) {
            event->accept();
            update();
        }
        break;

    case QEvent::WindowBlocked:
    case QEvent::WindowUnblocked:
        if (!d->children.isEmpty()) {
            QWidget *modalWidget = QApplication::activeModalWidget();
            for (int i = 0; i < d->children.size(); ++i) {
                QObject *o = d->children.at(i);
                if (o && o != modalWidget && o->isWidgetType()) {
                    QWidget *w  = static_cast<QWidget *>(o);
                    // do not forward the event to child windows; QApplication does this for us
                    if (!w->isWindow())
                        QApplication::sendEvent(w, event);
                }
            }
        }
#if 0 // Used to be included in Qt4 for Q_WS_WIN
            setDisabledStyle(this, (event->type() == QEvent::WindowBlocked));
#endif
        break;
#ifndef QT_NO_TOOLTIP
    case QEvent::ToolTip:
        if (!d->toolTip.isEmpty())
            QToolTip::showText(static_cast<QHelpEvent*>(event)->globalPos(), d->toolTip, this, QRect(), d->toolTipDuration);
        else
            event->ignore();
        break;
#endif
#if QT_CONFIG(whatsthis)
    case QEvent::WhatsThis:
        if (d->whatsThis.size())
            QWhatsThis::showText(static_cast<QHelpEvent *>(event)->globalPos(), d->whatsThis, this);
        else
            event->ignore();
        break;
    case QEvent::QueryWhatsThis:
        if (d->whatsThis.isEmpty())
            event->ignore();
        break;
#endif
    case QEvent::EmbeddingControl:
        d->topData()->frameStrut.setCoords(0 ,0, 0, 0);
        data->fstrut_dirty = false;
#if 0 /* Used to be included in Qt4 for Q_WS_WIN */ || 0 /* Used to be included in Qt4 for Q_WS_X11 */
        d->topData()->embedded = 1;
#endif
        break;
#ifndef QT_NO_ACTION
    case QEvent::ActionAdded:
    case QEvent::ActionRemoved:
    case QEvent::ActionChanged:
        actionEvent((QActionEvent*)event);
        break;
#endif

    case QEvent::KeyboardLayoutChange:
        {
            changeEvent(event);

            // inform children of the change
            QList<QObject*> childList = d->children;
            for (int i = 0; i < childList.size(); ++i) {
                QWidget *w = qobject_cast<QWidget *>(childList.at(i));
                if (w && w->isVisible() && !w->isWindow())
                    QApplication::sendEvent(w, event);
            }
            break;
        }
#if 0 // Used to be included in Qt4 for Q_WS_MAC
    case QEvent::MacGLWindowChange:
        d->needWindowChange = false;
        break;
#endif
    case QEvent::TouchBegin:
    case QEvent::TouchUpdate:
    case QEvent::TouchEnd:
    case QEvent::TouchCancel:
    {
        event->ignore();
        break;
    }
#ifndef QT_NO_GESTURES
    case QEvent::Gesture:
        event->ignore();
        break;
#endif
    case QEvent::ScreenChangeInternal:
        if (const QTLWExtra *te = d->maybeTopData()) {
            const QWindow *win = te->window;
            d->setWinId((win && win->handle()) ? win->handle()->winId() : 0);
        }
        if (d->data.fnt.d->dpi != logicalDpiY())
            d->updateFont(d->data.fnt);
#ifndef QT_NO_OPENGL
        d->renderToTextureReallyDirty = 1;
#endif
        break;
#ifndef QT_NO_PROPERTIES
    case QEvent::DynamicPropertyChange: {
        const QByteArray &propName = static_cast<QDynamicPropertyChangeEvent *>(event)->propertyName();
        if (propName.length() == 13 && !qstrncmp(propName, "_q_customDpi", 12)) {
            uint value = property(propName.constData()).toUInt();
            if (!d->extra)
                d->createExtra();
            const char axis = propName.at(12);
            if (axis == 'X')
                d->extra->customDpiX = value;
            else if (axis == 'Y')
                d->extra->customDpiY = value;
            d->updateFont(d->data.fnt);
        }
        if (windowHandle() && !qstrncmp(propName, "_q_platform_", 12))
            windowHandle()->setProperty(propName, property(propName));
        Q_FALLTHROUGH();
    }
#endif
    default:
        return QObject::event(event);
    }
    return true;
}

    其中:

    case QEvent::UpdateRequest:
        d->syncBackingStore();
        break;

从这里也可以看到事件的处理顺序:

......    
switch (event->type()) {
    case QEvent::MouseMove:
        mouseMoveEvent((QMouseEvent*)event);
        break;

    case QEvent::MouseButtonPress:
        mousePressEvent((QMouseEvent*)event);
        break;

    case QEvent::MouseButtonRelease:
        mouseReleaseEvent((QMouseEvent*)event);
        break;

    case QEvent::MouseButtonDblClick:
        mouseDoubleClickEvent((QMouseEvent*)event);
        break;
......

在event中,如mouseMove事件,调用        mouseMoveEvent((QMouseEvent*)event);接口

任何从QObject类派生的对象均可以通过QObject::event()方法接收事件;

事件产生时,QT会创建一个合适的QEvent对象或其子对象, 然后通过调用QObject类的event()函数将这个事件对象传给特定的QObject对象或其子对象

重新实现事件函数不需要调用, 因为在main.cpp中的exe()函数中有事件循环, 事件函数只是重新实现了一下, 事件函数时虚函数都在基类中定义的,在此处只是重新改写了函数内容.

事件处理顺序:

事件过滤器eventFilter(QObject *obj, QEvent *e);----->事件分发event(QEvent *e);-------->具体事件keyPressEvent(QKeyEvent *e)等事件

一. 事件处理方法
Qt的事件处理函数都是虚函数,在基类中声明,在子类中重新实现, 这些相应的事件函数在.h文件中定义在.cpp文件中重新实现函数内容

1. 重新实现特定事件

重新实现如 mousePressEvent() ,  keyPressEvent() 和 paintEvent() 等这些Qt事先已定义的事件处理器是进行事件处理的最简单的方式

2. 重新实现QObject::event()函数

通过重新实现event()函数, 可以在事件到达特定的事件处理器之前截获并处理它们. 这种方法可以用来覆盖已定义事件的默认处理方式,也可以用来处理Qt中尚未定义特定事件处理器的事件.当重新试下event()函数时, 如果不进行事件处理, 则需要调用基类的event()事件

3. 在QObject中注册事件过滤器

如果对象使用installEventFilter()函数注册了事件过滤器,目标对象中而所有事件将先发给这个监视对象的eventFilter()函数

4. 在QApplication中注册事件过滤器

如果一个事件过滤器被注册到程序中唯一的Qapplication对象,应用程序中的所有对象里的每一个事件都会在他们被送达其他事件过滤器前, 首先抵达这个eventFilter() 函数. 它可以用来处理一些通常被QApplication忽略的事件, 如发送给失效窗口的鼠标事件等.

5. 继承QApplication 并重新实现notify()函数

3 调用QWidgetPrivate::syncBackingStore()绘制

void QWidgetPrivate::syncBackingStore()
{
    if (paintOnScreen()) {
        repaint_sys(dirty);
        dirty = QRegion();
    } else if (QWidgetBackingStore *bs = maybeBackingStore()) {
        bs->sync();
    }
}

调用sync 

/*!
    Synchronizes the \a exposedRegion of the \a exposedWidget with the backing store.

    If there's nothing to repaint, the area is flushed and painting does not occur;
    otherwise the area is marked as dirty on screen and will be flushed right after
    we are done with all painting.
*/
void QWidgetBackingStore::sync(QWidget *exposedWidget, const QRegion &exposedRegion)
{
    QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
    if (!tlw->isVisible() || !tlwExtra || tlwExtra->inTopLevelResize)
        return;

    if (!exposedWidget || !exposedWidget->internalWinId() || !exposedWidget->isVisible() || !exposedWidget->testAttribute(Qt::WA_Mapped)
        || !exposedWidget->updatesEnabled() || exposedRegion.isEmpty()) {
        return;
    }

    // Nothing to repaint.
    if (!isDirty() && store->size().isValid()) {
        QPlatformTextureList *tl = widgetTexturesFor(tlw, exposedWidget);
        qt_flush(exposedWidget, tl ? QRegion() : exposedRegion, store, tlw, tlwOffset, tl, this);
        return;
    }

    if (exposedWidget != tlw)
        markDirtyOnScreen(exposedRegion, exposedWidget, exposedWidget->mapTo(tlw, QPoint()));
    else
        markDirtyOnScreen(exposedRegion, exposedWidget, QPoint());

    if (syncAllowed())
        doSync();//调用
}

doSync 

void QWidgetBackingStore::doSync()
{
    const bool updatesDisabled = !tlw->updatesEnabled();
    bool repaintAllWidgets = false;

    const bool inTopLevelResize = tlw->d_func()->maybeTopData()->inTopLevelResize;
    const QRect tlwRect(topLevelRect());
    const QRect surfaceGeometry(tlwRect.topLeft(), store->size());
    if ((inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) {
        if (hasStaticContents() && !store->size().isEmpty() ) {
            // Repaint existing dirty area and newly visible area.
            const QRect clipRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height());
            const QRegion staticRegion(staticContents(0, clipRect));
            QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height());
            newVisible -= staticRegion;
            dirty += newVisible;
            store->setStaticContents(staticRegion);
        } else {
            // Repaint everything.
            dirty = QRegion(0, 0, tlwRect.width(), tlwRect.height());
            for (int i = 0; i < dirtyWidgets.size(); ++i)
                resetWidget(dirtyWidgets.at(i));
            dirtyWidgets.clear();
            repaintAllWidgets = true;
        }
    }

    if (inTopLevelResize || surfaceGeometry.size() != tlwRect.size())
        store->resize(tlwRect.size());

    if (updatesDisabled)
        return;

    // Contains everything that needs repaint.
    QRegion toClean(dirty);

    // Loop through all update() widgets and remove them from the list before they are
    // painted (in case someone calls update() in paintEvent). If the widget is opaque
    // and does not have transparent overlapping siblings, append it to the
    // opaqueNonOverlappedWidgets list and paint it directly without composition.
    QVarLengthArray<QWidget *, 32> opaqueNonOverlappedWidgets;
    for (int i = 0; i < dirtyWidgets.size(); ++i) {
        QWidget *w = dirtyWidgets.at(i);
        QWidgetPrivate *wd = w->d_func();
        if (wd->data.in_destructor)
            continue;

        // Clip with mask() and clipRect().
        wd->dirty &= wd->clipRect();
        wd->clipToEffectiveMask(wd->dirty);

        // Subtract opaque siblings and children.
        bool hasDirtySiblingsAbove = false;
        // We know for sure that the widget isn't overlapped if 'isMoved' is true.
        if (!wd->isMoved)
            wd->subtractOpaqueSiblings(wd->dirty, &hasDirtySiblingsAbove);

        // Make a copy of the widget's dirty region, to restore it in case there is an opaque
        // render-to-texture child that completely covers the widget, because otherwise the
        // render-to-texture child won't be visible, due to its parent widget not being redrawn
        // with a proper blending mask.
        const QRegion dirtyBeforeSubtractedOpaqueChildren = wd->dirty;

        // Scrolled and moved widgets must draw all children.
        if (!wd->isScrolled && !wd->isMoved)
            wd->subtractOpaqueChildren(wd->dirty, w->rect());

        if (wd->dirty.isEmpty() && wd->textureChildSeen)
            wd->dirty = dirtyBeforeSubtractedOpaqueChildren;

        if (wd->dirty.isEmpty()) {
            resetWidget(w);
            continue;
        }

        const QRegion widgetDirty(w != tlw ? wd->dirty.translated(w->mapTo(tlw, QPoint()))
                                           : wd->dirty);
        toClean += widgetDirty;

#if QT_CONFIG(graphicsview)
        if (tlw->d_func()->extra->proxyWidget) {
            resetWidget(w);
            continue;
        }
#endif

        if (!hasDirtySiblingsAbove && wd->isOpaque && !dirty.intersects(widgetDirty.boundingRect())) {
            opaqueNonOverlappedWidgets.append(w);
        } else {
            resetWidget(w);
            dirty += widgetDirty;
        }
    }
    dirtyWidgets.clear();

#ifndef QT_NO_OPENGL
    // Find all render-to-texture child widgets (including self).
    // The search is cut at native widget boundaries, meaning that each native child widget
    // has its own list for the subtree below it.
    QTLWExtra *tlwExtra = tlw->d_func()->topData();
    qDeleteAll(tlwExtra->widgetTextures);
    tlwExtra->widgetTextures.clear();
    findAllTextureWidgetsRecursively(tlw, tlw);
    qt_window_private(tlw->windowHandle())->compositing = false; // will get updated in qt_flush()
#endif

    if (toClean.isEmpty()) {
        // Nothing to repaint. However renderToTexture widgets are handled
        // specially, they are not in the regular dirty list, in order to
        // prevent triggering unnecessary backingstore painting when only the
        // OpenGL content changes. Check if we have such widgets in the special
        // dirty list.
        QVarLengthArray<QWidget *, 16> paintPending;
        const int numPaintPending = dirtyRenderToTextureWidgets.count();
        paintPending.reserve(numPaintPending);
        for (int i = 0; i < numPaintPending; ++i) {
            QWidget *w = dirtyRenderToTextureWidgets.at(i);
            paintPending << w;
            resetWidget(w);
        }
        dirtyRenderToTextureWidgets.clear();
        for (int i = 0; i < numPaintPending; ++i) {
            QWidget *w = paintPending[i];
            w->d_func()->sendPaintEvent(w->rect());
            if (w != tlw) {
                QWidget *npw = w->nativeParentWidget();
                if (w->internalWinId() || (npw && npw != tlw)) {
                    if (!w->internalWinId())
                        w = npw;
                    QWidgetPrivate *wPrivate = w->d_func();
                    if (!wPrivate->needsFlush)
                        wPrivate->needsFlush = new QRegion;
                    appendDirtyOnScreenWidget(w);
                }
            }
        }

        // We might have newly exposed areas on the screen if this function was
        // called from sync(QWidget *, QRegion)), so we have to make sure those
        // are flushed. We also need to composite the renderToTexture widgets.
        flush();

        return;
    }

#ifndef QT_NO_OPENGL
    foreach (QPlatformTextureList *tl, tlwExtra->widgetTextures) {
        for (int i = 0; i < tl->count(); ++i) {
            QWidget *w = static_cast<QWidget *>(tl->source(i));
            if (dirtyRenderToTextureWidgets.contains(w)) {
                const QRect rect = tl->geometry(i); // mapped to the tlw already
                // Set a flag to indicate that the paint event for this
                // render-to-texture widget must not to be optimized away.
                w->d_func()->renderToTextureReallyDirty = 1;
                dirty += rect;
                toClean += rect;
            }
        }
    }
    for (int i = 0; i < dirtyRenderToTextureWidgets.count(); ++i)
        resetWidget(dirtyRenderToTextureWidgets.at(i));
    dirtyRenderToTextureWidgets.clear();
#endif

#if QT_CONFIG(graphicsview)
    if (tlw->d_func()->extra->proxyWidget) {
        updateStaticContentsSize();
        dirty = QRegion();
        updateRequestSent = false;
        for (const QRect &rect : toClean)
            tlw->d_func()->extra->proxyWidget->update(rect);
        return;
    }
#endif

    BeginPaintInfo beginPaintInfo;
    beginPaint(toClean, tlw, store, &beginPaintInfo);
    if (beginPaintInfo.nothingToPaint) {
        for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i)
            resetWidget(opaqueNonOverlappedWidgets[i]);
        dirty = QRegion();
        updateRequestSent = false;
        return;
    }

    // Must do this before sending any paint events because
    // the size may change in the paint event.
    updateStaticContentsSize();
    const QRegion dirtyCopy(dirty);
    dirty = QRegion();
    updateRequestSent = false;

    // Paint opaque non overlapped widgets.
    for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) {
        QWidget *w = opaqueNonOverlappedWidgets[i];
        QWidgetPrivate *wd = w->d_func();

        int flags = QWidgetPrivate::DrawRecursive;
        // Scrolled and moved widgets must draw all children.
        if (!wd->isScrolled && !wd->isMoved)
            flags |= QWidgetPrivate::DontDrawOpaqueChildren;
        if (w == tlw)
            flags |= QWidgetPrivate::DrawAsRoot;

        QRegion toBePainted(wd->dirty);
        resetWidget(w);

        QPoint offset(tlwOffset);
        if (w != tlw)
            offset += w->mapTo(tlw, QPoint());
        wd->drawWidget(store->paintDevice(), toBePainted, offset, flags, 0, this);
    }

    // Paint the rest with composition.
    if (repaintAllWidgets || !dirtyCopy.isEmpty()) {
        const int flags = QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawRecursive;
        tlw->d_func()->drawWidget(store->paintDevice(), dirtyCopy, tlwOffset, flags, 0, this);//调用
    }

    endPaint(toClean, store, &beginPaintInfo);
}

调用doSync调用  wd->drawWidget(store->paintDevice(), toBePainted, offset, flags, 0, this);函数进行绘制,函数的第一个参数是获取绘制设备,位置如下:

C:\Qt\Qt5.9.8\5.9.8\Src\qtbase\src\plugins\platforms\windows\qwindowsbackingstore.h

    QPaintDevice *paintDevice() override;
    QScopedPointer<QWindowsNativeImage> m_image;

QPaintDevice *QWindowsBackingStore::paintDevice()
{
    Q_ASSERT(!m_image.isNull());
    return &m_image->image();
}

paintDevice()  在window平台返回一张图片,最终绘制到一张图片上

C:\Qt\Qt5.9.8\5.9.8\Src\qtbase\src\platformsupport\fontdatabases\windows\qwindowsnativeimage_p.h

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#ifndef QWINDOWSNATIVEIMAGE_H
#define QWINDOWSNATIVEIMAGE_H

//
//  W A R N I N G
//  -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//

#include <QtCore/QtGlobal>
#include <QtCore/qt_windows.h>
#include <QtGui/QImage>

QT_BEGIN_NAMESPACE

class QWindowsNativeImage
{
    Q_DISABLE_COPY(QWindowsNativeImage)
public:
    QWindowsNativeImage(int width, int height,
                        QImage::Format format);

    ~QWindowsNativeImage();

    inline int width() const  { return m_image.width(); }
    inline int height() const { return m_image.height(); }

    QImage &image() { return m_image; }
    const QImage &image() const { return m_image; }

    HDC hdc() const { return m_hdc; }

    static QImage::Format systemFormat();

private:
    const HDC m_hdc;
    QImage m_image;

    HBITMAP m_bitmap = 0;
    HBITMAP m_null_bitmap = 0;
};

QT_END_NAMESPACE

#endif // QWINDOWSNATIVEIMAGE_H

windows重绘机制原理

https://blog.csdn.net/u011555996/article/details/78013698

drawWidget

void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QPoint &offset, int flags,
                                QPainter *sharedPainter, QWidgetBackingStore *backingStore)
{
    if (rgn.isEmpty())
        return;

    const bool asRoot = flags & DrawAsRoot;
    bool onScreen = paintOnScreen();

    Q_Q(QWidget);
#if QT_CONFIG(graphicseffect)
    if (graphicsEffect && graphicsEffect->isEnabled()) {
        QGraphicsEffectSource *source = graphicsEffect->d_func()->source;
        QWidgetEffectSourcePrivate *sourced = static_cast<QWidgetEffectSourcePrivate *>
                                                         (source->d_func());
        if (!sourced->context) {
            QWidgetPaintContext context(pdev, rgn, offset, flags, sharedPainter, backingStore);
            sourced->context = &context;
            if (!sharedPainter) {
                setSystemClip(pdev, rgn.translated(offset));
                QPainter p(pdev);
                p.translate(offset);
                context.painter = &p;
                graphicsEffect->draw(&p);
                setSystemClip(pdev, QRegion());
            } else {
                context.painter = sharedPainter;
                if (sharedPainter->worldTransform() != sourced->lastEffectTransform) {
                    sourced->invalidateCache();
                    sourced->lastEffectTransform = sharedPainter->worldTransform();
                }
                sharedPainter->save();
                sharedPainter->translate(offset);
                graphicsEffect->draw(sharedPainter);
                sharedPainter->restore();
            }
            sourced->context = 0;

            // Native widgets need to be marked dirty on screen so painting will be done in correct context
            // Same check as in the no effects case below.
            if (backingStore && !onScreen && !asRoot && (q->internalWinId() || !q->nativeParentWidget()->isWindow()))
                backingStore->markDirtyOnScreen(rgn, q, offset);

            return;
        }
    }
#endif // QT_CONFIG(graphicseffect)

    const bool alsoOnScreen = flags & DrawPaintOnScreen;
    const bool recursive = flags & DrawRecursive;
    const bool alsoInvisible = flags & DrawInvisible;

    Q_ASSERT(sharedPainter ? sharedPainter->isActive() : true);

    QRegion toBePainted(rgn);
    if (asRoot && !alsoInvisible)
        toBePainted &= clipRect(); //(rgn & visibleRegion());
    if (!(flags & DontSubtractOpaqueChildren))
        subtractOpaqueChildren(toBePainted, q->rect());

    if (!toBePainted.isEmpty()) {
        if (!onScreen || alsoOnScreen) {
            //update the "in paint event" flag
            if (Q_UNLIKELY(q->testAttribute(Qt::WA_WState_InPaintEvent)))
                qWarning("QWidget::repaint: Recursive repaint detected");
            q->setAttribute(Qt::WA_WState_InPaintEvent);

            //clip away the new area
#ifndef QT_NO_PAINT_DEBUG
            bool flushed = QWidgetBackingStore::flushPaint(q, toBePainted);
#endif
            QPaintEngine *paintEngine = pdev->paintEngine();
            if (paintEngine) {
                setRedirected(pdev, -offset);

#if 0 // Used to be included in Qt4 for Q_WS_MAC
                // (Alien support) Special case for Mac when redirecting: If the paint device
                // is of the Widget type we need to set WA_WState_InPaintEvent since painting
                // outside the paint event is not supported on QWidgets. The attributeis
                // restored further down.
                if (pdev->devType() == QInternal::Widget)
                    static_cast<QWidget *>(pdev)->setAttribute(Qt::WA_WState_InPaintEvent);

#endif
                if (sharedPainter)
                    setSystemClip(pdev, toBePainted);
                else
                    paintEngine->d_func()->systemRect = q->data->crect;

                //paint the background    1 绘制背景
                if ((asRoot || q->autoFillBackground() || onScreen || q->testAttribute(Qt::WA_StyledBackground))
                    && !q->testAttribute(Qt::WA_OpaquePaintEvent) && !q->testAttribute(Qt::WA_NoSystemBackground)) {
#ifndef QT_NO_OPENGL
                    beginBackingStorePainting();
#endif
                    QPainter p(q);
                    paintBackground(&p, toBePainted, (asRoot || onScreen) ? flags | DrawAsRoot : 0);
#ifndef QT_NO_OPENGL
                    endBackingStorePainting();
#endif
                }

                if (!sharedPainter)
                    setSystemClip(pdev, toBePainted.translated(offset));

                if (!onScreen && !asRoot && !isOpaque && q->testAttribute(Qt::WA_TintedBackground)) {
#ifndef QT_NO_OPENGL
                    beginBackingStorePainting();
#endif
                    QPainter p(q);
                    QColor tint = q->palette().window().color();
                    tint.setAlphaF(qreal(.6));
                    p.fillRect(toBePainted.boundingRect(), tint);
#ifndef QT_NO_OPENGL
                    endBackingStorePainting();
#endif
                }
            }

#if 0
            qDebug() << "painting" << q << "opaque ==" << isOpaque();
            qDebug() << "clipping to" << toBePainted << "location == " << offset
                     << "geometry ==" << QRect(q->mapTo(q->window(), QPoint(0, 0)), q->size());
#endif

            bool skipPaintEvent = false;
#ifndef QT_NO_OPENGL
            if (renderToTexture) {
                // This widget renders into a texture which is composed later. We just need to
                // punch a hole in the backingstore, so the texture will be visible.
                if (!q->testAttribute(Qt::WA_AlwaysStackOnTop)) {
                    beginBackingStorePainting();
                    if (backingStore) {
                        QPainter p(q);
                        p.setCompositionMode(QPainter::CompositionMode_Source);
                        p.fillRect(q->rect(), Qt::transparent);
                    } else {
                        QImage img = grabFramebuffer();
                        QPainter p(q);
                        // We are not drawing to a backingstore: fall back to QImage
                        p.drawImage(q->rect(), img);
                        skipPaintEvent = true;
                    }
                    endBackingStorePainting();
                }
                if (renderToTextureReallyDirty)
                    renderToTextureReallyDirty = 0;
                else
                    skipPaintEvent = true;
            }
#endif // QT_NO_OPENGL

            if (!skipPaintEvent) {
                //actually send the paint event 2 绘制前景
                sendPaintEvent(toBePainted);
            }

            // Native widgets need to be marked dirty on screen so painting will be done in correct context
            if (backingStore && !onScreen && !asRoot && (q->internalWinId() || (q->nativeParentWidget() && !q->nativeParentWidget()->isWindow())))
                backingStore->markDirtyOnScreen(toBePainted, q, offset);

            //restore
            if (paintEngine) {
#if 0 // Used to be included in Qt4 for Q_WS_MAC
                if (pdev->devType() == QInternal::Widget)
                    static_cast<QWidget *>(pdev)->setAttribute(Qt::WA_WState_InPaintEvent, false);
#endif
                restoreRedirected();
                if (!sharedPainter)
                    paintEngine->d_func()->systemRect = QRect();
                else
                    paintEngine->d_func()->currentClipDevice = 0;

                setSystemClip(pdev, QRegion());
            }
            q->setAttribute(Qt::WA_WState_InPaintEvent, false);
            if (Q_UNLIKELY(q->paintingActive()))
                qWarning("QWidget::repaint: It is dangerous to leave painters active on a widget outside of the PaintEvent");

            if (paintEngine && paintEngine->autoDestruct()) {
                delete paintEngine;
            }

#ifndef QT_NO_PAINT_DEBUG
            if (flushed)
                QWidgetBackingStore::unflushPaint(q, toBePainted);
#endif
        } else if (q->isWindow()) {
            QPaintEngine *engine = pdev->paintEngine();
            if (engine) {
                QPainter p(pdev);
                p.setClipRegion(toBePainted);
                const QBrush bg = q->palette().brush(QPalette::Window);
                if (bg.style() == Qt::TexturePattern)
                    p.drawTiledPixmap(q->rect(), bg.texture());
                else
                    p.fillRect(q->rect(), bg);

                if (engine->autoDestruct())
                    delete engine;
            }
        }
    }

    if (recursive && !children.isEmpty()) {
        //3 绘制子控件
        paintSiblingsRecursive(pdev, children, children.size() - 1, rgn, offset, flags & ~DrawAsRoot
                                , sharedPainter, backingStore);
    }
}

主体内容

1.绘制背景   paintBackground(&p, toBePainted, (asRoot || onScreen) ? flags | DrawAsRoot : 0);           

2 绘制前景 sendPaintEvent(toBePainted);//actually send th paint event 在paintevent 中处理的都是前景

调用paintevent事件:

void QWidgetPrivate::sendPaintEvent(const QRegion &toBePainted)
{
    Q_Q(QWidget);
    QPaintEvent e(toBePainted);
    QCoreApplication::sendSpontaneousEvent(q, &e);

#ifndef QT_NO_OPENGL
    if (renderToTexture)
        resolveSamples();
#endif // QT_NO_OPENGL
}

3 绘制子控件
        paintSiblingsRecursive(pdev, children, children.size() - 1, rgn, offset, flags & ~DrawAsRoot
                                , sharedPainter, backingStore);//树形绘制

children返回孩子控件的链表,先后顺序为Z序,在后面的孩子控件先绘制

void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectList& siblings, int index, const QRegion &rgn,
                                            const QPoint &offset, int flags
                                            , QPainter *sharedPainter, QWidgetBackingStore *backingStore)
{
    QWidget *w = 0;
    QRect boundingRect;
    bool dirtyBoundingRect = true;
    const bool exludeOpaqueChildren = (flags & DontDrawOpaqueChildren);
    const bool excludeNativeChildren = (flags & DontDrawNativeChildren);

    do {
        QWidget *x =  qobject_cast<QWidget*>(siblings.at(index));
        if (x && !(exludeOpaqueChildren && x->d_func()->isOpaque) && !x->isHidden() && !x->isWindow()
            && !(excludeNativeChildren && x->internalWinId())) {
            if (dirtyBoundingRect) {
                boundingRect = rgn.boundingRect();
                dirtyBoundingRect = false;
            }

            if (qRectIntersects(boundingRect, x->d_func()->effectiveRectFor(x->data->crect))) {
                w = x;
                break;
            }
        }
        --index;
    } while (index >= 0);

    if (!w)
        return;

    QWidgetPrivate *wd = w->d_func();
    const QPoint widgetPos(w->data->crect.topLeft());
    const bool hasMask = wd->extra && wd->extra->hasMask && !wd->graphicsEffect;
    if (index > 0) {
        QRegion wr(rgn);
        if (wd->isOpaque)
            wr -= hasMask ? wd->extra->mask.translated(widgetPos) : w->data->crect;
        paintSiblingsRecursive(pdev, siblings, --index, wr, offset, flags
                               , sharedPainter, backingStore);
    }

    if (w->updatesEnabled()
#if QT_CONFIG(graphicsview)
            && (!w->d_func()->extra || !w->d_func()->extra->proxyWidget)
#endif // QT_CONFIG(graphicsview)
       ) {
        QRegion wRegion(rgn);
        wRegion &= wd->effectiveRectFor(w->data->crect);
        wRegion.translate(-widgetPos);
        if (hasMask)
            wRegion &= wd->extra->mask;
        wd->drawWidget(pdev, wRegion, offset + widgetPos, flags, sharedPainter, backingStore);//绘制,形成树形绘制
    }
}

绘制完成后,呈现到屏幕上

最终调用:

void QWindowsBackingStore::flush(QWindow *window, const QRegion &region,
                                 const QPoint &offset)
{
    Q_ASSERT(window);

    const QRect br = region.boundingRect();
    if (QWindowsContext::verbose > 1)
        qCDebug(lcQpaBackingStore) << __FUNCTION__ << this << window << offset << br;
    QWindowsWindow *rw = QWindowsWindow::windowsWindowOf(window);
    Q_ASSERT(rw);

    const bool hasAlpha = rw->format().hasAlpha();
    const Qt::WindowFlags flags = window->flags();
    if ((flags & Qt::FramelessWindowHint) && QWindowsWindow::setWindowLayered(rw->handle(), flags, hasAlpha, rw->opacity()) && hasAlpha) {
        // Windows with alpha: Use blend function to update.
        QRect r = QHighDpi::toNativePixels(window->frameGeometry(), window);
        QPoint frameOffset(QHighDpi::toNativePixels(QPoint(window->frameMargins().left(), window->frameMargins().top()),
                                                    static_cast<const QWindow *>(Q_NULLPTR)));
        QRect dirtyRect = br.translated(offset + frameOffset);

        SIZE size = {r.width(), r.height()};
        POINT ptDst = {r.x(), r.y()};
        POINT ptSrc = {0, 0};
        BLENDFUNCTION blend = {AC_SRC_OVER, 0, BYTE(qRound(255.0 * rw->opacity())), AC_SRC_ALPHA};
        RECT dirty = {dirtyRect.x(), dirtyRect.y(),
                      dirtyRect.x() + dirtyRect.width(), dirtyRect.y() + dirtyRect.height()};
        UPDATELAYEREDWINDOWINFO info = {sizeof(info), NULL, &ptDst, &size, m_image->hdc(), &ptSrc, 0, &blend, ULW_ALPHA, &dirty};
        const BOOL result = UpdateLayeredWindowIndirect(rw->handle(), &info);
        if (!result)
            qErrnoWarning("UpdateLayeredWindowIndirect failed for ptDst=(%d, %d),"
                          " size=(%dx%d), dirty=(%dx%d %d, %d)", r.x(), r.y(),
                          r.width(), r.height(), dirtyRect.width(), dirtyRect.height(),
                          dirtyRect.x(), dirtyRect.y());
    } else {
        const HDC dc = rw->getDC();
        if (!dc) {
            qErrnoWarning("%s: GetDC failed", __FUNCTION__);
            return;
        }

        if (!BitBlt(dc, br.x(), br.y(), br.width(), br.height(),
                    m_image->hdc(), br.x() + offset.x(), br.y() + offset.y(), SRCCOPY)) {
            const DWORD lastError = GetLastError(); // QTBUG-35926, QTBUG-29716: may fail after lock screen.
            if (lastError != ERROR_SUCCESS && lastError != ERROR_INVALID_HANDLE)
                qErrnoWarning(int(lastError), "%s: BitBlt failed", __FUNCTION__);
        }
        rw->releaseDC();
    }

    // Write image for debug purposes.
    if (QWindowsContext::verbose > 2 && lcQpaBackingStore().isDebugEnabled()) {
        static int n = 0;
        const QString fileName = QString::fromLatin1("win%1_%2.png").
                arg(rw->winId()).arg(n++);
        m_image->image().save(fileName);
        qCDebug(lcQpaBackingStore) << "Wrote " << m_image->image().size() << fileName;
    }
}

BitBlt将图片绘制到显示屏上

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值