剖析Qt的事件机制原理(源代码级别)
在用Qt写Gui程序的时候,在main函数里面最后依据都是app.exec();很多书上对这句的解释是,使Qt程序进入消息循环。下面我们就到exec()函数内部,来看一下他的实现原理。
Let's go!
首先来到QTDIR/src/corelib/kernel/qcoreapplication.cpp
int QCoreApplication::exec()
{
if (!QCoreApplicationPrivate::checkInstance("exec"))
return -1;
//获取线程数据
QThreadData *threadData = self->d_func()->threadData;
//判断是否在主线程创建
if (threadData != QThreadData::current()) {
qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());
return -1;
}
//判断eventLoop是否已经创建
if (!threadData->eventLoops.isEmpty()) {
qWarning("QCoreApplication::exec: The event loop is already running");
return -1;
}
threadData->quitNow = false;
QEventLoop eventLoop;
self->d_func()->in_exec = true;
//创建eventLoop
int returnCode = eventLoop.exec();
threadData->quitNow = false;
if (self) {
self->d_func()->in_exec = false;
//退出程序
emit self->aboutToQuit();
sendPostedEvents(0, QEvent::DeferredDelete);
}
return returnCode;
}
再来到qeventloop.cpp中。
int QEventLoop::exec(ProcessEventsFlags flags)
{
Q_D(QEventLoop);
if (d->threadData->quitNow)
return -1;
//已经调用过exec了。
if (d->inExec) {
qWarning("QEventLoop::exec: instance %p has already called exec()", this);
return -1;
}
d->inExec = true;
d->exit = false;
++d->threadData->loopLevel;
//将事件类对象压入线程结构体中
d->threadData->eventLoops.push(this);
// remove posted quit events when entering a new event loop
// 这句不用翻译了把!
if (qApp->thread() == thread())
QCoreApplication::removePostedEvents(qApp, QEvent::Quit);
#if defined(QT_NO_EXCEPTIONS)
while (!d->exit)
//这里才是关键,我们还要继续跟踪进去。
processEvents(flags | WaitForMoreEvents);
#else
try {
while (!d->exit)
processEvents(flags | WaitForMoreEvents);
} catch (...) {
//如果使用了EXCEPTION,则继续对下一条时间进行处理。
qWarning("Qt has caught an exception thrown from an event handler. Throwing/n"
"exceptions from an event handler is not supported in Qt. You must/n"
"reimplement QApplication::notify() and catch all exceptions there./n");
throw;
}
#endif
//退出eventloop前,将时间对象从线程结构中取出。
QEventLoop *eventLoop = d->threadData->eventLoops.pop();
Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error");
Q_UNUSED(eventLoop); // --release warning
d->inExec = false;
--d->threadData->loopLevel;
//退出事件循环。
return d->returnCode;
}
来到了processEvents函数:
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
Q_D(QEventLoop);
//判断事件分派器是否为空。
if (!d->threadData->eventDispatcher)
return false;
if (flags & DeferredDeletion)
QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
//调用不同平台下的事件分派器来处理事件。
return d->threadData->eventDispatcher->processEvents(flags);
}
processEvents是在QAbstractEventDispatcher类中定义的纯虚方法。在QEventDispatcherWin32类有processEvents的实现。
bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
Q_D(QEventDispatcherWin32);
//内部数据创建。registerClass注册窗口类,createwindow创建窗体。
//注册socket notifiers,启动所有的normal timers
if (!d->internalHwnd)
createInternalHwnd();
d->interrupt = false;
emit awake();
bool canWait;
bool retVal = false;
do {
QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
DWORD waitRet = 0;
HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1];
QVarLengthArray processedTimers;
while (!d->interrupt) {
DWORD nCount = d->winEventNotifierList.count();
Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);
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()) {
// process queued socket events 处理socket事件,放入队列中。
haveMessage = true;
msg = d->queuedSocketEvents.takeFirst();
} else {
//从消息队列中取消息,同PeekMessage
haveMessage = winPeekMessage(&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)) {
// queue user input events for later processing
haveMessage = false;
d->queuedUserInputEvents.append(msg);
}
if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers)
&& (msg.message == WM_USER && 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();
//注册signal--slot。
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) {
//处理timer事件
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 (!filterEvent(&msg)) {
TranslateMessage(&msg);
QT_WA({
DispatchMessage(&msg);
} , {
DispatchMessageA(&msg);
});
}
} else if (waitRet >= WAIT_OBJECT_0 && waitRet < WAIT_OBJECT_0 + nCount) {
//处理signal--slot
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
QThreadData *data = d->threadData;
canWait = (!retVal
&& data->canWait
&& !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);
emit awake();
if (waitRet >= WAIT_OBJECT_0 && waitRet < WAIT_OBJECT_0 + nCount) {
d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));
retVal = true;
}
}
} while (canWait);
return retVal;
}
http://blog.sina.com.cn/s/blog_a401a1ea0101e751.html
剖析Qt的事件机制原理(源代码级别)的更多相关文章
- 剖析Qt的事件机制原理
版权声明 请尊重原创作品.转载请保持文章完整性,并以超链接形式注明原始作者“tingsking18”和主站点地址,方便其他朋友提问和指正. QT源码解析(一) QT创建窗口程序.消息循环和WinMai ...
- 详解 QT 源码之 Qt 事件机制原理
QT 源码之 Qt 事件机制原理是本文要介绍的内容,在用Qt写Gui程序的时候,在main函数里面最后依据都是app.exec();很多书上对这句的解释是,使 Qt 程序进入消息循环.下面我们就到ex ...
- 深入理解React:事件机制原理
目录 序言 DOM事件流 事件捕获阶段.处于目标阶段.事件冒泡阶段 addEventListener 方法 React 事件概述 事件注册 document 上注册 回调函数存储 事件分发 小结 参考 ...
- 【React】354- 一文吃透 React 事件机制原理
大纲 主要分为4大块儿,主要是结合源码对 react事件机制的原理 进行分析,希望可以让你对 react事件机制有更清晰的认识和理解. 当然肯定会存在一些表述不清或者理解不够标准的地方,还请各位大神. ...
- QWidget 键盘事件 焦点(源代码级别研究)
在Qt中,键盘事件和QWidget的focus密不可分:一般来说,一个拥有焦点(focus)的QWidget或者grabKeyboard()的QWidget才可以接受键盘事件. 键盘事件派发给谁? 如 ...
- nginx 事件机制原理
事件驱动模型是Nginx服务器保障完整功能和具有良好性能的重要机制之一. 事件驱动模型概述 实际上,事件驱动并不是计算机编程领域的专业词汇,它是一种比较古老的响应事件的模型,在计算机编程.公共关系.经 ...
- QT之事件机制
MyPushButton.h: #ifndef MYPUSHBUTTON_H #define MYPUSHBUTTON_H #include<QPushButton> #include&l ...
- QT源码之Qt信号槽机制与事件机制的联系
QT源码之Qt信号槽机制与事件机制的联系是本文要介绍的内容,通过解决一个问题,从中分析出的理论,先来看内容. 本文就是来解决一个问题,就是当signal和slot的连接为Qt::QueuedConne ...
- Qt中事件分发源代码剖析(一共8个步骤,顺序非常清楚:全局的事件过滤器,再传递给目标对象的事件过滤器,最终传递给目标对象)
Qt中事件分发源代码剖析 Qt中事件传递顺序: 在一个应该程序中,会进入一个事件循环,接受系统产生的事件,并且进行分发,这些都是在exec中进行的.下面举例说明: 1)首先看看下面一段示例代码: in ...
随机推荐
- 英文构词法 —— ant、ent 后缀
1. -ant:--人 accountant:会计, account(ac+count):计数,账户: assistant:助手: assist:帮助 descendant:后裔: descend:下 ...
- [Android]使用化名(alias)功能防止相同资源的重复
在为一个应用匹配不同资源文件的时候,有时可能需要在不同适配类型的资源路径下使用相同的资源文件,这时使用alias方法可以防止相同资源文件的重复,提高效率.以下摘自Android开发文档http://d ...
- DOS批处理
DOS批处理命令-注释 注释是每个程序中不可或缺的(不是对计算机来说,而是对我们这些程序员阅读代码来说) 语法: ①rem 这是批处理的注释命令,rem后面的内容全部是注释 例:rem 这是一行注 ...
- js精确判断数据类型为何用Object.prototype.toString.call()而不是Object.prototype.toString()
有何区别,为何一定要通过call. 我们知道call是用来改变函数作用域的,Object.prototype.toString.call在这儿也是用来改变作用域的. Object.prototype. ...
- Chrome和FireFox中年份显示为113年的解决方法
下面是段简单的JS显示时间代码片段: <script language="javascript" type="text/javascript"> v ...
- protobuf反射详解
本文主要介绍protobuf里的反射功能,使用的pb版本为2.6.1,同时为了简洁,对repeated/extension字段的处理方法没有说明. 最初是起源于这样一个问题: 给定一个pb对象,如何自 ...
- 用Delphi即时判断当前的网络的连接方式
判断网络的是否连接或者连接方式,比较重要的一点就是是不是即时判断,如果你明明无连接,判断出来却说你有,连接上了,却说你还未连接,下面要介绍的两个方法中 InternetGetConnectedStat ...
- 在IIS上部署.net core的webapi项目 以及502.5错误的两种解决方法
首先要在服务器上面安装.net core https://github.com/dotnet/core/tree/master/release-notes/download-archives 这里面有 ...
- i/o多路复用笔记
1.用户空间和内核空间 操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也可以访问底层硬件设备.为了保护用户进程不能直接操作内核,保证内核的安全,操作系统将虚拟空间划分为两部分, ...
- 用acharengine作Android图表
首先要下载acharengine的包,里面重要的有lib和一些简易的工具,等下我附在文件夹里,而这些包都必须调用的. 然后以下附上主要的作图代码: package org.achartengine.c ...