------------恢复内容开始------------

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::processEvents

while循环依次检查,首先处理没有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干了啥?的更多相关文章

  1. [QT]QApplication和QCoreApplication的用法

    转自:http://www.tuicool.com/articles/qmI7Bf 故事的背景是这样的,我们在写QT程序的时候或者在开始写QT程序之前总会看到这样的语句 QApplication ap ...

  2. Qt程序继承QApplication发生崩溃的原因

    一.前情介绍 QApplication是Qt开发中经常用到的一个类,用来管理应用程序的生命周期.跟其相关的类还有QCoreApplication和QGuiApplication,分别用于不同场景下为应 ...

  3. 1、QT分析之QApplication的初始化

    原文地址:http://blog.163.com/net_worm/blog/static/1277024192010097430321/ 在开始分析之前交代一下,一是分析的QT在Window平台实现 ...

  4. QT分析之QApplication的初始化

    原文地址:http://blog.163.com/net_worm/blog/static/1277024192010097430321/ 在开始分析之前交代一下,一是分析的QT在Window平台实现 ...

  5. QT多线程及通过事件进行通信(通过自定义事件,然后QApplication::postEvent给主界面,我之前用的是信号槽)

    可以通过QThread实现跨平台的多线程开发,Qt库负责在特定平台上的特定多线程实现.要采用QThread进行多线程开发,首先需要包含头文件: #include <QThread> 然后需 ...

  6. Qt入门(7)——QApplication类

    QApplication类管理图形用户界面应用程序的控制流和主要设置.它包含主事件循环,在其中来自窗口系统和其它资源的所有事件被处理和调度.也用于处理应用程序的初始化和结束,并且提供对话管理.它也处理 ...

  7. 解决Qt程序发布时中文乱码问题(通过QApplication.addLibraryPath加载QTextCodec插件)

    Qt程序的文字编码,是通过插件来解决的,所以我们发布的时候需要把相应的插件也发布出去,在开发者电脑上程序会自动从插件目录加载到插件,但是如果发布给别的电脑使用,需要手动指定插件路径,如下所示: int ...

  8. Qt中如何禁掉所有UI操作以及注意事项(处理各个widget的eventFilter这一层,但是感觉不好,为什么不使用QApplication呢)

    刚做完的一个项目,在测试时出现了一个问题:由于多线程的存在,当进行语音识别时:如果用户点击程序界面上的button或者其他接受点击事件后会发出信号的widget时,程序会crash ! 后来尝试着从多 ...

  9. Qt 如何处理密集型耗时的事情(频繁调用QApplication::processEvents)

    有时候需要处理一些跟界面无关的但非常耗时的事情,这些事情跟界面在同一个线程中,由于时间太长,导致界面无法响应,处于“假死”状态.例如:在应用程序中保存文件到硬盘上,从开始保存直到文件保存完毕,程序不响 ...

随机推荐

  1. [cf1491F]Magnets

    首先,只需要找到一个有磁性的位置,就可以通过$n-1$次判断其余磁铁是否有磁性,因此也就是要在$\lfloor\log_{2}n\rfloor+1$次中找到一个有磁性的位置 有一个$n-1$次的做法, ...

  2. [cf582E]Boolean Function

    由于每一个运算都有括号,因此添加的运算不会改变运算顺序 先将其建出一棵表达式树,也就是维护两个栈,是节点和运算符优先级单调递增的栈(设置左括号优先级最低,右括号弹出直至左括号) 每一次运算,也就是新建 ...

  3. [atARC109F]1D Kingdom Builder

    考虑最终有石子的位置的状态,判断一种状态是否可行 反过来,依次删除石子,删除条件是:当删除的石子是该段最后一个(即其两边都没有石子了),要求除其以外,每个连续段旁边的两个点都与其颜色不同 构造一种删除 ...

  4. 第一章 初始C语言

    第一章 初始C语言 目录 第一章 初始C语言 1. C语言起源 2. 选择C语言的理由 2.1 设计特性 2.2 高效性 2.3 可移植性 2.4 强大而灵活 2.5 面向程序员 3. C语言的应用范 ...

  5. JavaScript高级程序设计读后感(一)之零碎知识点查漏补缺

    目录 1-script延迟脚本defer及异步脚本async,区别及应用场景 2-未声明的变量,未初始化变量 3-Number parseInt 字符串转数值 ,进制转换 4-undefined &a ...

  6. char数据可以放入int[]中会自动转换

    int[] ary ={'b','c','a','d','e','f'};System.out.println(ary[0]);//98String str = new String(ary, 2, ...

  7. Nocalhost 为 KubeSphere 提供更强大的云原生开发环境

    作者简介 张海立(驭势科技云平台研发总监):开源爱好者,云原生社区上海站 PMC 成员,KubeSphere Ambassador:日常云原生领域工作涉及 Kubernetes.DevOps.可观察性 ...

  8. 【宏蛋白组】iMetaLab平台分析肠道宏蛋白质组数据

    目录 一.iMetaLab简介 二.内置工具与模块 1. Data Processing module 2. Functional Analysis 3. R Developing environme ...

  9. java中的Arrays类

    今天刚接触了数组,学到了几个比较常用的方法 Fill方法:给数组赋值 sort方法:给数组升序 equals方法:比较数组中元素 值是否相等 binarySearch方法:对排序好的数组进行二分查找法 ...

  10. ssm框架整合 — 更新完毕

    1.spring整合mybatis 数据表自行搭建 ,我的结构如下: 1).导入依赖 <!-- spring整合mybatis的依赖 --> <!-- 1.spring需要的依赖 - ...