在处理QT循环事件的时候遇到了问题,查了半天资料都没弄明白问题出在哪,后来找大牛同事问了一下,同事就给我写了QCoreApplication::processEvent()这个函数,好啦,终于搞定了,这里小记一下,以免以后遇到。

于是乎这里认真仔细的看了一下Qt的事件和事件循环。(引用了碎炎的博客)

事件和事件循环

作为一个事件驱动的工具包,事件和事件传递扮演者Qt架构中的中心角色。在本文中我们不会给出一个对这个话题的全面的概述,我们将着眼于一些线程相关的概念。

事件能被程序的内部和外部产生,举个例子:

QKeyEvent和QMouseEvent对象代表了一个键盘和鼠标的事件,它们从窗口由用户的操作而产生。

QTimerEvent对象是当某个时间被激发时投入,它们都由操作系统产生。

QChildEvent对象是当一个子窗口被添加或者移除时候被送入QObject的,它们的源头是Qt程序自己。

事件的重点是它们被产生的时候不会被传递;它们会先进入事件队列,某刻会被传递。传送者自己循环访问事件队列并把事件传递给目标QObject对象,因此称作事件循环。概念上说,时间循环就像这个:

1.   while (is_active)

2.   {

3.       while (!event_queue_is_empty)

4.           dispatch_next_event();

5.

6.       wait_for_more_events();

7.   }

我们通过运行QCoreApplication::exec()来进入消息循环,这个循环直到exit()或者quit()被调用时才会被堵塞,然后退出。

这个”wait_for_more_events()”函数处于堵塞状态,直到有新的事件被产生了。假如我们考虑它,所有在此刻可能产生的事件是外部源头的。因此,这个消息循环能被以下情况唤醒:

窗口管理活动(鼠标按键操作等);

套接字事件;

定时器事件;

其它线程中投递的事件、

在Unix-like系统中,窗口管理器通过套接字来通知应用程序,即使客户端使用它们来与x server通讯。如果我们决定用内部的套接字对去实现跨线程的事件投递,所有剩下的唤醒条件如下:

套接字;

定时器;

这个就是select(2)系统调用所做的;它监视着一系列的一系列的活动者的描述符,如果它们在一定的时间内没有特定的活动,它们就超时了。

一个运行着的事件循环需要什么?

这个不是完整的列表,但是如果你有整体画面,你将能够去猜测什么类需要一个运行着的事件循环。

Widgets的绘画和互动:QWidget::paintEvent()将在传递QPaintEvent对象时候被调用,这个对象将会在调用QWidget::update()或者窗口管理器的时候产生:响应的事件将需要一个时间循环去分发。

Timers:长话短说,它们在当select(2)或者超时的时候产生,因此它们需要让Qt在返回时间循环的时候作这些调用。

Networking“所有的底层Qt网络类 (QTcpSocket,QUdpSocket,QTcpServer等)都是异步设计的。当你调用ready(),它们只是返回已经可用的数据,当你调 用write(),它们只是将这个操作放入队列,适时会写入。只有当你返回消息循环的时候真实的读取,写入才会执行。注意它们确实提供了同步的方法,但是 它们的用法是不被提倡的,因为它们会堵塞事件循环。高级类,比如QNetworkAccessManager,简单的不提供同步API,需要一个事件循 环。

堵塞事件循环

在我们讨论为什么你应该从不堵塞消息循环之前,我们试着分析堵塞的含义。假象你有一个按钮,它将会在它被点击的时候发出clicked信号;在我们的对象中连接着一个槽函数,当你点击了那个按钮后,栈追踪将会像这样:

1.     main(int, char **)

2.     QApplication::exec()

3.     […]

4.     QWidget::event(QEvent *)

5.     Button::mousePressEvent(QMouseEvent *)

6.     Button::clicked()

7.     […]

8.     Worker::doWork()

在main函数中我们启动了时间循环,平常的调用了 exex(),窗口管理器给我们发送了一个鼠标点击事件,它被Qt内核取走,转换成QMouseEvent并被送往我们widget的event()方 法,该方法被QApplication::notify()发送。因为按钮没有重写event(),基类方法将被调用,QWidget::event() 检测到了这个事件确实是一个鼠标点击事件,然后调用特定的事件处理函数,那就是Button::mousePressEvent(),我们重写这个方法去 发送clicked()信号,这将会调用被连接的槽函数。

