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

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. python有关于图像的深度和通道

    目录: (一)图像的深度和图像的通道  (1)图像的深度  (2)图像的通道 (二)自定义一张多通道的图片 (1)zeros 函数 (2)ones  函数 (三)自定义一张单通道的图片 (四)像素操作 ...

  2. 在cmd中使用vim编译器

    下载地址:http://www.vim.org/download.php#pc 下载GVIM,配置下path环境变量就可以在cmd中使用vim了 把vim.exe复制一份,更名为vi.exe,就可以直 ...

  3. [luogu7831]Travelling Merchant

    考虑不断找到以下两种类型的边,并维护答案: 1.终点出度为0的边,那么此时即令$ans_{x}=\min(ans_{x},\max(r,ans_{y}-p))$​ 2.(在没有"终点出度为0 ...

  4. [loj3528]位移寄存器

    当$s=0$时(求最小值): 若$x_{0},x_{1},...,x_{n-1}$和$y_{0},y_{1},...,y_{n-1}$像题中所给的方式存储在$r[0][0..nk-1]$和$r[1][ ...

  5. AOP实现方式二

    applicationContext.xml <!--方法二 自定义类--> <bean id="diyPointCut" class="com.sha ...

  6. docker详细

    镜像(image) 容器(container) 启动,删除,停止 仓库(repository)   docker images  

  7. Object类的toString和Equals方法,以及Objects类的Equals方法

    Object类 toString()方法 public class Person { private String name; private int age; public Person() { } ...

  8. Codeforces 1464F - My Beautiful Madness(树的直径)

    Codeforces 题面传送门 & 洛谷题面传送门 树上数据结构大杂烩(?) 首先考虑什么样的点能够在所有路径的 \(d\) 邻居的交集内.显然如果一个点在一条路径的 \(d\) 邻居内则必 ...

  9. 洛谷 P3266 - [JLOI2015]骗我呢(容斥原理+组合数学)

    题面传送门 神仙题. 首先乍一看此题非常棘手,不过注意到有一个条件 \(0\le x_{i,j}\le m\),而整个矩阵恰好有 \(m\) 列,这就启发我们考虑将每个元素的上下界求出来,如果我们第一 ...

  10. centOS6和7单用户修改密码

    CentOS6 1.       进入启动系统倒计时的时候,按esc 之后进入一下界面: 2.       按a 键进入修改内核参数页面 3.       在quiet后面加入空格和1 ,如下:回车进 ...