剖析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 ...
随机推荐
- 解决:insert Vodafone sim card,open the mms read report,when receive the read report,cann't download..
insert Vodafone sim card,open the mms read report,when receive the read report,cann't download the m ...
- Qt::WindowFlags枚举类型(Qt::Widget是独立窗口和子窗口两用的,Qt::Window会有标题栏)
Qt::Widget : QWidget构造函数的默认值,如新的窗口部件没有父窗口部件,则它是一个独立的窗口,否则就是一个子窗口部件. Qt::Window : 无论是否有父窗口部件,新窗口部件都是一 ...
- WPF MVVM系列文章
网上搜到了MSDN Magazine上Laurent Bugnion的系列文章. 以下为关于WPF的优秀实践,很有必要阅读. 很吸引人的标题有: IOC Containers and MVVM Mes ...
- 参数的范数正则/惩罚(parameter norm penalties)
1. L2 范数 J~(w;X,y)=J(w;X,y)+α2wTw J 表示的是原始的目标函数,J~ 则是二范数约束后的新的目标函数. 则根据梯度下降算法有: ∇wJ~=∇wJ+αw w←w−ϵ∇wJ ...
- OpenGl中使用着色器的基本步骤及GLSL渲染简单示例
OpenGL着色语言(OpenGL Shading Language,GLSL)是用来在OpenGL中着色编程的语言,是一种具有C/C++风格的高级过程语言,同样也以main函数开始,只不过执行过程是 ...
- 图像处理结果的度量 —— SNR、PSNR、SSIM
衡量两幅图像的相似度: SNR/PSNR SSIM 1. SNR vs PSNR about SNR 和 PSNR MSE:mean squared error ∑x=1Nx∑y=1Ny(f(x,y) ...
- Frequentist 观点和 Bayesian 观点
1. Frequentist view Frequentist approach views the model parameters as unknown constants(未知的常数,而不是一个 ...
- window7使用MinGW在命令行编译C/C++源程序(从零开始,设置PATH,LIBRARY_PATH,C_INCLUDE_PATH)
1.要想在window命令行下面编译C/C++源程序,对于初学者来说,需要在window环境下面配置好GCC和G++编译器,我使用的是MinGW,下载地址为:http://sourceforge.ne ...
- Go程序开发---Go环境配置:CentOS6.5+Go1.8标准包安装
1.Go安装 1.1Go的三种安装方式 Go有多种安装方式,可以选择自己习惯的方式进行,这里介绍三种安装方式: 1)Go源码安装 2)Go标准包安装 3)第三方工具安装 这里主要介绍下Go标准包在Ce ...
- ASP.NET Core Razor 标签助手 - ASP.NET Core 基础教程 - 简单教程,简单编程
原文:ASP.NET Core Razor 标签助手 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core Razor 标签助手 上一章节我们介绍了视图导入,学习了 ...