上一篇说到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 ¶meters,
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?
答:本篇文章第二节做出具体回答