QNetworkReply and QProgressBar, GUI freezes. Are there ways to optimize it?
-
Greetings!
I have a project which depends on QNAM for downloading big files. Now, I wanted to show the current progress of the download using QProgressBar. The progress bar is updated using
QNetworkReply::downloadProgress()
. But I am getting a problem since the GUI freezes.Are there ways to optimize it? Or are there other alternatives that can be used?
Thanks -
If the UI is freezing it means that you are blocking the main thread somewhere. It would be easier to help if you shared some code, but for example don't do stuff like this:
while(!reply.isFinished());
QNAM is asynchronous by design so it won't freeze your UI if you don't force it to do so. Here's a simple non-blocking example:
#include <QApplication> #include <QNetworkAccessManager> #include <QNetworkRequest> #include <QNetworkReply> #include <QProgressBar> #include <QUrl> int main(int argc, char *argv[]) { QApplication a(argc, argv); QProgressBar pb; pb.show(); QNetworkAccessManager nam; QNetworkRequest rq(QUrl("http://path.to.the.largest.file.ever")); QNetworkReply* reply = nam.get(rq); a.connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); a.connect(reply, &QNetworkReply::downloadProgress, [&](qint64 received, qint64 total){ pb.setMaximum(total); pb.setValue(received); }); return a.exec(); }
-
Thanks Krzysztof Kawa for the immediate reply. I knew I never used any "blocking" stuff on my codes. I actually created a separate class for the "downloader" class. Here is a part of the implementation (.cpp)
#include "qdownloader.h" QDownloader::QDownloader(QObject *parent) : QObject(parent) { } void QDownloader::download(QUrl url, QProgressBar *progressBar) { q_progressBar = progressBar; QNetworkAccessManager *manager = new QNetworkAccessManager(this); QNetworkRequest request; request.setUrl(url); request.setRawHeader("User-Agent", "ReaQtor 1.0"); QNetworkReply *reply = manager->get(request); QObject::connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(replyProgress(qint64,qint64))); QObject::connect(reply, SIGNAL(readyRead()), this, SLOT(replyReady())); } void QDownloader::replyFinished() { qDebug() << "download finished"; } void QDownloader::replyProgress(qint64 bytesReceived, qint64 bytesTotal) { q_progressBar->setValue(bytesReceived); q_progressBar->setMaximum(bytesTotal); } void QDownloader::replyReady() { QNetworkReply *reply = qobject_cast<QNetworkReply*>(QObject::sender()); if (reply->hasRawHeader("Content-Disposition")) { q_fileName = reply->rawHeader("Content-Disposition"); } else { QFileInfo fileInfo(reply->url().toString()); q_fileName = fileInfo.fileName(); } QFile file(q_fileName); if (file.open(QIODevice::WriteOnly | QIODevice::Append)) { file.write(reply->read(reply->bytesAvailable())); } file.close(); }
However, I am thinking that what causes the freezing was by passing a pointer to the progress bar (or I could be wrong here. I am not really sure).
Another thing, I suspect, was that readyRead signal has very high update "frequency". Do you think this causes freezing? Does setting the "buffer size" will help? thanks in advance.
-
The readyRead signal can get called every few ms so the responding slot should be as fast and lightweight as possible. Your replyReady slot is very "heavy". You can move out almost everything from it:
- you've got the reply object from the get() call, don't cast it from sender every time
- you recheck for file name by processing headers(which can be slow) every time. do it once and remember the result
- you open/close file every time, yikes! IO is extremely heavy, and acquiring file handle especially. This is brutalizing hard drive and performance. Open file once before the download starts and close it once at the end. If that is not an option - buffer the reads and write to file in large chunks so that you don't have to open/close it so often.
- recap of the previous - IO is expensive. Don't write separately every tiny bit you get from readyRead. Read it to a buffer and write it in larger chunks (possibly in another, non-gui thread).
-
Thanks... Apparently, what freezes my GUI was the opening and closing of the file.