当该对象处理量很大,那么消息循环在作什么?我们应该猜测它:什么都不做!它分发鼠标按下事件,然后就堵塞着等待着事件处理函数返回。这个就是堵塞了时间循环,它意味着没有消息被分发了,知道我们从槽函数返回了,然后继续处理挂起的消息。

在消息循环被卡住的情况下,widgets将不能更新它们 自身,不可能有更多的互动,timers将不会被激发,网络通讯将缓慢下来,或者停止。进一步的说,许多窗口管理器将检测到你的应用程序不在处理事件了, 然后告诉用户你的程序没有响应。这就是为什么快速的对事件响应并且即时返回到事件循环是多么的重要!

强制事件分发

所以,假如我们有一个很长的任务去运行但是又不希望堵塞这 个消息循环,该怎么做呢?一个可能的回答是将这个任务移到另一个线程中,在下一个张洁我们将看到这是如何做的。我们也能手动强制事件循环去运行,这个方法 是通过在堵塞的任务函数中调用QCoreApplication::processEvent()来实现 的,QCoreApplication::processEvent()将处理所有在消息队列中的消息并返回给调用者。

另一个可选的选项是我们能够强制重入事件循环的对象,就是QEventLoop类。通过调用QEventLoop::exec()我们将重入事件循环,然后我们能将槽函数QVentLoop::quit()连接到信号上去使它退出。举个例子:

1.   QNetworkAccessManager qnam;

2.   QNetworkReply *reply = qnam.get(QNetworkRequest(QUrl(...)));

3.   QEventLoop loop;

4.   QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));

5.   loop.exec();

6.   /* reply has finished, use it */

QNetworkReply不提供堵塞的API,它要求一个在运行的事件循环。我们进入了一个本地的事件循环,然后当回复完成时候,这个本地的循环退出了。

要特别小心的是在其它路径下重入事件循环:它可能导致不希望的递归!让我们回到前面看看按钮的例子。假如我们在槽函数中调用了QCoreApplication::processEvent(),当用户点击了这个按钮,这个槽函数将被再次调用:

1.     main(int, char **)

2.     QApplication::exec()

3.     […]

4.     QWidget::event(QEvent *)

5.     Button::mousePressEvent(QMouseEvent *)

6.     Button::clicked()

7.     […]

8.     Worker::doWork() // first, inner invocation

9.     QCoreApplication::processEvents() // we manually dispatch events and…

10.   […]

11.   QWidget::event(QEvent * ) // another mouse click is sent to the Button…

12.   Button::mousePressEvent(QMouseEvent *)

13.   Button::clicked() // which emits clicked() again…

14.   […]

15.   Worker::doWork() // DANG! we’ve recursed into our slot.

一个快速并简便的变通方法是把QEventLoop::ExcludeUserInputEvent传递给QCoreApplication::processEvents(),这会告诉消息循环不要再次分发任何用户的输入事件。

幸运的是,这个相同的事情不会在检测事件中发生。事实上,它们被Qt通过特殊的方法处理了,只有当运行的时间循环有了一个比deleteLater被调用后更小的”nesting”值才会被处理:

1.   QObject *object = new QObject;

2.   object->deleteLater();

3.   QDialog dialog;

4.   dialog.exec();

将不会使object成为一个悬空指针。相同的东西被应用 到了本地的事件循环中。唯一的一个显著区别我已经发现了,它在假如当没有事件循环在运行的时候deleteLater被调用了的条件下,然后第一个消息循 环进入了后会取走这个事件,然后删除这个object。这是相当合理的,因为Qt不知道任何外部的循环将最终影响这个检测,因此马上删除了这个 object。

