QT 源码之 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 -;
//获取线程数据
QThreadData *threadData = self->d_func()->threadData;
//判断是否在主线程创建
if (threadData != QThreadData::current()) {
qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());
return -;
}
//判断eventLoop是否已经创建
if (!threadData->eventLoops.isEmpty()) {
qWarning("QCoreApplication::exec: The event loop is already running");
return -;
}
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(, QEvent::DeferredDelete);
}
return returnCode;
}
再来到qeventloop.cpp中。
int QEventLoop::exec(ProcessEventsFlags flags)
{
Q_D(QEventLoop);
if (d->threadData->quitNow)
return -;
//已经调用过exec了。
if (d->inExec) {
qWarning("QEventLoop::exec: instance %p has already called exec()", this);
return -;
}
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(, 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(, , d->threadData);
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()) {
// process queued socket events 处理socket事件,放入队列中。
haveMessage = true;
msg = d->queuedSocketEvents.takeFirst();
} else {
//从消息队列中取消息,同PeekMessage
haveMessage = winPeekMessage(&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)) {
// 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=; i<(int)nCount; i++)
pHandles[i] = d->winEventNotifierList.at(i)->handle();
//注册signal--slot。
waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, , 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 = ; !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 *ddata = d->threadData;
canWait = (!retVal
&& data->canWait
&& !d->interrupt
&& (flags & QEventLoop::WaitForMoreEvents));
if (canWait) {
DWORD nCount = d->winEventNotifierList.count();
Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - );
for (int i=; 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;
}

小结:关于详解 QT 源码之 Qt 事件机制原理的内容介绍完了,基本属于代码实现的内容,最后希望本文对你有帮助!

详解 QT 源码之 Qt 事件机制原理的更多相关文章

  1. QT源码之Qt信号槽机制与事件机制的联系

    QT源码之Qt信号槽机制与事件机制的联系是本文要介绍的内容,通过解决一个问题,从中分析出的理论,先来看内容. 本文就是来解决一个问题,就是当signal和slot的连接为Qt::QueuedConne ...

  2. select用法&amp;原理详解(源码剖析)(转)

    今天遇到了在select()前后fd_set的变化问题,查了好久终于找到一个有用的帖子了,很赞,很详细!!原文链接如下: select用法&原理详解(源码剖析) 我的问题是: 如下图示:在se ...

  3. 基于双向BiLstm神经网络的中文分词详解及源码

    基于双向BiLstm神经网络的中文分词详解及源码 基于双向BiLstm神经网络的中文分词详解及源码 1 标注序列 2 训练网络 3 Viterbi算法求解最优路径 4 keras代码讲解 最后 源代码 ...

  4. Android应用AsyncTask处理机制详解及源码分析

    1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个知识点.前面我们分析了Handler异步机制原理(不了解的可以阅读我的<Android异步消息处理机 ...

  5. SpringBoot之DispatcherServlet详解及源码解析

    在使用SpringBoot之后,我们表面上已经无法直接看到DispatcherServlet的使用了.本篇文章,带大家从最初DispatcherServlet的使用开始到SpringBoot源码中Di ...

  6. Java SPI机制实战详解及源码分析

    背景介绍 提起SPI机制,可能很多人不太熟悉,它是由JDK直接提供的,全称为:Service Provider Interface.而在平时的使用过程中也很少遇到,但如果你阅读一些框架的源码时,会发现 ...

  7. Spring Boot启动命令参数详解及源码分析

    使用过Spring Boot,我们都知道通过java -jar可以快速启动Spring Boot项目.同时,也可以通过在执行jar -jar时传递参数来进行配置.本文带大家系统的了解一下Spring ...

  8. 【转载】Android应用AsyncTask处理机制详解及源码分析

    [工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果] 1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个 ...

  9. SpringBoot内置生命周期事件详解 SpringBoot源码(十)

    SpringBoot中文注释项目Github地址: https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE 本篇接 SpringBoot事件监听 ...

随机推荐

  1. 原创:跳坑指南——微信小程序真机预览跟本地不同的问题

    微信小程序中出现最多的一个问题,就是真机跟本地不同:我简单列举一些我发现的原因,给大家参考,大家也可以把自己发现的东西回复给我,给我参考:本地看不到数据,就先让本地能看到数据,再看本帖.... 1:本 ...

  2. Kali Linux (XFce版本)安装后的一些设置

    kali Linux的主版本自带的是Gnome桌面环境,安装后使用效率太低,不知道是不是我机器配置低的原因, 在虚拟机里运行起来太慢.卡.丑啦....所以以前都一直都在用Backbox Linux,并 ...

  3. 网络爬虫讲解(附java实现的实例)

    本文转载http://blog.csdn.net/luojinping/article/details/6870898 转载过来主要是防止想百度文库一样突然停止运作,导致很多文库丢失而无法找寻 网络蜘 ...

  4. Windows 10开启默认网络驱动器访问

    在Windows 10的系统策略中,驱动器盘符的网络访问是默认关闭的,用管理员权限打开注册表,找到HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Curre ...

  5. 实现一个基于FTP协议的程序——文件上传下载器(十三)

    此为一个系列,后续会把内容补上...

  6. iis7 发布mvc3 遇到的HTTP错误 403.14-Forbidden Web 服务器被配置为不列出此目录的内容及Login on failed for &quot;IIS APPPOOL\ASP.NET v4.0&quot;问题

    问题1: 发布mvc3报错:403.14-Forbidden Web 服务器被配置为不列出此目录的内容 折腾了半天,提示里面的解决方法是: 如果不希望启用目录浏览,请确保配置了默认文档并且该文件存在. ...

  7. CoreLocation导航Demo

    CoreLocation实现定位和导航功能还是非常简单的,基本思路是: 1.导入<CoreLocation/CoreLocation.h>头文件 2.使用该框架内的导航管理者,创建该导航管 ...

  8. linux硬盘挂载-新硬盘挂载和扩容硬盘挂载

    这里对当前我实际操作后的两种硬盘挂载进行整理: 第1种是直接添加一块新硬盘,然后进行挂载. 第2种是对硬盘进行扩容后,对扩容后的空间进行分区再进行挂载. [内容为参考网上资料,再加自已实际操作情况进行 ...

  9. 复习-css边框和背景属性

    css边框和背景属性 border:所有边框属性 border-width:四条边框的宽度 border-style:设置边框样式,主要有dotted.solid.double border-colo ...

  10. IDEA 热部署- 自动编译设置

    原文:https://www.cnblogs.com/TechSnail/p/7690829.html    &&   https://blog.csdn.net/qq_3129357 ...