关于Qt的事件循环以及QEventLoop的简单使用
1.一般我们的事件循环都是由exec()来开启的,例如下面的例子:
1 QCoreApplicaton::exec()
2 QApplication::exec()
3 QDialog::exec()
4 QThread::exec()
5 QDrag::exec()
6 QMenu::exec()
这些都开启了事件循环,事件循环首先是一个无限“循环”,程序在exec()里面无限循环,能让跟在exec()后面的代码得不到运行机会,直至程序从exec()跳出。从exec()跳出时,事件循环即被终止。QEventLoop::quit()能够终止事件循环。
事件循环实际上类似于一个事件队列,对列入的事件依次的进行处理,当时间做完而时间循环没有结束的时候,其实际上比较类似于一个不占用CPU事件的for(;;)循环。
其本质实际上是以队列的方式来重新分配时间片。
2.事件循环是可以嵌套的,当在子事件循环中的时候,父事件循环中的事件实际上处于中断状态,当子循环跳出exec之后才可以执行父循环中的事件。当然,这不代表在执行子循环的时候,类似父循环中的界面响应会被中断,因为往往子循环中也会有父循环的大部分事件,执行QMessageBox::exec(),QEventLoop::exec()的时候,虽然这些exec()打断了main()中的QApplication::exec(),但是由于GUI界面的响应已经被包含到子循环中了,所以GUI界面依然能够得到响应。
3.如果某个子事件循环仍然有效,但其父循环被强制跳出,此时父循环不会立即执行跳出,而是等待子事件循环跳出后,父循环才会跳出
举几个例子吧,比如说如果想要将主线程等待100ms,总不能使用sleep吧,那样会导致GUI界面停止响应的,但是用事件循环就可以避免这一点:
1 QEventLoop loop;
2 QTimer::singleShot(100, &loop, SLOT(quit()));
3 loop.exec();
还有,比如说对于一个槽函数,触发之后会弹出一个dialog,但是像下面这样写的话,窗口会一闪而过的:
1 void ****::mySLot{
2 QDialog dlg;
3 dlg.show();
4 }
当然这里可以使用将dlg改成一个静态成员,通过增长期生存期的方法来解决这个问题,但是这里同样可以使用eventLoop来解决这个问题:
void ****::mySLot{
QDialog dlg;
dlg.show();
QEventLoop loop;
connect(&dlg, SIGNAL(finished(int)), &loop, SLOT(quit()));
loop.exec(QEventLoop::ExcludeUserInputEvents);
}
QEventLoop的在http下载中的巧妙使用
在Qt中当我们想通过HTTP下载文件或者图片时,通常会使用QNetWorkAccessManager,QNetworkRequest,QNetworkReply这几个类的方法,通过用信号SIGNAL(finished(QNetworkReply*))与槽SLOT(httpReplyFiished(QNetworkReply*)机制实现了在HTTP文件下载,但是接收和处理文件资源数据是在槽函数中进行的,为此,我们不得不将其分割为两个函数——一个发送资源请求,一个接收和处理资源。
而实际上,在某些特殊的使用情境之下,我们希望文件资源的接收和处理能与图片资源的请求在同一个函数之内进行,这就要求程序能够提供足够的时间来接收数据,并在数据接收完成之后能够及时返回开放的资源接口,以完成下一步的处理操作。
这里,QEventLoop为我们提供了解决的方法。来看官方的解释:
Detailed Description
The QEventLoop class provides a means of entering and leaving an event loop.
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.
翻译:
QEventLoop类为我们提供了一种进入和退出事件循环的方法。
在任何时候,你都可以创建一个QEventLoop实例,并通过调用exec()来启动一个事件循环。在循环期间,主动调用exit()可以使exec()强制返回。
从以上说明可以略知QEventLoop的使用方法,陈列如下:
(1)创建QEventLoop实例;
(2)调用exec()来启动;
(3)通过exit()来使exec()退出并返回退出码。
看起来挺简单的,不是吗?但是问题在于,如何掌握退出的时机呢?对了,就是“信号与槽”!!!
本文的目的在于,接收完数据之后就返回,不是吗?那只要判断数据何时读取完毕就可以解决问题了。因此,采用信号与槽机制来判断数据是否接收完毕恰如其境。代码附上:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QEventLoop loop;
manager = new QNetworkAccessManager(this);
QNetworkRequest request;
request.setUrl(QUrl("http://www.350351.com/templets/default/css/images/logo.png"));
QNetworkReply *reply = manager->get(request);
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));//关键之处1
loop.exec();//关键之处2
QPixmap pix;
QByteArray data = reply->readAll();
pix.loadFromData(data, "PNG");
pix.save("./0001.png", "PNG", 100);
ui->textEdit->append(" src=./0001.png>");
}
参考博客:http://www.cnblogs.com/-wang-cheng/p/4973021.html
http://www.cnblogs.com/-wang-cheng/p/4973021.html