Qt之事件循环源码剖析(二)-----源码面前了无秘密

上一篇说到QApplication注册了一个qt_internal_proc方法来处理消息循环,但是在整个过程中都没有看到处理用户输入事件的过程。例如鼠标事件、键盘事件等。

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

上篇文章分析了QApplication的构造,主事件循环启动、运行。 对于鼠标、键盘事件的响应肯定依托于一个可见的窗口,QApplication本身并不属于窗体,接下来针对于QWidget展开分析。

一、启动用户输入事件循环

Step 1

// src\widgets\kernel\qwidget.cpp
void QWidget::show()
{
    Qt::WindowState defaultState = QGuiApplicationPrivate::platformIntegration()->defaultWindowState(data->window_flags);
    if (defaultState == Qt::WindowFullScreen)
        showFullScreen();
    else if (defaultState == Qt::WindowMaximized)
        showMaximized();
    else
        setVisible(true); // Don't call showNormal() as not to clobber Qt::Window(Max/Min)imized
}
void QWidget::setVisible(bool visible)
{
    if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden) == !visible)
        return;

    // Remember that setVisible was called explicitly
    setAttribute(Qt::WA_WState_ExplicitShowHide);

    Q_D(QWidget);
    d->setVisible(visible);
}
void QWidgetPrivate::setVisible(bool visible)
{
    Q_Q(QWidget);
    if (visible) { // show
    /**
	* 代码省略 .......
	*/
        //create toplevels but not children of non-visible parents
        QWidget *pw = q->parentWidget();
        if (!q->testAttribute(Qt::WA_WState_Created)
            && (q->isWindow() || pw->testAttribute(Qt::WA_WState_Created))) {
            q->create();
        }
    }
}

QWidget在调用show函数显示一个窗口时,无论是全屏显示还是最大化显示最总都会调用QWidgetPrivate::setVisible函数。由于此Widget无父对象,在构造时将type设置为Qt::Window,因此会调用QWidget::create函数
Step 2

// src\widgets\kernel\qwidget.cpp
void QWidget::create(WId window, bool initializeWindow, bool destroyOldWindow)
{
    /**
	* 代码省略 .......
	*/
    setAttribute(Qt::WA_WState_Created);                        // set created flag
    d->create();
    /**
	* 代码省略 .......
	*/
}
void QWidgetPrivate::create()
{
    /**
	* 代码省略 .......
	*/
    QWidgetWindow *win = topData()->window;
    // topData() ensures the extra is created but does not ensure 'window' is non-null
    // in case the extra was already valid.
    if (!win) {
        createTLSysExtra();
        win = topData()->window;
    }
    /**
	* 代码省略 .......
	*/
    if (q->windowType() != Qt::Desktop || q->testAttribute(Qt::WA_NativeWindow)) {
        win->create();
        // Enable nonclient-area events for QDockWidget and other NonClientArea-mouse event processing.
        if (QPlatformWindow *platformWindow = win->handle())
            platformWindow->setFrameStrutEventsEnabled(true);
    }  
    /**
	* 代码省略 .......
	*/      
}
void QWidgetPrivate::createTLSysExtra()
{
    Q_Q(QWidget);
    if (!extra->topextra->window && (q->testAttribute(Qt::WA_NativeWindow) || q->isWindow())) {
        extra->topextra->window = new QWidgetWindow(q);
    /**
	* 代码省略 .......
	*/    
    }
}

通过QWidgetPrivate::createTLSysExtra()创建QWidgetWindow对象,q->windowType()的类型为Qt::Window,通过QWindowPrivate::create创建窗口
Step 3

