1. 程式人生 > >Qt GUI 通過滑鼠事件剖析整個事件處理流程(基於Qt4.5.2原始碼windows平臺)

Qt GUI 通過滑鼠事件剖析整個事件處理流程(基於Qt4.5.2原始碼windows平臺)

/*以下分析的是Windows平臺 Qt GUI程式的事件產生,分發,處理全過程(基於Qt5.4.2原始碼整理)
以一個滑鼠按壓的事件為例子講述
...表示省略掉一些程式碼(skip code)

事件起源: 基於事件如何被產生與分發,可以把事件分為以下三類。

Spontaneous 事件——自發事件
由視窗系統產生,它們被放到系統佇列中,通過事件迴圈逐個處理。(例如滑鼠事件、鍵盤事件)

Posted 事件
由Qt或是應用程式產生,它們被Qt組成佇列,再通過事件迴圈處理。

Sent 事件
由Qt或是應用程式產生,但它們被直接傳送到目標物件。

Spontaneous 事件、Posted 事件都是post到事件佇列,迴圈處理

因為都是程式碼,所以推薦用Notepad++開啟本文件
*/
//函式呼叫關係
main(int, char **)   
QApplication::exec() 
QCoreApplication::exec()   
QEventLoop::exec(ProcessEventsFlags )   
QEventLoop::processEvents(ProcessEventsFlags )
QWindowsGuiEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
QWindowsGuiEventDispatcher::sendPostedEvents()
QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::ProcessEventsFlags flags)
QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e)
QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent *e)
QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event)
QCoreApplication::notifyInternal(QObject *receiver, QEvent *event)
QApplication::notify(QObject *receiver, QEvent *e)
QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)
QWidgetWindow::event(QEvent *event)
QWidgetWindow::handleMouseEvent(QMouseEvent *event)
QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event,QWidget *alienWidget, QWidget *nativeWidget,
									QWidget **buttonDown, QPointer<QWidget> &lastMouseReceiver,bool spontaneous)
QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event)
QCoreApplication::notifyInternal(QObject *receiver, QEvent *event)
QApplication::notify(QObject *receiver, QEvent *e)
QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)
QWidget::event(QEvent *event)

