1. At any time, you can create a QEventLoop object and call exec() on it to start a local event loop. From within the event loop, calling exit() will force exec() to return.
  2. 在任何时候,你都可以创建一个QEventLoop的对象,然后调用它的exec()来开始一个局部时间的循环

EventLoop本质就是一个while(1),程序会在exec里边无限循环,不让后边的代码执行。除非调用quit让循环退出。
事件循环在Qt GUI编程里也常见,比如main.cpp中:

  1. #include "widget.h"
  2. #include <QApplication>
  3. int main(int argc, char *argv[])
  4. {
  5. QApplication a(argc, argv);
  6. Widget w;
  7. w.show();
  8. return a.exec();
  9. }

程序初始化结束之后,就会留在QApplication::exec的事件循环中,而不是直接退出,当quit被调用的时候,程序才会结束。不return a.exec()而是直接return 0;的话,我们观察到的现象就是程序初始化出了一个窗口,然后一闪就没了,原因就是程序实例化出了窗口之后走到了return 0;直接就退出了。
通过控制事件循环,我们就可以控制整个程序的的生命周期,比如:

  1. int main(int argc, char *argv[])
  2. {
  3. QApplication a(argc, argv);
  4. Widget w;
  5. w.show();
  6. QTimer::singleShot(2000,&a,SLOT(quit()));
  7. return a.exec();
  8. }

设置QTimer让QAppliciation实例化出的对象的事件循环在2s后退出。我们可以观察到的现象就是程序在2s后,就会自己退出。

嵌套循环

如下代码:

  1. void Widget::on_pushButton_clicked()
  2. {
  3. static int i = 0;
  4. QEventLoop loop;
  5. QTimer::singleShot(2000,&loop,SLOT(quit()));
  6. loop.exec();
  7. qDebug()<<"Loop exit "<<i;
  8. i++;
  9. }

QApplication已经在执行exec在事件循环中了,那这里的事件循环其实就是一个嵌套的循环。so,这里就涉及到了嵌套的循环。
当处在loop.exec()的2s时,点击界面控件我们可以发现,控件还是可以正常响应的。说明:
子层事件循环具有父层事件循环的所有功能,但在执行子层的时间循环时,父层的事件循环会被打断,但是GUI界面还是可以正常响应不会出现卡住的问题。

事件循环的重要性

在main中,我们通过QApplication::exec()开启了事件循环。视窗的管理者发送了鼠标点击事件,该时间被Qt内核捕获,并转换成QMouseEvent,随后通过QApplication::notify()发送到了自己widget的event中,并调用了对应的事件处理器,假设该事件处理会引起一个耗时的操作,程序会怎么样呢?比如一个button的clicked的信号,对应的槽函数中有一个sleep(10)的操作,此时事件循环会怎么样呢?是的,事件循环什么也没做。它一直在等待这个sleep(10)的返回。也就是说sleep(10)阻塞了整个事件循环。那整个程序也就不能交互了。操作系统系统的管理软件检测到你的程序不再处理事件,也会提示用户程序不再响应给用户选择是否关闭。这就是为什么快速的响应事件并尽早返回事件循环这么重要。
耗时的操作,就将其移到另外一个线程中去。或者使用QApplication::processEvents

QProcessEvents

假设有一个1000次的循环,每个循环中,都会消耗掉0.001s,每次的时间消耗其实不多,但当这个循环执行1000次的时候,程序就会卡上1s,1s的时间已经是比较明显的卡顿了。这个时候,要是对多线程编程不熟悉的话,就可以使用QApplication::processEvent()了。
QApplication::processEvent()的功能就是让程序处理那些还没有处理的时间,然后再把使用权返回给调用者。当QApplication::processEvent()加到循环里时,在这1s的耗时操作里,时间会被处理1000次,卡顿也就没有那么明显了。使用这种方式的优点是编程成本低,穿插在频繁的耗时操作里就可以。