QT QApplication干了啥?
------------恢复内容开始------------
QCoreApplicationPrivate 会取得current thread; 在windows平台创建TLS变量,记录线程信息,并将此线程记录为主线程。交由QCoreApplicationPrivate::theMainThread维护。

QThreadData *QThreadData::current(bool createIfNecessary)
{
qt_create_tls();
QThreadData *threadData = reinterpret_cast<QThreadData *>(TlsGetValue(qt_current_thread_data_tls_index));
if (!threadData && createIfNecessary) {
threadData = new QThreadData;
// This needs to be called prior to new AdoptedThread() to
// avoid recursion.
TlsSetValue(qt_current_thread_data_tls_index, threadData);
QT_TRY {
threadData->thread = new QAdoptedThread(threadData);
} QT_CATCH(...) {
TlsSetValue(qt_current_thread_data_tls_index, 0);
threadData->deref();
threadData = 0;
QT_RETHROW;
}
threadData->deref();
threadData->isAdopted = true;
threadData->threadId.storeRelaxed(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId()))); if (!QCoreApplicationPrivate::theMainThread) {
QCoreApplicationPrivate::theMainThread = threadData->thread.loadRelaxed();
} else {
HANDLE realHandle = INVALID_HANDLE_VALUE;
DuplicateHandle(GetCurrentProcess(),
GetCurrentThread(),
GetCurrentProcess(),
&realHandle,
0,
FALSE,
DUPLICATE_SAME_ACCESS);
qt_watch_adopted_thread(realHandle, threadData->thread);
}
}
return threadData;
}
每个private init 都会先执行上个init,首先执行QCoreApplicationPrivate::init();
initLocale(), 初始化linux相关locale,在windows平台这步好像被跳过。接着创建eventDispatcher.这是个整个qt事件机制的核心。

plugins\platforms\qwindowsd.dll ,加载相关插件。先创建并初始化平台相关内容,其中创建了QWindowsGdiIntegration,记录平台相关信息。还会查找平台主题相关内容。
然后创建evenDispatcher. QWindowsGuiEventDispatcher.记录几个关键的变量:


创建internalHwnd.什么internal_window?看上去就是一个普通的win32 窗口。

static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatcher)
{
QWindowsMessageWindowClassContext *ctx = qWindowsMessageWindowClassContext();
if (!ctx->atom)
return 0;
HWND wnd = CreateWindow(ctx->className, // classname
ctx->className, // window name
0, // style
0, 0, 0, 0, // geometry
HWND_MESSAGE, // parent
0, // menu handle
GetModuleHandle(0), // application
0); // windows creation data. if (!wnd) {
qErrnoWarning("CreateWindow() for QEventDispatcherWin32 internal window failed");
return 0;
} #ifdef GWLP_USERDATA
SetWindowLongPtr(wnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(eventDispatcher));
#else
SetWindowLong(wnd, GWL_USERDATA, reinterpret_cast<LONG>(eventDispatcher));
#endif return wnd;
}
qt_create_internal_window
QWindowsMessageWindowClassContext又干了什么?保存了一些WNDCLASS信息,className 是个奇怪的值,后面的数值是函数地址,QEventDispatcherWin32_Internal_Widget140726494787934。qt_internal_proc这个回调也重要。HWND_MESSAGE表明这是一个专门用于回调的窗口,这个窗口并不会显示出来,不会接受广播消息,自然不会接收到鼠标或键盘消息,只接受指定给它的消息。
QWindowsMessageWindowClassContext将创建的disPatcher 作为用户数据保存在窗口的缓存。对当前线程设置钩子。
qt_GetMessageHook:指向相应的挂钩处理过程.若参数dwThreadId为0或者指示了一个其他进程创建的线程之标识符,则参数lpfn必须指向一个动态链接中的挂钩处理过程.否则,参数lpfn可以指向一个与当前进程相关的代码中定义的挂钩处理过程.
hMod(NULL):指示了一个动态链接的句柄,该动态连接库包含了参数lpfn 所指向的挂钩处理过程.若参数dwThreadId指示的线程由当前进程创建,并且相应的挂钩处理过程定义于当前进程相关的代码中,则参数hMod必须被设置为NULL(0).
WH_GETMESSAGE Hook来监视从GetMessage or PeekMessage函数返回的消息。
至此,eventdispatcher创建完毕。大致设想一下,先由钩子处理消息,再有internal_window处理消息。
eventdispatcherReady(), platform_integration->initialize();创建了一个QWindowsInputContext。接着处理命令行输入。
接着在QGuiApplicationPrivate::init()又干了啥? 在windows平台生成session_id和session_key。设置palette,初始化font。mouseDoubleClickDistance,touchDoubleTapDistance相关设置
以及生成鼠标形状信息。检查opengl相关信息。QApplicationPrivate::process_cmdline()。
void QApplicationPrivate::initialize();创建静态变量
QWidgetPrivate::mapper = new QWidgetMapper;//map WID -> WIDGET
QWidgetPrivate::allWidgets = new QWidgetSet;// set WIDGET
下一步eventDispatcher->startingUp(); //目前是个空实现。整个application的初始化工作大致就是这些了。
QCoreApplication::exec();创建主eventLoop. eventLoop循环处理processEvents最终都会调到eventDispathcer的processEvent;
每次处理processEvents之前发送信号awake, 接着sendPostedEvents();