Qt事件和事件循环的更多相关文章

  1. Qt ------ 再论事件循环

    在介绍在以前,我们要认识两个术语: 可重入的(Reentrant):如果多个线程可以在同一时刻调用一个类的所有函数,并且保证每一次函数调用都引用一个唯一的数据,就称这个类是可重入的(Reentrant ...

  2. 【Qt开发】事件循环与线程 二

    事件循环与线程 二 Qt 线程类 Qt对线程的支持已经有很多年了(发布于2000年九月22日的Qt2.2引入了QThread类),Qt 4.0版本的release则对其所有所支持平台默认地是对多线程支 ...

  3. 【Qt开发】事件循环与线程 一

    事件循环与线程 一 初次读到这篇文章,译者感觉如沐春风,深刻体会到原文作者是花了很大功夫来写这篇文章的,文章深入浅出,相信仔细读完原文或下面译文的读者一定会有收获. 由于原文很长,原文作者的行文思路是 ...

  4. Qt自定义事件的实现(军队真正干活,但要增加监军,大平台通知事件,事件内容自定义)

    初学Qt,用了Qt自带的事件,然后想怎么才能定义自己的事件呢?又如何使用自定义事件呢?看了篇文章,说先要子类化QEvent,然后定义自己的QEvent::Type,然后重写QWidget::event ...

  5. qt中的事件机制

    事件 1.QEvent -->类型 -> QKeyEvent QEvent::KeyRelease QEvent::MouseMove -> QMouseEvent 2.事件处理过程 ...

  6. 【转】Qt鼠标键盘事件

    http://blog.csdn.net/lovebird_27/article/details/50351336 Qt 程序需要在main()函数创建一个QCoreApplication对象,然后调 ...

  7. Qt消息机制和事件、事件过滤

    一,事件 事件(event)是由系统或者 Qt 本身在不同的时刻发出的.当用户按下鼠标.敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件.一些事件在对用户操作做出响应时发出,如键盘事件等 ...

  8. QT父子窗口事件传递与事件过滤器(讲了一些原理,比较清楚)

    处理监控系统的时候遇到问题,在MainWidget中创建多个子Widget的时候,原意是想鼠标点击先让MainWidget截获处理后再分派给子Widget去处理,但调试后发现如果子Widget重新实现 ...

  9. Qt之键盘事件监听-实时响应大小写Capslock按键

    目录 一.开篇 二.效果展示 三.实现思路 1.重写QLlinEdit 2.全局应用程序事件 3.windows钩子 四.相关文章 原文链接:Qt之键盘事件监听-实时响应大小写Capslock按键 一 ...

随机推荐

  1. 数据结构中的棧在C#中的实现

    数据结构中的棧在C#中的实现 一.大致学习 棧是一种面向表的数据结构,棧中的数据只能在标的某一短进行添加和删除操作,是一种典型的(LIFO)数据结构. 现实生活中的理解:自助餐厅的盘子堆,人们总是从顶 ...

  2. CentOS-6.5x64:SSH安装配置

    1.CentOS 默认已经安装了 OpenSSH 2.vim /etc/ssh/sshd_config Port: SSH的监听端口 默认为22,设置为[Port 22] Protocol:SSH允许 ...

  3. FileTable初体验

    FileTable初体验 阅读导航 启用FILESTREAM设置 更改FILESTRAM设置 启用数据库非事务性访问级别 FileTable 在我接触FileTable之前,存储文件都是存储文件的链接 ...

  4. Python之路 1Day

    1.python基础入门   作者:黄旭 提示: 语法基于python3.5版本(会提示2.7版本和3.5版本的区别) Python命令行将以>>>开始,比如 >>> ...

  5. get post 知多少

    GET与POST简介 POST和GET都属于http请求的方法,所以都包含开始行,头域,头域结束符,消息主体,但是,他们同样存在很多异同,为了更好的区别这两种请求,我们对他们的异同进行具体的分析. 表 ...

  6. HttpURLConnection碰到连续302跳转的问题的原因及解决方法

    用HttpURLConnection联网的代码: HttpURLConnection conn = null;       URL url = new URL("http://10.0.0. ...

  7. TCP/IP传输层,你懂多少?

    1. 传输层的主要功能是什么?2. 传输层如何区分不同应用程序的数据流?3. 传输层有哪些协议?4. 什么是UDP协议?5. 为什么有了UDP,还需要TCP?6. 什么是TCP协议?7. 怎么理解协议 ...

  8. java 异常处理机制及说明。

    又抄袭了一篇文章,其实就是想保存到自己的博客中而已,文章出处:http://www.cnblogs.com/LilianChen/p/4639471.html 1. 如何捕获异常 try { 可能会出 ...

  9. shapeless官方指南翻译写在前面

    目录 前言 Shapeless简介 The Type Astronaut's Guide to Shapeless简介 总结 一.前言        在我的2016,感恩.乐观.努力一文中,说2017 ...

  10. 【JS学习笔记】第一个JS效果——鼠标提示框

    分析效果实现原理--鼠标提示框 样式:div的display 事件:onmouseover,onmouseout 编写JS的流程 布局:HTML+CSS 属性:确定需要修改哪些属性 事件:确定用户做哪 ...