本文共 12025 字,大约阅读时间需要 40 分钟。
理解Qt消息机制刻不容缓,那我们从对比传统的windows消息处理机制对比来说起;
只有知道QT底层的消息处理、对我们理解并学习Qt有很大帮助; 下面我将对windows程序与Qt对比,并在核心代码处并给出注释进行对比、方便学习。 注意重点看代码中的注视进行对比:! 注意重点看代码中的注视进行对比:! 注意重点看代码中的注视进行对比:! 一:windows程序的消息处理 windows程序的处理大概一致 如下: 1.0 windows 消息处理机制: int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("Hello"); HWND hwnd; MSG msg; WNDCLASS wndclass; //fill wndclass wndclass.lpfnWndProc = WndProc; ... RegisterClass(&wndclass); hwnd = CreateWindow( .... ); // creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); while(GetMessage(&msg, NULL, 0, 0)) { //这一块位置得到消息 TranslateMessage(&msg);//转换消息 DispatchMessage(&msg);//分发消息到系统处理 } return msg.wParam; } 1.1 这是分发消息的回调函数熟悉windows程序应该不难看懂 LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; RECT rect; switch(message) { case WM_CREATE: return 0; case WM_PAINT://重绘、比如窗口大小拉伸 ... return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); // windows 操作系统内部的消息机制、系统调用函数; } 二 Qt消息处理机制 2.0 Qt的消息机制 QEventDispatcherWin32: 注册窗口类别,并创建一个隐藏窗口 (QEventDispatcherWin32_Internal_WidgetXXXX) 窗口的回调函数 qt_internal_proc() 安装WH_GETMESSAGE类型的钩子函数 qt_GetMessageHook() bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) 与上面的消息循环:的while一样 、得到过滤所有消息 { TranslateMessage(&msg);//转换消息 DispatchMessage(&msg); //分发消息 } DispatchMessage(&msg); //分发消息 分发消息的或回调函数、这个与windows程序的CALLBACK WndProc一样 LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp) 2.1 下面就是从系统获得的消息后Qt封装消息后所作的事情 这个就是Qt的消息回调: LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp) { if (message == WM_NCCREATE) return true; // MSG windows 消息结构: 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(); // 下面 WM_QT_SOCKETNOTIFIER socket Qt 事件底层的处理机制、处理网络的消息事件 if (message == WM_QT_SOCKETNOTIFIER) { // socket notifier message int type = -1; switch (WSAGETSELECTEVENT(lp)) { //在非阻塞模式下利用socket事件的消息机制,Server端与Client端之间的通信处于异步状态下 case FD_READ: //socket 文件描述符 read 、有数据到达时发生 case FD_ACCEPT: //socket 文件描述符 接收连接 、 作为客户端连接成功时发生 type = 0; break; case FD_WRITE: //socket 文件描述符写 、有数据发送时产生 case FD_CONNECT: //socket 文件描述符发起连接 、 作为服务端等待连接成功时发生 type = 1; break; case FD_OOB: //socket 文件描述符收到数据 、 收到外带数据时发生 type = 2; break; case FD_CLOSE: //socket 文件描述符关闭断开连接 、套接口关闭时发生 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 == nullptr) { d->postActivateSocketNotifiers(); } else { Q_ASSERT(d->active_fd.contains(sn->fd)); QSockFd &sd = d->active_fd[sn->fd]; if (sd.selected) { Q_ASSERT(sd.mask == 0); d->doWsaAsyncSelect(sn->fd, 0); sd.selected = false; } d->postActivateSocketNotifiers(); // Ignore the message if a notification with the same type was // received previously. Suppressed message is definitely spurious. const long eventCode = WSAGETSELECTEVENT(lp); if ((sd.mask & eventCode) != eventCode) { sd.mask |= eventCode; QEvent event(type < 3 ? QEvent::SockAct : QEvent::SockClose); QCoreApplication::sendEvent(sn->obj, &event); } } } return 0; } else if (message == WM_QT_ACTIVATENOTIFIERS) { // 处理postEvent事件 Q_ASSERT(d != 0); // postEvent() 事件,因为是队列的存储的方式,邮递发送消息、直接返回 // Postpone activation if we have unhandled socket notifier messages // in the queue. WM_QT_ACTIVATENOTIFIERS will be posted again as a result of // event processing. MSG msg;//异步调用 if (!PeekMessage(&msg, d->internalHwnd, WM_QT_SOCKETNOTIFIER, WM_QT_SOCKETNOTIFIER, PM_NOREMOVE) && d->queuedSocketEvents.isEmpty()) { // d->queuedSocketEvents 消息队列如果消息有很多的时候都会加入这个队列、这个就是关于Qt::connect();参数:地址 // register all socket notifiers for (QSFDict::iterator it = d->active_fd.begin(), end = d->active_fd.end(); it != end; ++it) { QSockFd &sd = it.value(); if (!sd.selected) { d->doWsaAsyncSelect(it.key(), sd.event); // allow any event to be accepted sd.mask = 0; sd.selected = true; } } } d->activateNotifiersPosted = false; return 0; } else if (message == WM_QT_SENDPOSTEDEVENTS // 处理 Qt sendPostEvent()发送事件 // we also use a Windows timer to send posted events when the message queue is full // WM_QT_SENDPOSTEDEVENTS : 这个消息是我们Qt程序大部分走的事件,特殊情况除外。 || (message == WM_TIMER && d->sendPostedEventsWindowsTimerId != 0 && wp == (uint)d->sendPostedEventsWindowsTimerId)) { const int localSerialNumber = d->serialNumber.load(); if (localSerialNumber != d->lastSerialNumber) { d->lastSerialNumber = localSerialNumber; q->sendPostedEvents();//因为sendevent是同步所以直接进入进行调用 } return 0; } else if (message == WM_TIMER) {//系统定时器超时 Q_ASSERT(d != 0); d->sendTimerEvent(wp); return 0; } return DefWindowProc(hwnd, message, wp, lp); // 这个与windows程序一样的地方。 } 另外还有一点很重要的:大家都知道Qt消息处理比windows处理的块,Qt程序通过一通处理才调用DefWindowProc传给系统所以Qt系统的消息机制是比windows的消息慢的原因之一; 不过他也有它优点:qt的信号与槽函数让我们让我们编程更方便。 2.3 消息全局通知事件 另外我们都知道我们开发的系统、所有的消息都会到这个类QApplication::notify事件过滤所有的事件: 其实QApplication继承QGuiApplication类; bool QGuiApplication::notify(QObject *object, QEvent *event) { if (object->isWindowType()) QGuiApplicationPrivate::sendQWindowEventToQPlatformWindow(static_cast<QWindow *>(object), event); return QCoreApplication::notify(object, event); } 2.4 消息的组装 下面是所有的消息类型处理:在我们开发的系统中别人使用processEvent发送消息是比较高效的。 原因:这个消息直达经过的处理的少。 void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e) { switch(e->type) { case QWindowSystemInterfacePrivate::FrameStrutMouse: case QWindowSystemInterfacePrivate::Mouse: QGuiApplicationPrivate::processMouseEvent(static_cast<QWindowSystemInterfacePrivate::MouseEvent *>(e)); break; case QWindowSystemInterfacePrivate::Wheel: QGuiApplicationPrivate::processWheelEvent(static_cast<QWindowSystemInterfacePrivate::WheelEvent *>(e)); break; case QWindowSystemInterfacePrivate::Key: QGuiApplicationPrivate::processKeyEvent(static_cast<QWindowSystemInterfacePrivate::KeyEvent *>(e)); break; case QWindowSystemInterfacePrivate::Touch: QGuiApplicationPrivate::processTouchEvent(static_cast<QWindowSystemInterfacePrivate::TouchEvent *>(e)); break; case QWindowSystemInterfacePrivate::GeometryChange: QGuiApplicationPrivate::processGeometryChangeEvent(static_cast<QWindowSystemInterfacePrivate::GeometryChangeEvent*>(e)); break; case QWindowSystemInterfacePrivate::Enter: QGuiApplicationPrivate::processEnterEvent(static_cast<QWindowSystemInterfacePrivate::EnterEvent *>(e)); break; case QWindowSystemInterfacePrivate::Leave: QGuiApplicationPrivate::processLeaveEvent(static_cast<QWindowSystemInterfacePrivate::LeaveEvent *>(e)); break; case QWindowSystemInterfacePrivate::ActivatedWindow: QGuiApplicationPrivate::processActivatedEvent(static_cast<QWindowSystemInterfacePrivate::ActivatedWindowEvent *>(e)); break; case QWindowSystemInterfacePrivate::WindowStateChanged: QGuiApplicationPrivate::processWindowStateChangedEvent(static_cast<QWindowSystemInterfacePrivate::WindowStateChangedEvent *>(e)); break; case QWindowSystemInterfacePrivate::WindowScreenChanged: QGuiApplicationPrivate::processWindowScreenChangedEvent(static_cast<QWindowSystemInterfacePrivate::WindowScreenChangedEvent *>(e)); break; case QWindowSystemInterfacePrivate::ApplicationStateChanged: { QWindowSystemInterfacePrivate::ApplicationStateChangedEvent * changeEvent = static_cast<QWindowSystemInterfacePrivate::ApplicationStateChangedEvent *>(e); QGuiApplicationPrivate::setApplicationState(changeEvent->newState, changeEvent->forcePropagate); } break; case QWindowSystemInterfacePrivate::FlushEvents: { QWindowSystemInterfacePrivate::FlushEventsEvent *flushEventsEvent = static_cast<QWindowSystemInterfacePrivate::FlushEventsEvent *>(e); QWindowSystemInterface::deferredFlushWindowSystemEvents(flushEventsEvent->flags); } break; case QWindowSystemInterfacePrivate::Close: QGuiApplicationPrivate::processCloseEvent( static_cast<QWindowSystemInterfacePrivate::CloseEvent *>(e)); break; case QWindowSystemInterfacePrivate::ScreenOrientation: QGuiApplicationPrivate::reportScreenOrientationChange( static_cast<QWindowSystemInterfacePrivate::ScreenOrientationEvent *>(e)); break; case QWindowSystemInterfacePrivate::ScreenGeometry: QGuiApplicationPrivate::reportGeometryChange( static_cast<QWindowSystemInterfacePrivate::ScreenGeometryEvent *>(e)); break; case QWindowSystemInterfacePrivate::ScreenLogicalDotsPerInch: QGuiApplicationPrivate::reportLogicalDotsPerInchChange( static_cast<QWindowSystemInterfacePrivate::ScreenLogicalDotsPerInchEvent *>(e)); break; case QWindowSystemInterfacePrivate::ScreenRefreshRate: QGuiApplicationPrivate::reportRefreshRateChange( static_cast<QWindowSystemInterfacePrivate::ScreenRefreshRateEvent *>(e)); break; case QWindowSystemInterfacePrivate::ThemeChange: QGuiApplicationPrivate::processThemeChanged( static_cast<QWindowSystemInterfacePrivate::ThemeChangeEvent *>(e)); break; case QWindowSystemInterfacePrivate::Expose: QGuiApplicationPrivate::processExposeEvent(static_cast<QWindowSystemInterfacePrivate::ExposeEvent *>(e)); break; case QWindowSystemInterfacePrivate::Tablet: QGuiApplicationPrivate::processTabletEvent( static_cast<QWindowSystemInterfacePrivate::TabletEvent *>(e)); break; case QWindowSystemInterfacePrivate::TabletEnterProximity: QGuiApplicationPrivate::processTabletEnterProximityEvent( static_cast<QWindowSystemInterfacePrivate::TabletEnterProximityEvent *>(e)); break; case QWindowSystemInterfacePrivate::TabletLeaveProximity: QGuiApplicationPrivate::processTabletLeaveProximityEvent( static_cast<QWindowSystemInterfacePrivate::TabletLeaveProximityEvent *>(e)); break; #ifndef QT_NO_GESTURES case QWindowSystemInterfacePrivate::Gesture: QGuiApplicationPrivate::processGestureEvent( static_cast<QWindowSystemInterfacePrivate::GestureEvent *>(e)); break; #endif case QWindowSystemInterfacePrivate::PlatformPanel: QGuiApplicationPrivate::processPlatformPanelEvent( static_cast<QWindowSystemInterfacePrivate::PlatformPanelEvent *>(e)); break; case QWindowSystemInterfacePrivate::FileOpen: QGuiApplicationPrivate::processFileOpenEvent( static_cast<QWindowSystemInterfacePrivate::FileOpenEvent *>(e)); break; #ifndef QT_NO_CONTEXTMENU case QWindowSystemInterfacePrivate::ContextMenu: QGuiApplicationPrivate::processContextMenuEvent( static_cast<QWindowSystemInterfacePrivate::ContextMenuEvent *>(e)); break; #endif case QWindowSystemInterfacePrivate::EnterWhatsThisMode: QGuiApplication::postEvent(QGuiApplication::instance(), new QEvent(QEvent::EnterWhatsThisMode)); break; default: qWarning() << "Unknown user input event type:" << e->type; break; } } 2.5 常用的消息过滤事件 通过2.4组装的消息最终到达、此处。经过这些之后:之前的windows消息现在都已映射为QT类型的消息、进入Qt消息处理机制中来、才有了我们的消息过滤等等消息事件键盘鼠标等等; virtual void mouseDoubleClickEvent(QMouseEvent *event) virtual void mouseMoveEvent(QMouseEvent *event) virtual void mousePressEvent(QMouseEvent *event) virtual void mouseReleaseEvent(QMouseEvent *event) virtual void moveEvent(QMoveEvent *event) virtual bool nativeEvent(const QByteArray &eventType, void *message, long *result) virtual void paintEvent(QPaintEvent *event) virtual void resizeEvent(QResizeEvent *event) virtual void showEvent(QShowEvent *event) virtual void tabletEvent(QTabletEvent *event) virtual void wheelEvent(QWheelEvent *event) 重新实现上面的消息事件做我们的功能; 总结: 通过windows消息的运行机制与Qt的消息机制进行了对比。其实Qt在windows平台上的消息传递依赖的是windows消息机制。只不过在开始时进行注册消息拦截等一些封装处理、加工成Qt消息在Qt程序中进行传递。转载地址:http://gdlws.baihongyu.com/