// src\widgets\kernel\qwidget.cpp
void QWindowPrivate::create(bool recursive, WId nativeHandle)
{
    Q_Q(QWindow);
    if (platformWindow)
        return;

    // avoid losing update requests when re-creating
    const bool needsUpdate = updateRequestPending;
    // the platformWindow, if there was one, is now gone, so make this flag reflect reality now
    updateRequestPending = false;

    if (q->parent())
        q->parent()->create();

    QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration();
    platformWindow = nativeHandle ? platformIntegration->createForeignWindow(q, nativeHandle)
        : platformIntegration->createPlatformWindow(q);
    /**
	* 代码省略 .......
	*/           
}
// src\plugins\platforms\windows\qwindowsintegration.cpp
QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) const
{
    if (window->type() == Qt::Desktop) {
        auto *result = new QWindowsDesktopWindow(window);
        qCDebug(lcQpaWindows) << "Desktop window:" << window
            << Qt::showbase << Qt::hex << result->winId() << Qt::noshowbase << Qt::dec << result->geometry();
        return result;
    }

    QWindowsWindowData requested;
    requested.flags = window->flags();
    requested.geometry = window->isTopLevel()
        ? QHighDpi::toNativePixels(window->geometry(), window)
        : QHighDpi::toNativeLocalPosition(window->geometry(), window);
    // Apply custom margins (see  QWindowsWindow::setCustomMargins())).
    const QVariant customMarginsV = window->property("_q_windowsCustomMargins");
    if (customMarginsV.isValid())
        requested.customMargins = qvariant_cast<QMargins>(customMarginsV);

    QWindowsWindowData obtained =
        QWindowsWindowData::create(window, requested,
                                   QWindowsWindow::formatWindowTitle(window->title()));
    /**
	* 代码省略 .......
	*/  
}

在上篇文章中,初始化主事件循环创建了QWindowsIntegration对象,因此在此处可通过QGuiApplicationPrivate::platformIntegration()平台对象
Step 4

// src\plugins\platforms\windows\qwindowswindow.cpp
QWindowsWindowData
    QWindowsWindowData::create(const QWindow *w,
                                       const QWindowsWindowData &parameters,
                                       const QString &title)
{
    WindowCreationData creationData;
    creationData.fromWindow(w, parameters.flags);
    QWindowsWindowData result = creationData.create(w, parameters, title);
    // Force WM_NCCALCSIZE (with wParam=1) via SWP_FRAMECHANGED for custom margin.
    creationData.initialize(w, result.hwnd, !parameters.customMargins.isNull(), 1);
    return result;
}
QWindowsWindowData
    WindowCreationData::create(const QWindow *w, const WindowData &data, QString title) const
{
    WindowData result;
    result.flags = flags;

    const auto appinst = reinterpret_cast<HINSTANCE>(GetModuleHandle(nullptr));

    const QString windowClassName = QWindowsContext::instance()->registerWindowClass(w); // QWindowsContext对象在创建主事件循环时被创建

    /**
	* 代码省略 .......
	* 省略了计算创建窗口所需参数的过程
	*/  
	
	// 调用系统API创建一个新的窗口
    result.hwnd = CreateWindowEx(exStyle, classNameUtf16, titleUtf16,
                                 style,
                                 pos.x(), pos.y(),
                                 context->frameWidth, context->frameHeight,
                                 parentHandle, nullptr, appinst, nullptr);
}
// src\plugins\platforms\windows\qwindowscontext.cpp
QString QWindowsContext::registerWindowClass(const QWindow *w)
{  
	/**
	* 代码省略 .......
	*/  
    return registerWindowClass(cname, qWindowsWndProc, style, GetSysColorBrush(COLOR_WINDOW), icon);
}
QString QWindowsContext::registerWindowClass(QString cname,
                                             WNDPROC proc,
                                             unsigned style,
                                             HBRUSH brush,
                                             bool icon)
{
    // since multiple Qt versions can be used in one process
    // each one has to have window class names with a unique name
    // The first instance gets the unmodified name; if the class
    // has already been registered by another instance of Qt then
    // add a UUID. The check needs to be performed for each name
    // in case new message windows are added (QTBUG-81347).
    // Note: GetClassInfo() returns != 0 when a class exists.
    const auto appInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr)); // 获取当前进程句柄
    WNDCLASS wcinfo;
    const bool classExists = GetClassInfo(appInstance, reinterpret_cast<LPCWSTR>(cname.utf16()), &wcinfo) != FALSE
        && wcinfo.lpfnWndProc != proc;

    if (classExists)
        cname += QUuid::createUuid().toString();

    if (d->m_registeredWindowClassNames.contains(cname))        // already registered in our list
        return cname;

    WNDCLASSEX wc;
    wc.cbSize       = sizeof(WNDCLASSEX);
    wc.style        = style;
    wc.lpfnWndProc  = proc; // 窗口过程处理函数
    wc.cbClsExtra   = 0;
    wc.cbWndExtra   = 0;
    wc.hInstance    = appInstance;
    wc.hCursor      = nullptr;
    wc.hbrBackground = brush;
    if (icon) {
        wc.hIcon = static_cast<HICON>(LoadImage(appInstance, L"IDI_ICON1", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE));
        if (wc.hIcon) {
            int sw = GetSystemMetrics(SM_CXSMICON);
            int sh = GetSystemMetrics(SM_CYSMICON);
            wc.hIconSm = static_cast<HICON>(LoadImage(appInstance, L"IDI_ICON1", IMAGE_ICON, sw, sh, 0));
        } else {
            wc.hIcon = static_cast<HICON>(LoadImage(nullptr, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED));
            wc.hIconSm = nullptr;
        }
    } else {
        wc.hIcon    = nullptr;
        wc.hIconSm  = nullptr;
    }

    wc.lpszMenuName  = nullptr;
    wc.lpszClassName = reinterpret_cast<LPCWSTR>(cname.utf16());
    ATOM atom = RegisterClassEx(&wc); // 注册窗体
    if (!atom)
        qErrnoWarning("QApplication::regClass: Registering window class '%s' failed.",
                      qPrintable(cname));

    d->m_registeredWindowClassNames.insert(cname);
    qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << ' ' << cname
        << " style=0x" << Qt::hex << style << Qt::dec
        << " brush=" << brush << " icon=" << icon << " atom=" << atom;
    return cname;
}

