【转自】:http://mobile.51cto.com/symbian-272812.htm

在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent. 接下来依次谈谈Qt中有谁来产生、分发、接受和处理事件。

本篇来介绍Qt 事件处理机制 。深入了解事件处理系统对于每个学习Qt人来说非常重要,可以说,Qt是以事件驱动的UI工具集。 大家熟知Signals/Slots在多线程的实现也依赖于Qt事件处理机制。

Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent.  接下来依次谈谈Qt中有谁来产生、分发、接受和处理事件

1、谁来产生事件: 最容易想到的是我们的输入设备,比如键盘、鼠标产生的

keyPressEvent,keyReleaseEvent,mousePressEvent,mouseReleaseEvent事件(他们被封装成QMouseEvent和QKeyEvent),这些事件来自于底层的操作系统,它们以异步的形式通知Qt事件处理系统,后文会仔细道来。当然Qt自己也会产生很多事件,比如QObject::startTimer()会触发QTimerEvent. 用户的程序可还以自己定制事件。

2、谁来接受和处理事件:答案是QObject。在Qt的内省机制剖析一文已经介绍QObject 类是整个Qt对象模型的心脏,事件处理机制是QObject三大职责(内存管理、内省(intropection)与事件处理制)之一。任何一个想要接受并处理事件的对象均须继承自QObject,可以选择重载QObject::event()函数或事件的处理权转给父类。

3、谁来负责分发事件:对于non-GUI的Qt程序,是由QCoreApplication负责将QEvent分发给QObject的子类-Receiver. 对于Qt GUI程序,由QApplication来负责。

接下来,将通过对代码的解析来看看QT是利用event loop从事件队列中获取用户输入事件,又是如何将事件转义成QEvents,并分发给相应的QObject处理。

 #include <QApplication>
