利用QEventLoop实现同步等待信号槽返回结果
在QT中想要实现一个简单的函数:给定一个网页地址(http://www.hao123.com),等待返回网页的html内容。
当然我想到可利用QNetWork相关的类:QNetworkAccessManager、QNetworkRequest、QNetworkReply三个类;进一步深入发现,网络访问一般是异步的,因此Qt中reply的结果是利用信号/槽机制来进行通知和处理的,这仍然是异步的,也就是说不能立即取得reply结果,而只能在槽函数中来处理reply结果。我也想到用lambda函数实现槽函数,似乎能立即取得结果,但可惜的是lambda函数仍然是异步调用的。
我用一个类来实现该功能。
头文件:webspider.h
1 #include "QtNetwork\QNetworkAccessManager" 2 class WebSpider:public QObject 3 { 4 Q_OBJECT 5 public: 6 WebSpider(); 7 ~WebSpider(); 8 9 QString getHtml(QString urlStr); 10 11 public slots: 12 void requestFinished(QNetworkReply *reply); 13 private: 14 QNetworkAccessManager* _netManager; 15 QNetworkRequest* _netRequest; 16 17 QByteArray _replyResult; 18 };
实现文件:webspider.cpp
1 #include "WebSpider.h" 2 #include "QEventLoop" 3 #include "QtNetwork\QNetworkAccessManager" 4 #include "QtNetwork\QNetworkReply" 5 #include "QObject" 6 7 8 9 WebSpider::WebSpider() 10 :QObject() 11 { 12 _netManager = new QNetworkAccessManager(); 13 14 } 15 16 17 WebSpider::~WebSpider() 18 { 19 delete _netManager; 20 } 21 22 QString WebSpider::getHtml(QString urlStr) 23 { 24 _netRequest = new QNetworkRequest(QUrl(urlStr)); 25 _netManager->get(*_netRequest); 26 27 QEventLoop* el = new QEventLoop(); 28 29 connect(_netManager, &QNetworkAccessManager::finished, el, &QEventLoop::quit); //网络访问结束时,结束事件循环 30 connect(_netManager, &QNetworkAccessManager::finished, this, &WebSpider::requestFinished); //网络访问结束时,处理返回结果 31 el->exec(); //开始事件循环,等待网络访问结束 32 //事件循环结束后,requestFinished槽中已获取网络访问结果,保存于_replyResult中 33 return QString(_replyResult); //返回结果 34 } 35 36 void WebSpider::requestFinished(QNetworkReply *reply) 37 { 38 _replyResult=reply->readAll(); 39 }
在程序中只需要简单二行代码就可取得结果
1 WebSpider ws; 2 QString htmlStr=ws.getHtml("http://www.hao123.com");
就象一个函数一样,结果立即就获得了,没有异步访问的感觉了,呵呵。任务达成。
WebSpider中实际上是异步的,但利用QEventLoop等待异步调用后,再返回结果的,也就是说,有一个等待的过程。这里我们没有考虑网络异常的可能,具体应用中可进一步完善。
在QT中异步无处不在,这个小例子有助于我们实现一些简单的小功能,将异步的代码集中包装,在主程序中代码将显示出简单直接。
进一步,如果槽函数可直接写成lambda函数,则所有过程可简化为一个函数的形式。