QWindowsContext对象在创建主事件循环时被创建,通过调用QWindowsContext::registerWindowClass为新的窗口注册qWindowsWndProc窗口过程处理函数,最后调用CreateWindowEx创建一个新的窗口
Step 5

// src\plugins\platforms\windows\qwindowscontext.cpp
extern "C" LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    LRESULT result;
    const QtWindows::WindowsEventType et = windowsEventType(message, wParam, lParam);
    QWindowsWindow *platformWindow = nullptr;
    const RECT ncCalcSizeFrame = rectFromNcCalcSize(message, wParam, lParam, 0);
    const bool handled = QWindowsContext::instance()->windowsProc(hwnd, message, et, wParam, lParam, &result, &platformWindow);
    if (QWindowsContext::verbose > 1 && lcQpaEvents().isDebugEnabled()) {
        if (const char *eventName = QWindowsGuiEventDispatcher::windowsMessageName(message)) {
            qCDebug(lcQpaEvents).nospace() << "EVENT: hwd=" << hwnd << ' ' << eventName
                << " msg=0x" << Qt::hex << message << " et=0x" << et << Qt::dec << " wp="
                << int(wParam) << " at " << GET_X_LPARAM(lParam) << ','
                << GET_Y_LPARAM(lParam) << " handled=" << handled;
        }
    }
    if (!handled)
        result = DefWindowProc(hwnd, message, wParam, lParam);
	/**
	* 代码省略 .......
	*/  
}

至此用于处理用户输入事件的事件循环就启动完成了,通过windowsEventType和QWindowsContext::windowsProc将系统消息转为Qt事件并进行处理。

二、消息转换

以鼠标滚轮为例,看看Qt如何将windows的系统消息转换为QEvent,然后再对事件传递,层层去做处理的。
鼠标事件加入队列堆栈:
在这里插入图片描述

Step 1