void QEventDispatcherWin32::sendPostedEvents()
{
Q_D(QEventDispatcherWin32); if (d->sendPostedEventsTimerId != 0)
KillTimer(d->internalHwnd, d->sendPostedEventsTimerId);
d->sendPostedEventsTimerId = 0; // Allow posting WM_QT_SENDPOSTEDEVENTS message.
d->wakeUps.storeRelaxed(0); QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData.loadRelaxed());
}
sendPostedEventsTimerId是又SetTimer接口返回的(在qt_GetMessageHook里执行的),每次执行sendPostedEvents都会执行赋值0操作。会将之前postEvent,所累计的消息postEventList全部处理完毕。逐一通过sendEvent发送给对应的接受者。
sendWindowSystemEvents,处理windows本地的消息,再封装成QT自己的消息,传递给对应的接受者。
QEventDispatcherWin32::processEventswhile循环依次检查,首先处理没有ExcludeUserInputEvents的标记且queuedUserInputEvents不为空的情况。接着处理没有ExcludeSocketNotifiers且queuedSocketEvents不为空的的情况,然后处理peekMessage的信息。在处理这个分支时,peekMessage会触发前文提到的qt_GetMessageHook。我们回到qt_GetMessageHook:

主要看这个逻辑判断,消息是否是指定发给前文的消息窗口,消息类型是否是WM_QT_SENDPOSTEDEVENTS,是否带有PM_REMOVE标记,以及d->sendPostedEventsTimerId是否为0;我们
先了解一下setTimer是干嘛的。该函数会返回一个定时器标识,给指定窗口发送一个wm_timer消息。

源自http://www.cppblog.com/ivenher/articles/19969.html。
hook返回之后,检测ExcludeUserInputEvents是否存在,再判断是不是用户输入,会将这个事件加入到queuedUserInputEvents,直到没有ExcludeUserInputEvents再处理。对queuedSocketEvents同样处理。
if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) {
// Set result to 'true' because the message was sent by wakeUp().
retVal = true;
continue;
}
通过wakeUp来打破这个do...while循环,进行新一轮的processEvent.
当没有外部事件时,setTimer触发,不停发送timer事件。
if (msg.message == WM_TIMER)
// Skip timer event intended for use inside foreign loop.
if (d->internalHwnd == msg.hwnd && msg.wParam == d->sendPostedEventsTimerId)
continue;
整个循环空转,直至接受到外部输入事件,比如主动wakeUp,或者window其他消息,当这个消息派发出去后,其实一般会调到qt_internal_proc wm_timer处理逻辑里,至于为什么会触发到timer,难道是在捕获到系统消息后,timer事件还在继续发送,所以会调到这来?wm_timer消息会自动派发到消息窗口(hwnd_message)吗?这个函数栈很费解。


可以看到在这调了一遍sendPostedEvents;
void QWindowsGuiEventDispatcher::sendPostedEvents()
{
QEventDispatcherWin32::sendPostedEvents();
QWindowSystemInterface::sendWindowSystemEvents(m_flags);
}
void QEventDispatcherWin32::sendPostedEvents()
{
Q_D(QEventDispatcherWin32); if (d->sendPostedEventsTimerId != 0)
KillTimer(d->internalHwnd, d->sendPostedEventsTimerId);
d->sendPostedEventsTimerId = 0; // Allow posting WM_QT_SENDPOSTEDEVENTS message.
d->wakeUps.storeRelaxed(0); QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData.loadRelaxed());
}
清除计时器,这也能退出do...while循环空转,d->sendPostedEventsTimerId = 0;代表新一轮的processEvent马上开始了。d->wakeUps.storeRelaxed(0);代表允许给internal_window窗口 post WM_QT_SENDPOSTEDEVENTS message,其实也是setTimer的开关。timer的意义是想定时把系统消息队列的事件一次性处理干净吗?那WM_QT_SENDPOSTEDEVENTS 呢?干的好像也是差不多的事,消息队列为空,才setPostEvent,那这样的话,只会处理qt的postEvent,模态对话框起作用。差别在于是不是主动的,timer的方式是被动的,
对于系统消息 ,会通过handleWindowSystemEvent暂时加到队列,并wakeup,向internal_window窗口 post WM_QT_SENDPOSTEDEVENTS message, 下一次getMessageHook,检测到这个post给internal_window的WM_QT_SENDPOSTEDEVENTS消息时,会setTimer.

前面讲了postEvent的处理时机,那么关于sendEvent呢?注意下和sendSpontaneousEvent的区别,sendEvent是不是由系统触发的,是手动sendEvent的。