//section 1 
//Qt 程式入口,開啟事件迴圈
int main(int argc, char *argv[])     
{     
    QApplication app(argc, argv);     
    Widget window;  // Widget 繼承自QWidget     
    window.show();     
    return app.exec(); // 進入QApplication事件迴圈=>見section 2     
} 
//section 2 qapplication.cpp
int QApplication::exec()
{
	//呼叫QGuiApplication的exec=>見section 3
    return QGuiApplication::exec();
}
//section 3 qguiapplication.cpp
int QGuiApplication::exec()
{
	//如果可訪問,設定根物件
#ifndef QT_NO_ACCESSIBILITY
    QAccessible::setRootObject(qApp);
#endif
	//呼叫QCoreApplication的exec=>見section 4
    return QCoreApplication::exec();
}
//section 4 qcoreapplication.cpp
int QCoreApplication::exec()
{
	...
    QEventLoop eventLoop;
    self->d_func()->in_exec = true;
    self->d_func()->aboutToQuitEmitted = false;
	//委任eventLoop處理事件迴圈=>見section 5
    int returnCode = eventLoop.exec();
	...
    return returnCode;
}
//section 5 qeventLoop.cpp
int QEventLoop::exec(ProcessEventsFlags flags)
{
	//獲取私有資料指標d
    Q_D(QEventLoop);
	...
	//只要沒遇到退出就迴圈派發事件=>見section 6
    while (!d->exit.loadAcquire())
	{
		processEvents(flags | WaitForMoreEvents | EventLoopExec);
	}
    ...
    return d->returnCode.load();
}
//section 6 qeventLoop.cpp
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
	//如果事件分發器等於空就返回false
    if (!d->threadData->eventDispatcher.load())
        return false;
	
	//將事件派發給與平臺相關的QAbstractEventDispatcher子類 =>Section 7
    return d->threadData->eventDispatcher.load()->processEvents(flags);
}
//section 7 qwindowsguieventdispatcher.cpp
// 這段程式碼是完成與windows平臺相關的windows c++。以跨平臺著稱的Qt同時也提供了對Unix,Symiban等平臺的訊息派發支援         
// QWindowsGuiEventDispatcher派生自QEventDispatcherWin32  QEventDispatcherWin32派生自QAbstractEventDispatcher. 
bool QWindowsGuiEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
{
    m_flags = flags;
    ...
	//呼叫基類的事件處理函式=>見section 8
    const bool rc = QEventDispatcherWin32::processEvents(flags);
    ...
    return rc;
}
//section 8 qeventdispatcher_win.cpp  
bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
    Q_D(QEventDispatcherWin32);
    ...
    bool canWait;
    bool retVal = false;
    bool seenWM_QT_SENDPOSTEDEVENTS = false;
    bool needWM_QT_SENDPOSTEDEVENTS = false;
    do {
        DWORD waitRet = 0;
        HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1];
        QVarLengthArray<MSG> processedTimers;
        while (!d->interrupt) 
		{
            DWORD nCount = d->winEventNotifierList.count();
            Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);

            MSG msg;
            bool haveMessage;
			//事件迴圈標誌沒有排除使用者輸入標誌(ExcludeUserInputEvents) 、使用者輸入事件佇列非空
            if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) 
			{
                // process queued user input events
                haveMessage = true;
				//把使用者輸入事件佇列的第一個事件取出
                msg = d->queuedUserInputEvents.takeFirst();
            } 
			//事件迴圈標誌沒有排除socket通知標誌(ExcludeSocketNotifiers) 、socket事件佇列非空
			else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) 
			{
                // process queued socket events
                haveMessage = true;
				//把socket事件佇列的第一個事件取出
                msg = d->queuedSocketEvents.takeFirst();
            }
			else 
			{
				//偷窺一下,看有沒有訊息
                haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
                if (haveMessage 
					&& (flags & QEventLoop::ExcludeUserInputEvents)
                    && ((msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST)
                    || (msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST)
                    || msg.message == WM_MOUSEWHEEL
                    || msg.message == WM_MOUSEHWHEEL
                    || msg.message == WM_TOUCH
#ifndef QT_NO_GESTURES
                    || msg.message == WM_GESTURE
                    || msg.message == WM_GESTURENOTIFY
#endif
                    || msg.message == WM_CLOSE)) 
				{
                    // queue user input events for later processing
                    haveMessage = false;
                    d->queuedUserInputEvents.append(msg);
                }
                if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers)
                    && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) 
				{
                    // queue socket events for later processing
                    haveMessage = false;
                    d->queuedSocketEvents.append(msg);
                }
            }
			
            if (!haveMessage) 
			{
                // no message - check for signalled objects
                for (int i=0; i<(int)nCount; i++)
				{
					pHandles[i] = d->winEventNotifierList.at(i)->handle();
				}
                    
                waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE);
                if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) 
				{
                    // a new message has arrived, process it
                    continue;
                }
            }
            if (haveMessage) 
			{
                // WinCE doesn't support hooks at all, so we have to call this by hand :(
                if (!d->getMessageHook)
				{
					(void) qt_GetMessageHook(0, PM_REMOVE, (LPARAM) &msg);
				}
                    
                if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) 
				{
                    if (seenWM_QT_SENDPOSTEDEVENTS) 
					{
                        // when calling processEvents() "manually", we only want to send posted
                        // events once
                        needWM_QT_SENDPOSTEDEVENTS = true;
                        continue;
                    }
                    seenWM_QT_SENDPOSTEDEVENTS = true;
                } 
				else if (msg.message == WM_TIMER) 
				{
                    // avoid live-lock by keeping track of the timers we've already sent
                    bool found = false;
                    for (int i = 0; !found && i < processedTimers.count(); ++i) 
					{
                        const MSG processed = processedTimers.constData()[i];
                        found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam);
                    }
                    if (found)
					{
                        continue;	
					}
                    processedTimers.append(msg);
                }
				else if (msg.message == WM_QUIT) 
				{
                    if (QCoreApplication::instance())
					{
                        QCoreApplication::instance()->quit();
					}
                    return false;
                }

                if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) 
				{
					//將事件打包成message呼叫Windows API派發出去     
                    //分發一個訊息給視窗程式。訊息被分發到回撥函式,將訊息傳遞給windows系統,windows處理完畢,會呼叫回撥函式 => section 9 
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            } 
			else if (waitRet - WAIT_OBJECT_0 < nCount) 
			{
                d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));
            } 
			else 
			{
                // nothing todo so break
                break;
            }
            retVal = true;
        }

        // still nothing - wait for message or signalled objects
        canWait = (!retVal
                   && !d->interrupt
                   && (flags & QEventLoop::WaitForMoreEvents));
        if (canWait) 
		{
            DWORD nCount = d->winEventNotifierList.count();
            Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);
            for (int i=0; i<(int)nCount; i++)
			{
                pHandles[i] = d->winEventNotifierList.at(i)->handle();
			}

            emit aboutToBlock();
            waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
            emit awake();
            if (waitRet - WAIT_OBJECT_0 < nCount) 
			{
                d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));
                retVal = true;
            }
        }
    } while (canWait);

    if (!seenWM_QT_SENDPOSTEDEVENTS && (flags & QEventLoop::EventLoopExec) == 0) 
	{
        // when called "manually", always send posted events
        sendPostedEvents();
    }

    if (needWM_QT_SENDPOSTEDEVENTS)
	{
        PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0);
	}
	
    return retVal;
}
//section 9 qeventdispatcher_win.cpp 
//呼叫回撥,將事件給視窗處理
LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
{
    if (message == WM_NCCREATE)
        return true;
	//重新組織訊息
    MSG msg;
    msg.hwnd = hwnd;
    msg.message = message;
    msg.wParam = wp;
    msg.lParam = lp;
    QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance();
    long result;
    if (!dispatcher) 
	{
        if (message == WM_TIMER)
            KillTimer(hwnd, wp);
        return 0;
    } 
	else if (dispatcher->filterNativeEvent(QByteArrayLiteral("windows_dispatcher_MSG"), &msg, &result)) 
	{
        return result;
    }

#ifdef GWLP_USERDATA
    QEventDispatcherWin32 *q = (QEventDispatcherWin32 *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
#else
    QEventDispatcherWin32 *q = (QEventDispatcherWin32 *) GetWindowLong(hwnd, GWL_USERDATA);
#endif
    QEventDispatcherWin32Private *d = 0;
    if (q != 0)
        d = q->d_func();

    if (message == WM_QT_SOCKETNOTIFIER) 
	{
        // socket notifier message
        int type = -1;
        switch (WSAGETSELECTEVENT(lp)) 
		{
        case FD_READ:
        case FD_ACCEPT:
            type = 0;
            break;
        case FD_WRITE:
        case FD_CONNECT:
            type = 1;
            break;
        case FD_OOB:
            type = 2;
            break;
        case FD_CLOSE:
            type = 3;
            break;
        }
        if (type >= 0) 
		{
            Q_ASSERT(d != 0);
            QSNDict *sn_vec[4] = { &d->sn_read, &d->sn_write, &d->sn_except, &d->sn_read };
            QSNDict *dict = sn_vec[type];

            QSockNot *sn = dict ? dict->value(wp) : 0;
            if (sn) 
			{
                if (type < 3) 
				{
                    QEvent event(QEvent::SockAct);
                    QCoreApplication::sendEvent(sn->obj, &event);
                } else 
				{
                    QEvent event(QEvent::SockClose);
                    QCoreApplication::sendEvent(sn->obj, &event);
                }
            }
        }
        return 0;
    } 
	else if (message == WM_QT_SENDPOSTEDEVENTS
               // we also use a Windows timer to send posted events when the message queue is full
               || (message == WM_TIMER
                   && d->sendPostedEventsWindowsTimerId != 0
                   && wp == (uint)d->sendPostedEventsWindowsTimerId)) 
	{
        const int localSerialNumber = d->serialNumber.load();
        if (localSerialNumber != d->lastSerialNumber) 
		{
            d->lastSerialNumber = localSerialNumber;
			//派發post事件=> section 10
            q->sendPostedEvents();
        }
        return 0;
    } 
	else if (message == WM_TIMER) 
	{
        Q_ASSERT(d != 0);
        d->sendTimerEvent(wp);
        return 0;
    }
	//windows預設處理
    return DefWindowProc(hwnd, message, wp, lp);
}

//從Section 1~Section9, Qt進入QApplication的event loop,經過層層委任,
//最終QEventloop的processEvent將通過與平臺相關的QAbstractEventDispatcher的子類QEventDispatcherWin32獲得使用者的使用者輸入事件,
//並將其打包成message後,通過標準Windows API ,把訊息傳遞給了Windows,Windows得到通知後回撥qt_internal_proc,  至此事件的分發與處理完成了一半的路程。

//section 10 qwindowsguieventdispatcher.cpp
void QWindowsGuiEventDispatcher::sendPostedEvents()
{
    QCoreApplication::sendPostedEvents();
	//傳送windows系統事件m_flags == allEvents => section 11
    QWindowSystemInterface::sendWindowSystemEvents(m_flags);
}
//section 11 qwindowsysteminterface.cpp
bool QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::ProcessEventsFlags flags)
{
    int nevents = 0;

    while (QWindowSystemInterfacePrivate::windowSystemEventsQueued()) 
	{
        QWindowSystemInterfacePrivate::WindowSystemEvent *event =
            (flags & QEventLoop::ExcludeUserInputEvents) ?
                QWindowSystemInterfacePrivate::getNonUserInputWindowSystemEvent() :
                QWindowSystemInterfacePrivate::getWindowSystemEvent();
        if (!event)
            break;
        nevents++;
		//處理windows系統事件 => section 12
        QGuiApplicationPrivate::processWindowSystemEvent(event);
        delete event;
    }

    return (nevents > 0);
}
//section 12 qguiapplication.cpp
void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e)
{
    switch(e->type) 
	{
    case QWindowSystemInterfacePrivate::FrameStrutMouse:
    case QWindowSystemInterfacePrivate::Mouse:
		//處理滑鼠事件=> section 13
        QGuiApplicationPrivate::processMouseEvent(static_cast<QWindowSystemInterfacePrivate::MouseEvent *>(e));
        break;
	...
    }
}
//section 13 qguiapplication.cpp
void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent *e)
{
    QEvent::Type type;
    Qt::MouseButtons stateChange = e->buttons ^ buttons;
    ...
	//這裡實際物件指標是QWidgetWindow*
    QWindow *window = e->window.data();
    modifier_buttons = e->modifiers;

    QPointF localPoint = e->localPos;
    QPointF globalPoint = e->globalPos;

    if (e->nullWindow) 
	{
        window = QGuiApplication::topLevelAt(globalPoint.toPoint());
        if (window) 
		{
            QPointF delta = globalPoint - globalPoint.toPoint();
            localPoint = window->mapFromGlobal(globalPoint.toPoint()) + delta;
        }
    }

    Qt::MouseButton button = Qt::NoButton;
    bool doubleClick = false;
    const bool frameStrut = e->type == QWindowSystemInterfacePrivate::FrameStrutMouse;

    if (QGuiApplicationPrivate::lastCursorPosition != globalPoint) 
	{
        type = frameStrut ? QEvent::NonClientAreaMouseMove : QEvent::MouseMove;
        QGuiApplicationPrivate::lastCursorPosition = globalPoint;
        if (qAbs(globalPoint.x() - mousePressX) > mouse_double_click_distance||
            qAbs(globalPoint.y() - mousePressY) > mouse_double_click_distance)
			{
				mousePressButton = Qt::NoButton;
			}  
    }
	else 
	{ // Check to see if a new button has been pressed/released.
        for (int check = Qt::LeftButton; check <= int(Qt::MaxMouseButton); check = check << 1) {
            if (check & stateChange) 
			{
                button = Qt::MouseButton(check);
                break;
            }
        }
        if (button == Qt::NoButton) 
		{
            // Ignore mouse events that don't change the current state.
            return;
        }
        mouse_buttons = buttons = e->buttons;
        if (button & e->buttons) 
		{
            ulong doubleClickInterval = static_cast<ulong>(qApp->styleHints()->mouseDoubleClickInterval());
            doubleClick = e->timestamp - mousePressTime < doubleClickInterval && button == mousePressButton;
            type = frameStrut ? QEvent::NonClientAreaMouseButtonPress : QEvent::MouseButtonPress;
            mousePressTime = e->timestamp;
            mousePressButton = button;
            const QPoint point = QGuiApplicationPrivate::lastCursorPosition.toPoint();
            mousePressX = point.x();
            mousePressY = point.y();
        } 
		else 
		{
            type = frameStrut ? QEvent::NonClientAreaMouseButtonRelease : QEvent::MouseButtonRelease;
        }
    }

    if (!window)
	{
		return;
	}
        
	//這裡構造了Qt的滑鼠事件QMouseEvent
    QMouseEvent ev(type, localPoint, localPoint, globalPoint, button, buttons, e->modifiers);
    ev.setTimestamp(e->timestamp);
    setMouseEventSource(&ev, e->source);
#ifndef QT_NO_CURSOR
    if (!e->synthetic) 
	{
        if (const QScreen *screen = window->screen())
            if (QPlatformCursor *cursor = screen->handle()->cursor())
                cursor->pointerEvent(ev);
    }
#endif

    if (window->d_func()->blockedByModalWindow) 
	{
        // a modal window is blocking this window, don't allow mouse events through
        return;
    }

    if (doubleClick && (ev.type() == QEvent::MouseButtonPress)) 
	{
        // QtBUG-25831, used to suppress delivery in qwidgetwindow.cpp
        setMouseEventFlags(&ev, ev.flags() | Qt::MouseEventCreatedDoubleClick);
    }
	
	//傳送事件給相應視窗=> section 14
    QGuiApplication::sendSpontaneousEvent(window, &ev);
	
    if (!e->synthetic && !ev.isAccepted()
        && !frameStrut
        && qApp->testAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents)) 
	{
        if (!m_fakeTouchDevice) 
		{
            m_fakeTouchDevice = new QTouchDevice;
            QWindowSystemInterface::registerTouchDevice(m_fakeTouchDevice);
        }
        QList<QWindowSystemInterface::TouchPoint> points;
        QWindowSystemInterface::TouchPoint point;
        point.id = 1;
        point.area = QRectF(globalPoint.x() - 2, globalPoint.y() - 2, 4, 4);

        // only translate left button related events to
        // avoid strange touch event sequences when several
        // buttons are pressed
        if (type == QEvent::MouseButtonPress && button == Qt::LeftButton) {
            point.state = Qt::TouchPointPressed;
        } else if (type == QEvent::MouseButtonRelease && button == Qt::LeftButton) {
            point.state = Qt::TouchPointReleased;
        } else if (type == QEvent::MouseMove && (buttons & Qt::LeftButton)) {
            point.state = Qt::TouchPointMoved;
        } else {
            return;
        }

        points << point;

        QEvent::Type type;
        QList<QTouchEvent::TouchPoint> touchPoints = QWindowSystemInterfacePrivate::convertTouchPoints(points, &type);

        QWindowSystemInterfacePrivate::TouchEvent fake(window, e->timestamp, type, m_fakeTouchDevice, touchPoints, e->modifiers);
        fake.synthetic = true;
        processTouchEvent(&fake);
    }
    if (doubleClick) 
	{
        mousePressButton = Qt::NoButton;
        if (!e->window.isNull() || e->nullWindow) { // QTBUG-36364, check if window closed in response to press
            const QEvent::Type doubleClickType = frameStrut ? QEvent::NonClientAreaMouseButtonDblClick : QEvent::MouseButtonDblClick;
            QMouseEvent dblClickEvent(doubleClickType, localPoint, localPoint, globalPoint,
                                      button, buttons, e->modifiers);
            dblClickEvent.setTimestamp(e->timestamp);
            setMouseEventSource(&dblClickEvent, e->source);
			//傳送事件給相應視窗=> section 14
            QGuiApplication::sendSpontaneousEvent(window, &dblClickEvent);
        }
    }
}
//section 14 qcoreapplication.cpp
inline bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event)
{ 
	//作為自發事件處理=> section 15
	if (event) 
		event->spont = true; 
	return self ? self->notifyInternal(receiver, event) : false; 
}
//section 15 qcoreapplication.cpp
bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event)
{
    // Make it possible for Qt Script to hook into events even
    // though QApplication is subclassed...
    bool result = false;
    void *cbdata[] = { receiver, event, &result };
    if (QInternal::activateCallbacks(QInternal::EventNotifyCallback, cbdata)) {
        return result;
    }

    // Qt enforces the rule that events can only be sent to objects in
    // the current thread, so receiver->d_func()->threadData is
    // equivalent to QThreadData::current(), just without the function
    // call overhead.
    QObjectPrivate *d = receiver->d_func();
    QThreadData *threadData = d->threadData;
    QScopedLoopLevelCounter loopLevelCounter(threadData);
	//準備通知接受者(這裡是QWidgetWindow*)=> section 16
    return notify(receiver, event);
}
//section 16 qapplication.cpp
bool QApplication::notify(QObject *receiver, QEvent *e)
{
    Q_D(QApplication);
	...
    bool res = false;
    if (!receiver->isWidgetType()) 
	{
		//通知接受者=> section 17
        res = d->notify_helper(receiver, e);
    } 
	...

    return res;
}
//section 17 qapplication.cpp
bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)
{
    // send to all application event filters
    if (sendThroughApplicationEventFilters(receiver, e))
        return true;

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

#if !defined(Q_OS_WINCE) || (defined(GWES_ICONCURS) && !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
    if (sendThroughObjectEventFilters(receiver, e))
        return true;

    // deliver the event =>section 18
    bool consumed = receiver->event(e);
    QCoreApplicationPrivate::setEventSpontaneous(e, false);
    return consumed;
}
//section 18 qwidgetwindow.cpp
bool QWidgetWindow::event(QEvent *event)
{
    if (m_widget->testAttribute(Qt::WA_DontShowOnScreen)) 
	{
        // \a event is uninteresting for QWidgetWindow, the event was probably
        // generated before WA_DontShowOnScreen was set
        return m_widget->event(event);
    }

    switch (event->type()) 
	{
    ...
    case QEvent::MouseMove:
    case QEvent::MouseButtonPress:
    case QEvent::MouseButtonRelease:
    case QEvent::MouseButtonDblClick:
		//處理滑鼠事件 =>section 19
        handleMouseEvent(static_cast<QMouseEvent *>(event));
        return true;
	...
    }

    return m_widget->event(event) || QWindow::event(event);
}
//section 19 qwidgetwindow.cpp
void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
{
	...
	QWidget *receiver = QApplicationPrivate::pickMouseReceiver(m_widget, 
															   event->windowPos().toPoint(), 
															   &mapped, event->type(), event->buttons(),
                                                               qt_button_down, widget);
	...
    if ((event->type() != QEvent::MouseButtonPress)
        || !(event->flags().testFlag(Qt::MouseEventCreatedDoubleClick))) {

        // The preceding statement excludes MouseButtonPress events which caused
        // creation of a MouseButtonDblClick event. QTBUG-25831
        QMouseEvent translated(event->type(), mapped, event->windowPos(), event->screenPos(),
                               event->button(), event->buttons(), event->modifiers());
        QGuiApplicationPrivate::setMouseEventSource(&translated, QGuiApplicationPrivate::mouseEventSource(event));
        translated.setTimestamp(event->timestamp());
		//傳送滑鼠事件=>section 20
        QApplicationPrivate::sendMouseEvent(receiver, &translated, widget, m_widget,
                                            &qt_button_down, qt_last_mouse_receiver);
    }
	...
}
//section 20 qapplication.cpp
bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event,
                                         QWidget *alienWidget, QWidget *nativeWidget,
                                         QWidget **buttonDown, QPointer<QWidget> &lastMouseReceiver,
                                         bool spontaneous)
{
	...
	//傳送自發事件=> section 21
    if (spontaneous)
        result = QApplication::sendSpontaneousEvent(receiver, event);
    ...
}
//section 21 qcoreapplication.cpp
inline bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event)
{ 
	//作為自發事件處理=> section 22
	if (event) 
		event->spont = true; 
	return self ? self->notifyInternal(receiver, event) : false; 
}
//section 22 qcoreapplication.cpp
bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event)
{
    // Make it possible for Qt Script to hook into events even
    // though QApplication is subclassed...
    bool result = false;
    void *cbdata[] = { receiver, event, &result };
    if (QInternal::activateCallbacks(QInternal::EventNotifyCallback, cbdata)) {
        return result;
    }

    // Qt enforces the rule that events can only be sent to objects in
    // the current thread, so receiver->d_func()->threadData is
    // equivalent to QThreadData::current(), just without the function
    // call overhead.
    QObjectPrivate *d = receiver->d_func();
    QThreadData *threadData = d->threadData;
    QScopedLoopLevelCounter loopLevelCounter(threadData);
	//準備通知接受者(這裡就是我們的widget)=> section 23
    return notify(receiver, event);
}
//section 23 qapplication.cpp
bool QApplication::notify(QObject *receiver, QEvent *e)
{
    Q_D(QApplication);
	...
    bool res = false;
    if (!receiver->isWidgetType()) 
	{
		//通知接受者=> section 24
        res = d->notify_helper(receiver, e);
    } 
	...

    return res;
}
//section 24 qapplication.cpp
bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)
{
    // send to all application event filters
    if (sendThroughApplicationEventFilters(receiver, e))
        return true;

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

#if !defined(Q_OS_WINCE) || (defined(GWES_ICONCURS) && !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
    if (sendThroughObjectEventFilters(receiver, e))
        return true;

    // deliver the event =>section 25
    bool consumed = receiver->event(e);
    QCoreApplicationPrivate::setEventSpontaneous(e, false);
    return consumed;
}
//section 25 qwidget.cp
bool QWidget::event(QEvent *event)
{
    Q_D(QWidget);

	...
    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;
	...
    default:
        return QObject::event(event);
    }
    return true;
}