// src\plugins\platforms\windows\qwindowscontext.cpp
inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamIn, LPARAM lParamIn)
{
    switch (message) {
    case WM_PAINT:
    case WM_ERASEBKGND:
        return QtWindows::ExposeEvent;
    case WM_CLOSE:
        return QtWindows::CloseEvent;
    case WM_DESTROY:
        return QtWindows::DestroyEvent;
	/**
	* 代码省略 .......
	*/  
}
// src\plugins\platforms\windows\qwindowscontext.cpp
bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
                                  QtWindows::WindowsEventType et,
                                  WPARAM wParam, LPARAM lParam,
                                  LRESULT *result,
                                  QWindowsWindow **platformWindowPtr)
{
	/**
	* 代码省略 .......
	*/  
    case QtWindows::MouseWheelEvent:
    case QtWindows::MouseEvent:
    case QtWindows::LeaveEvent:
        {
            QWindow *window = platformWindow->window();
            while (window && (window->flags() & Qt::WindowTransparentForInput))
                window = window->parent();
            if (!window)
                return false;
            if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer)
                return sessionManagerInteractionBlocked() || d->m_pointerHandler.translateMouseEvent(window, hwnd, et, msg, result);
            else
                return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateMouseEvent(window, hwnd, et, msg, result);
        }
        break;
	/**
	* 代码省略 .......
	*/          
}
// src\plugins\platforms\windows\qwindowspointerhandler.cpp
bool QWindowsPointerHandler::translateMouseEvent(QWindow *window,
                                                 HWND hwnd,
                                                 QtWindows::WindowsEventType et,
                                                 MSG msg,
                                                 LRESULT *result)
{
	/**
	* 代码省略 .......
	*/   
    if (et == QtWindows::MouseWheelEvent)
        return translateMouseWheelEvent(window, currentWindowUnderPointer, msg, globalPos, keyModifiers);
	/**
	* 代码省略 .......
	*/   
}   
// src\plugins\platforms\windows\qwindowspointerhandler.cpp
bool QWindowsPointerHandler::translateMouseWheelEvent(QWindow *window,
                                                      QWindow *currentWindowUnderPointer,
                                                      MSG msg,
                                                      QPoint globalPos,
                                                      Qt::KeyboardModifiers keyModifiers)
{
    QWindow *receiver = currentWindowUnderPointer;
    if (!isValidWheelReceiver(receiver))
        receiver = window;
    if (!isValidWheelReceiver(receiver))
        return true;

    int delta = GET_WHEEL_DELTA_WPARAM(msg.wParam);

    // Qt horizontal wheel rotation orientation is opposite to the one in WM_MOUSEHWHEEL
    if (msg.message == WM_MOUSEHWHEEL)
        delta = -delta;

    const QPoint angleDelta = (msg.message == WM_MOUSEHWHEEL || (keyModifiers & Qt::AltModifier)) ?
                QPoint(delta, 0) : QPoint(0, delta);

    QPoint localPos = QWindowsGeometryHint::mapFromGlobal(receiver, globalPos);

    QWindowSystemInterface::handleWheelEvent(receiver, localPos, globalPos, QPoint(), angleDelta, keyModifiers);
    return true;
}     

WindowsEventType 将Windows消息转为Qt定义的事件,一系列计算按键信息后,最后通过QWindowSystemInterface::handleWheelEvent转换为WheelEvent
Step 2

// src\gui\kernel\qwindowsysteminterface.cpp
bool QWindowSystemInterface::handleWheelEvent(QWindow *window, const QPointF &local, const QPointF &global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase, Qt::MouseEventSource source)
{
    unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed();
    return handleWheelEvent(window, time, local, global, pixelDelta, angleDelta, mods, phase, source);
}

bool QWindowSystemInterface::handleWheelEvent(QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase,
                                              Qt::MouseEventSource source, bool invertedScrolling)
{
    // Qt 4 sends two separate wheel events for horizontal and vertical
    // deltas. For Qt 5 we want to send the deltas in one event, but at the
    // same time preserve source and behavior compatibility with Qt 4.
    //
    // In addition high-resolution pixel-based deltas are also supported.
    // Platforms that does not support these may pass a null point here.
    // Angle deltas must always be sent in addition to pixel deltas.
    QWindowSystemInterfacePrivate::WheelEvent *e;

    // Pass Qt::ScrollBegin and Qt::ScrollEnd through
    // even if the wheel delta is null.
    if (angleDelta.isNull() && phase == Qt::ScrollUpdate)
        return false;

    // Simple case: vertical deltas only:
    if (angleDelta.y() != 0 && angleDelta.x() == 0) {
        e = new QWindowSystemInterfacePrivate::WheelEvent(window, timestamp, QHighDpi::fromNativeLocalPosition(local, window), QHighDpi::fromNativePixels(global, window), pixelDelta, angleDelta, angleDelta.y(), Qt::Vertical,
                                                          mods, phase, source, invertedScrolling);

        return QWindowSystemInterfacePrivate::handleWindowSystemEvent(e);
    }
	/**
	* 代码省略 .......
	*/       
}
// src\gui\kernel\qwindowsysteminterface.cpp
template<>
bool QWindowSystemInterfacePrivate::handleWindowSystemEvent<QWindowSystemInterface::DefaultDelivery>(QWindowSystemInterfacePrivate::WindowSystemEvent *ev)
{
    if (synchronousWindowSystemEvents)
        return handleWindowSystemEvent<QWindowSystemInterface::SynchronousDelivery>(ev);
    else
        return handleWindowSystemEvent<QWindowSystemInterface::AsynchronousDelivery>(ev);
}
template<>
bool QWindowSystemInterfacePrivate::handleWindowSystemEvent<QWindowSystemInterface::AsynchronousDelivery>(WindowSystemEvent *ev)
{
    windowSystemEventQueue.append(ev);
    if (QAbstractEventDispatcher *dispatcher = QGuiApplicationPrivate::qt_qpa_core_dispatcher())
        dispatcher->wakeUp();
    return true;
}