#include "widget.h"
//Section 1
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Widget window; // Widget 继承自QWidget
window.show();
return app.exec(); // 进入Qpplication事件循环,见section 2
}
// Section 2:
int QApplication::exec()
{
//skip codes
//简单的交给QCoreApplication来处理事件循环=〉section 3
return QCoreApplication::exec();
}
// Section 3
int QCoreApplication::exec()
{
//得到当前Thread数据
QThreadData *threadData = self->d_func()->threadData;
if (threadData != QThreadData::current()) {
qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());
return -;
}
//检查event loop是否已经创建
if (!threadData->eventLoops.isEmpty()) {
qWarning("QCoreApplication::exec: The event loop is already running");
return -;
}
...
QEventLoop eventLoop;
self->d_func()->in_exec = true;
self->d_func()->aboutToQuitEmitted = false;
//委任QEventLoop 处理事件队列循环 ==> Section 4
int returnCode = eventLoop.exec();
....
}
return returnCode;
}
// Section 4
int QEventLoop::exec(ProcessEventsFlags flags)
{
//这里的实现代码不少,最为重要的是以下几行
Q_D(QEventLoop); // 访问QEventloop私有类实例d
try {
//只要没有遇见exit,循环派发事件
while (!d->exit)
processEvents(flags | WaitForMoreEvents | EventLoopExec);
} catch (...) {}
}
// Section 5
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
Q_D(QEventLoop);
if (!d->threadData->eventDispatcher)
return false;
if (flags & DeferredDeletion)
QCoreApplication::sendPostedEvents(, QEvent::DeferredDelete);
//将事件派发给与平台相关的QAbstractEventDispatcher子类 =>Section 6
return d->threadData->eventDispatcher->processEvents(flags);
} // Section 6,QTDIR\src\corelib\kernel\qeventdispatcher_win.cpp
// 这段代码是完成与windows平台相关的windows c++。 以跨平台著称的Qt同时也提供了对Symiban,Unix等平台的消息派发支持
// 其事现分别封装在QEventDispatcherSymbian和QEventDispatcherUNIX
// QEventDispatcherWin32派生自QAbstractEventDispatcher.
bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
Q_D(QEventDispatcherWin32);
if (!d->internalHwnd)
createInternalHwnd();
d->interrupt = false;
emit awake();
bool canWait;
bool retVal = false;
bool seenWM_QT_SENDPOSTEDEVENTS = false;
bool needWM_QT_SENDPOSTEDEVENTS = false;
do {
DWORD waitRet = ;
HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - ];
QVarLengthArray<MSG> processedTimers;
while (!d->interrupt) {
DWORD nCount = d->winEventNotifierList.count();
Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - );
MSG msg;
bool haveMessage;
if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
// process queued user input events
haveMessage = true;
//从处理用户输入队列中取出一条事件
msg = d->queuedUserInputEvents.takeFirst();
} else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {
// 从处理socket队列中取出一条事件
haveMessage = true;
msg = d->queuedSocketEvents.takeFirst();
} else {
haveMessage = PeekMessage(&msg, , , , 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)) {
// 用户输入事件入队列,待以后处理
haveMessage = false;
d->queuedUserInputEvents.append(msg);
}
if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers)
&& (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {
// socket 事件入队列,待以后处理
haveMessage = false;
d->queuedSocketEvents.append(msg);
}
}
....
if (!filterEvent(&msg)) {
TranslateMessage(&msg);
//将事件打包成message调用Windows API派发出去
//分发一个消息给窗口程序。消息被分发到回调函数,将消息传递给windows系统,windows处理完毕,会调用回调函数 => section 7
DispatchMessage(&msg);
}
}
}
} while (canWait);
...
return retVal;
}
// Section 6,QTDIR\src\corelib\kernel\qeventdispatcher_win.cpp
// 这段代码是完成与windows平台相关的windows c++。 以跨平台著称的Qt同时也提供了对Symiban,Unix等平台的消息派发支持
// 其事现分别封装在QEventDispatcherSymbian和QEventDispatcherUNIX
// QEventDispatcherWin32派生自QAbstractEventDispatcher.
bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
Q_D(QEventDispatcherWin32);
if (!d->internalHwnd)
createInternalHwnd();
d->interrupt = false;
emit awake();
bool canWait;
bool retVal = false;
bool seenWM_QT_SENDPOSTEDEVENTS = false;
bool needWM_QT_SENDPOSTEDEVENTS = false;
do {
DWORD waitRet = ;
HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - ];
QVarLengthArray<MSG> processedTimers;
while (!d->interrupt) {
DWORD nCount = d->winEventNotifierList.count();
Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - );
MSG msg;
bool haveMessage;
if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
// process queued user input events
haveMessage = true;
//从处理用户输入队列中取出一条事件
msg = d->queuedUserInputEvents.takeFirst();
} else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {
// 从处理socket队列中取出一条事件
haveMessage = true;
msg = d->queuedSocketEvents.takeFirst();
} else {
haveMessage = PeekMessage(&msg, , , , 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)) {
// 用户输入事件入队列,待以后处理
haveMessage = false;
d->queuedUserInputEvents.append(msg);
}
if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers)
&& (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {
// socket 事件入队列,待以后处理
haveMessage = false;
d->queuedSocketEvents.append(msg);
}
}
....
if (!filterEvent(&msg)) {
TranslateMessage(&msg);
//将事件打包成message调用Windows API派发出去
//分发一个消息给窗口程序。消息被分发到回调函数,将消息传递给windows系统,windows处理完毕,会调用回调函数 => section 7
DispatchMessage(&msg);
}
}
}
} while (canWait);
...
return retVal;
} // Section 7 windows窗口回调函数 定义在QTDIR\src\gui\kernel\qapplication_win.cpp
extern "C" LRESULT QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
...
//将消息重新封装成QEvent的子类QMouseEvent ==> Section 8
result = widget->translateMouseEvent(msg);
...
} // Section 7 windows窗口回调函数 定义在QTDIR\src\gui\kernel\qapplication_win.cpp
extern "C" LRESULT QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
...
//将消息重新封装成QEvent的子类QMouseEvent ==> Section 8
result = widget->translateMouseEvent(msg);
...
}

从Section 1~Section7, Qt进入QApplication的event loop,经过层层委任,最终QEventloop的processEvent将通过与平台相关的QAbstractEventDispatcher的子类QEventDispatcherWin32获得用户的用户输入事件,并将其打包成message后,通过标准Windows API ,把消息传递给了Windows OS,Windows OS得到通知后回调QtWndProc,  至此事件的分发与处理完成了一半的路程。