一般的事件,先是执行本类的event函数,再在Qwidget::event 派发给具体的事件重写函数如showEvent(该函数在窗口未显示之前执行)。
QT QApplication干了啥?的更多相关文章
- [QT]QApplication和QCoreApplication的用法
转自:http://www.tuicool.com/articles/qmI7Bf 故事的背景是这样的,我们在写QT程序的时候或者在开始写QT程序之前总会看到这样的语句 QApplication ap ...
- Qt程序继承QApplication发生崩溃的原因
一.前情介绍 QApplication是Qt开发中经常用到的一个类,用来管理应用程序的生命周期.跟其相关的类还有QCoreApplication和QGuiApplication,分别用于不同场景下为应 ...
- 1、QT分析之QApplication的初始化
原文地址:http://blog.163.com/net_worm/blog/static/1277024192010097430321/ 在开始分析之前交代一下,一是分析的QT在Window平台实现 ...
- QT分析之QApplication的初始化
原文地址:http://blog.163.com/net_worm/blog/static/1277024192010097430321/ 在开始分析之前交代一下,一是分析的QT在Window平台实现 ...
- QT多线程及通过事件进行通信(通过自定义事件,然后QApplication::postEvent给主界面,我之前用的是信号槽)
可以通过QThread实现跨平台的多线程开发,Qt库负责在特定平台上的特定多线程实现.要采用QThread进行多线程开发,首先需要包含头文件: #include <QThread> 然后需 ...
- Qt入门(7)——QApplication类
QApplication类管理图形用户界面应用程序的控制流和主要设置.它包含主事件循环,在其中来自窗口系统和其它资源的所有事件被处理和调度.也用于处理应用程序的初始化和结束,并且提供对话管理.它也处理 ...
- 解决Qt程序发布时中文乱码问题(通过QApplication.addLibraryPath加载QTextCodec插件)
Qt程序的文字编码,是通过插件来解决的,所以我们发布的时候需要把相应的插件也发布出去,在开发者电脑上程序会自动从插件目录加载到插件,但是如果发布给别的电脑使用,需要手动指定插件路径,如下所示: int ...
- Qt中如何禁掉所有UI操作以及注意事项(处理各个widget的eventFilter这一层,但是感觉不好,为什么不使用QApplication呢)
刚做完的一个项目,在测试时出现了一个问题:由于多线程的存在,当进行语音识别时:如果用户点击程序界面上的button或者其他接受点击事件后会发出信号的widget时,程序会crash ! 后来尝试着从多 ...
- Qt 如何处理密集型耗时的事情(频繁调用QApplication::processEvents)
有时候需要处理一些跟界面无关的但非常耗时的事情,这些事情跟界面在同一个线程中,由于时间太长,导致界面无法响应,处于“假死”状态.例如:在应用程序中保存文件到硬盘上,从开始保存直到文件保存完毕,程序不响 ...
随机推荐
- [loj2462]完美的集合
当$k$个集合依次为$S_{1},S_{2},...,S_{k}$时,称$x$合法当且仅当: 1.$\forall 1\le i\le k,x\in S_{i}$ 2.$\forall y\in \b ...
- [loj3313]序列
定义$C_{i}$表示令$i,i+1,i+2,...$的位置减1的操作,定义$I_{i}$表示令$i,i+2,i+4,...$的位置减1的操作 结论1:一定存在一种最优解使得$\forall i$不同 ...
- AOP实现方式一
1.创建相应的类 2.代码 service沿用前面的 增加两个log Log.java package com.shao.log; import org.springframework.aop.Met ...
- 【HTML】基础
HTML基础 2019-07-23 10:16:28 by冲冲 在线编辑HTML/CSS/JS效果,实时查看效果 https://c.runoob.com/front-end/61 1. 概念 ① ...
- 【Java面试题】-- Java基本类型
Java基本类型 2019-11-03 19:03:48 by冲冲 1.两个float型相减丢失精度,如何解决? 使用BigDemical装饰器模式 public class Test { pub ...
- Terminator--最强Ubuntu终端
个人使用的一个非常好用的Ubuntu终端软件. Terminator的安装和配置 安装 Ubuntu sudo add-apt-repository ppa:gnome-terminator sudo ...
- Codeforces 856D - Masha and Cactus(树链剖分优化 dp)
题面传送门 题意: 给你一棵 \(n\) 个顶点的树和 \(m\) 条带权值的附加边 你要选择一些附加边加入原树中使其成为一个仙人掌(每个点最多属于 \(1\) 个简单环) 求你选择的附加边权值之和的 ...
- 洛谷 P7116 - [NOIP2020] 微信步数(拉格朗日插值)
洛谷题面传送门 我竟然独立切掉了这道题!incredible! 纪念我逝去的一上午(NOIP 总时长 4.5h,这题做了我整整 4.5h) 首先讲一下现场我想的 80 分的做法,虽然最后挂成了 65 ...
- 【R shiny】一些应用记录
目录 DT和downloadButton应用 downloadButton 中验证结果输出 添加进度条 如何确保仅在使用Shiny按下操作按钮时才触发操作 其他 DT和downloadButton应用 ...
- Linux之vi和vim编辑器
目录 1. vi和vim简介 2. vi 和 vim 的三种常见模式 2.1 正常模式 2.2 插入模式 2.3 命令行模式 3. 三种模式间的切换 4. 常用快捷键案例 5. 常用命令 1. vi和 ...