将鼠标滚轮事件添加到队列中,并且通过事件分发者通知有新的事件加入,主事件循环会获取到一个WM_QT_SENDPOSTEDEVENTS消息,然后去处理windowSystemEventQueue队列中的事件。
Step 3
QWidget处理滚轮事件的堆栈:在这里插入图片描述

// src\platformsupport\eventdispatchers\qwindowsguieventdispatcher.cpp
void QWindowsGuiEventDispatcher::sendPostedEvents()
{
    QEventDispatcherWin32::sendPostedEvents();
    QWindowSystemInterface::sendWindowSystemEvents(m_flags);
}
// src\gui\kernel\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;

        if (QWindowSystemInterfacePrivate::eventHandler) {
            if (QWindowSystemInterfacePrivate::eventHandler->sendEvent(event))
                nevents++;
        } else {
            nevents++;
            QGuiApplicationPrivate::processWindowSystemEvent(event);
        }

        // Record the accepted state for the processed event
        // (excluding flush events). This state can then be
        // returned by flushWindowSystemEvents().
        if (event->type != QWindowSystemInterfacePrivate::FlushEvents)
            QWindowSystemInterfacePrivate::eventAccepted.storeRelaxed(event->eventAccepted);

        delete event;
    }

    return (nevents > 0);
}

主事件循环收到WM_QT_SENDPOSTEDEVENTS后,最终会调到sendPostedEvents。接着从windowSystemEventsQueued队列中取出一个事件交给QGuiApplicationPrivate::processWindowSystemEvent处理
Step 4

// src\gui\kernel\qguiapplication.cpp
void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e)
{
    Q_TRACE_SCOPE(QGuiApplicationPrivate_processWindowSystemEvent, e->type);

    switch(e->type) {
    case QWindowSystemInterfacePrivate::Mouse:
        QGuiApplicationPrivate::processMouseEvent(static_cast<QWindowSystemInterfacePrivate::MouseEvent *>(e));
        break;
    case QWindowSystemInterfacePrivate::Wheel:
        QGuiApplicationPrivate::processWheelEvent(static_cast<QWindowSystemInterfacePrivate::WheelEvent *>(e));
        break;
}
void QGuiApplicationPrivate::processWheelEvent(QWindowSystemInterfacePrivate::WheelEvent *e)
{
#if QT_CONFIG(wheelevent)
    QWindow *window = e->window.data();
    QPointF globalPoint = e->globalPos;
    QPointF localPoint = e->localPos;

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

    if (!window)
        return;

    QGuiApplicationPrivate::lastCursorPosition = globalPoint;
    modifier_buttons = e->modifiers;

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

#if QT_DEPRECATED_SINCE(5, 14)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
     QWheelEvent ev(localPoint, globalPoint, e->pixelDelta, e->angleDelta, e->qt4Delta, e->qt4Orientation,
                    mouse_buttons, e->modifiers, e->phase, e->source, e->inverted);
QT_WARNING_POP
#else
    QWheelEvent ev(localPoint, globalPoint, e->pixelDelta, e->angleDelta,
                   mouse_buttons, e->modifiers, e->phase, e->inverted, e->source);// 将WheelEvent转换为QWheelEvent 
#endif
     ev.setTimestamp(e->timestamp);
     QGuiApplication::sendSpontaneousEvent(window, &ev);
#else
     Q_UNUSED(e);
#endif // QT_CONFIG(wheelevent)
}

将WheelEvent转换为QWheelEvent
Step 5

bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event)
{
    Q_TRACE(QCoreApplication_sendSpontaneousEvent, receiver, event, event->type());

    if (event)
        event->spont = true;
    return notifyInternal2(receiver, event);
}
bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event)
{
    bool selfRequired = QCoreApplicationPrivate::threadRequiresCoreApplication();
    if (!self && selfRequired)
        return false;

    // 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;
    QScopedScopeLevelCounter scopeLevelCounter(threadData);
    if (!selfRequired)
        return doNotify(receiver, event);
    return self->notify(receiver, event);
}
// src\widgets\kernel\qapplication.cpp
bool QApplication::notify(QObject *receiver, QEvent *e)
{
	/**
	* 代码省略 .......
	*/  
	if (!receiver->isWidgetType()) {
        res = d->notify_helper(receiver, e);
    } 
	/**
	* 代码省略 .......
	*/  
}
bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)
{
	/**
	* 代码省略 .......
	*/  
    // send to all receiver event filters
    if (sendThroughObjectEventFilters(receiver, e)) {
        filtered = true;
        return filtered;
    }

    // deliver the event
    consumed = receiver->event(e);

    QCoreApplicationPrivate::setEventSpontaneous(e, false);
    return consumed;
}