【转】解读Qt 事件处理机制(上篇)的更多相关文章

  1. Qt 事件处理机制 (上篇)

    本篇来介绍Qt 事件处理机制 .深入了解事件处理系统对于每个学习Qt人来说非常重要,可以说,Qt是以事件驱动的UI工具集. 大家熟知Signals/Slots在多线程的实现也依赖于Qt的事件处理机制. ...

  2. 【转】Qt 事件处理机制 (下篇)

    转自:http://mobile.51cto.com/symbian-272816.htm 在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent. 接下来依次谈谈Qt中有谁来产生.分 ...

  3. Qt 事件处理机制 (下篇)

    继续我们上一篇文章继续介绍,Qt 事件处理机制 (上篇) 介绍了Qt框架的事件处理机制:事件的产生.分发.接受和处理,并以视窗系统鼠标点击QWidget为例,对代码进行了剖析,向大家分析了Qt框架如何 ...

  4. Qt事件处理机制

    研一的时候开始使用Qt,感觉用Qt开发图形界面比MFC的一套框架来方便的多.后来由于项目的需要,也没有再接触Qt了.现在要重新拾起来,于是要从基础学起. Now,开始学习Qt事件处理机制. 先给出原文 ...

  5. 9、Qt 事件处理机制

    原文地址:http://mobile.51cto.com/symbian-272812.htm 在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent. 接下来依次谈谈Qt中有谁来产生 ...

  6. Qt 事件处理机制

    Qt 事件处理机制 因为这篇文章写得特别好,将Qt的事件处理机制能够阐述的清晰有条理,并且便于学习.于是就装载过来了(本文做了排版,并删减了一些冗余的东西,希望原主勿怪),以供学习之用. 简介 在Qt ...

  7. QT开发(十二)——QT事件处理机制

    一.QT事件简介 QT程序是事件驱动的, 程序的每个动作都是由内部某个事件所触发.QT事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. 常见的QT事件类型如下: 键盘事件: 按键按下和松开 ...

  8. 【QT学习】QT事件处理机制

    GUI应用程序由 事件驱动. 键盘.鼠标.拖放.滚动.绘屏.定时事件. connect

  9. Qt之事件处理机制

    思维导读 一.事件简介 QT程序是事件驱动的, 程序的每个动作都是由内部某个事件所触发.QT事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. 常见的QT事件类型如下: 键盘事件: 按键按下 ...

随机推荐

  1. 转:Java开发牛人十大必备网站

    原文来自于:http://www.importnew.com/7980.html 以下是我收集的Java开发牛人必备的网站.这些网站可以提供信息,以及一些很棒的讲座, 还能解答一般问题.面试问题等.质 ...

  2. Android 判断当前网络连接类型

    实际应用开发时,如果存在需要用户获取大量数据的情况,最好是先判断下网络类型,提示用户当前的网络类型,是否需要连接Wifi,etc.(手机流量太贵啦,当然土豪是无视这玩意的, (/ □ \)). 定义网 ...

  3. Android 4.4 上实现透明导航栏和状态栏 Translucent system bar

    Translucent system UI styling To get the most impact out of your content, you can now use new window ...

  4. nice命令

    Linux nice命令 Linux nice命令以更改过的优先序来执行程序,如果未指定程序,则会印出目前的排程优先序,内定的 adjustment 为 10,范围为 -20(最高优先序)到 19(最 ...

  5. 【转】Android进阶2之Activity之间数据交流(onActivityResult的用法)----不错

    原文网址:http://blog.csdn.net/sjf0115/article/details/7387467 主要功能: 在一个主界面(主Activity)上能连接往许多不同子功能模块(子Act ...

  6. C语言---递归反向输出任意长度的字符串

    (该字符串可以包含空格和回车!) [题目要求] 编写一个递归函数,实现将输入的任意长度的字符串反向输出的功能. 例如输入字符串:ABCD,输出字符串:DCBA. [题目分析] 应用递归的思想有时可以很 ...

  7. 【6】JAVA---地址App小软件(QueryPanel.class)(表现层)

    查找模块: 年龄可进行段查找. 其他的都是模糊匹配. 空格为无用字符,会屏蔽的(除年龄). (如果在年龄中输入空格,会出现异常,当时没想到这点,要防护这点很容易的,但因为在这个小软件的编写过程,我主要 ...

  8. Nodejs in Visual Studio Code 02.学习Nodejs

    1.开始 源码下载:https://github.com/sayar/NodeMVA 在线视频:https://mva.microsoft.com/en-US/training-courses/usi ...

  9. P - 奔小康赚大钱 - hdu 2255(带权值的匹配)

    分析:这是一个KM的模板题,也就不多说了,KM最复杂的情况都能过,下面是没有优化过的代码: ****************************************************** ...

  10. C - Courses - hdu 1083(模板)

    一共有N个学生跟P门课程,一个学生可以任意选一 门或多门课,问是否达成: 1.每个学生选的都是不同的课(即不能有两个学生选同一门课) 2.每门课都有一个代表(即P门课都被成功选过) 输入为: P N( ...