QWheelEvent首先会被QApplication::notify处理,然后交给QWidgetWindow::event处理
Step 6

bool QWidgetWindow::event(QEvent *event)
{    
	/**
	* 代码省略 .......
	*/  
		case QEvent::Wheel:
        handleWheelEvent(static_cast<QWheelEvent *>(event));
        return true;
	/**
	* 代码省略 .......
	*/  
} 
void QWidgetWindow::handleWheelEvent(QWheelEvent *event)
{
    if (QApplicationPrivate::instance()->modalState() && !qt_try_modal(m_widget, event->type()))
        return;

    QWidget *rootWidget = m_widget;
    QPoint pos = event->position().toPoint();

    // Use proper popup window for wheel event. Some QPA sends the wheel
    // event to the root menu, so redirect it to the proper popup window.
    QWidget *activePopupWidget = QApplication::activePopupWidget();
    if (activePopupWidget && activePopupWidget != m_widget) {
        rootWidget = activePopupWidget;
        pos = rootWidget->mapFromGlobal(event->globalPosition().toPoint());
    }

    // which child should have it?
    QWidget *widget = rootWidget->childAt(pos);

    if (!widget)
        widget = rootWidget;

    QPoint mapped = widget->mapFrom(rootWidget, pos);

#if QT_DEPRECATED_SINCE(5, 0)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
    QWheelEvent translated(mapped, event->globalPos(), event->pixelDelta(), event->angleDelta(), event->delta(), event->orientation(), event->buttons(), event->modifiers(), event->phase(), event->source(), event->inverted());
QT_WARNING_POP
#else
    QWheelEvent translated(QPointF(mapped), event->globalPosition(), event->pixelDelta(), event->angleDelta(),
                           event->buttons(), event->modifiers(), event->phase(), event->inverted(), event->source());
#endif
    translated.setTimestamp(event->timestamp());
    QGuiApplication::forwardEvent(widget, &translated, event);
}   
bool QCoreApplication::forwardEvent(QObject *receiver, QEvent *event, QEvent *originatingEvent)
{
    if (event && originatingEvent)
        event->spont = originatingEvent->spont;

    return notifyInternal2(receiver, event);
}    

在QWidgetWindow中找到即将要处理QWheelEvent的对象,然后通过QApplication::notify处理
Step 7

bool QWidget::event(QEvent *event)
{
	/**
	* 代码省略 .......
	*/  
    case QEvent::Wheel:
        wheelEvent((QWheelEvent*)event);
        break;
	/**
	* 代码省略 .......
	*/  
}
void QWidget::wheelEvent(QWheelEvent *event)
{
    event->ignore();
}

QApplication::notify处理后,最后到QWidget::event处理,在event中根据事件类型处理对应事件逻辑。

三、事件传递

在这里插入图片描述
在Qt事件首先被原生事件过滤器处理,然后给QApplication::notify,再被目标对象的事件过滤处理,再到event中处理,在event中根据事件的具体类型,在调用具体事件处理函数。如:鼠标按下事件(mousePressEvent),鼠标释放事件(mouseReleaseEvent)

四、总结

至此,Qt的事件循环处理到此就结束了。
针对上篇文章遗留的两个问题:
1.对于Windows,Qt是如何捕获事件(如:鼠标事件,键盘按键事件)?如何对捕获到的事件进行处理的?
答:主线事件循环通过PeekMessage捕获用户输入事件,在顶层窗口(没有父对象的窗体)创建新的windows窗口并注册qWindowsWndProc处理函数(具体在本篇文章第一节“启动用户输入事件循环做出回答”)
2.如何将Windows消息转换为QEvent?
答:本篇文章第二节做出具体回答

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值