Doing multiple QNetworkRequest on the same QNetworkAccessManager
-
I have a QStringList with some urls to text files (Changelogs) on the internet. (Github RAWs from gist, if interesed). Each version has it's struct (Inside that struct i have QString to place the changelog).
I have no problem requesting the changelogs, but i don't know how to assign them to their specific Version struct.@ struct VersionInfo {
QString versionID;
QString updateVersionTo;
QString updateDate;
QString operatingSystems;
QString changelog;
QString downloadLink;
};
@That's the struct of each VersionInfo. And i use this code to request:
@ m_manager = new QNetworkAccessManager(this);
QStringList m_urllist; m_urllist << "Link 1" << "Link 2"; for(int counter = 0; counter < m_urllist.length(); counter++) { QNetworkRequest m_request; m_request.setRawHeader("User-Agent", "Autoupdater"); m_request.setUrl(QUrl(m_urllist[counter])); m_manager->get(m_request); }@
-
Hello,
QNetworkAccessManager is an asynchronous API, which means if you want to do multiple calls, you'll have to connect them to multiple slots. Or calling 1 by 1 the requests, and connect them to the same slot where you would manage them.But I don't know if calling 2 times the same slot will call 2 difference slot and not the same one.
Moreover, I noticed that you're using non-pointer QNetworkRequest, which means it will disappear and be removed from the known variables, so I think the SLOT wouldn't be called if maybe the SLOT corresponding to "counter==0" is called DURING "counter==1" for example...
Sorry if it's not clear, if you need precision, I will ;)
-
[quote author="Max13" date="1356829581"]Hello,
QNetworkAccessManager is an asynchronous API, which means if you want to do multiple calls, you'll have to connect them to multiple slots. Or calling 1 by 1 the requests, and connect them to the same slot where you would manage them.But I don't know if calling 2 times the same slot will call 2 difference slot and not the same one.
Moreover, I noticed that you're using non-pointer QNetworkRequest, which means it will disappear and be removed from the known variables, so I think the SLOT wouldn't be called if maybe the SLOT corresponding to "counter==0" is called DURING "counter==1" for example...
Sorry if it's not clear, if you need precision, I will ;)[/quote]
Now that i think about it, i have the same question. o.O
My problem is that i can't identify each QNetworkReply with each VersionInfo.
-
One solution is to create one QObject to handle each VersionInfo + QNetworkReply pair, e.g.
@
for(int counter = 0; counter < m_urllist.length(); counter++) {
QNetworkRequest m_request;
m_request.setRawHeader("User-Agent", "Autoupdater");
m_request.setUrl(QUrl(m_urllist[counter]));
QNetworkReply *reply = m_manager->get(m_request);// Store the reply, and connect the QNetworkReply::finished() signal // an internal slot that creates a VersionInfo based on the URL and the reply ChangelogProcessor *processor = new ChangelogProcessor(m_urllist[counter], reply); }
@
(Just be careful of memory leaks: You need to delete the ChangelogProcessor AND the QNetworkReply when you're done!)
-
@ void Updater::requestChangelogDownload()
{
QNetworkRequest m_request;
m_request.setRawHeader("User-Agent", "Autoupdater");
for(int counter = 0; counter < m_versionList.size(); counter++) {
m_request.setUrl(QUrl(m_versionList[counter].changelog));
m_changelogRequest.insert(m_versionList[counter].versionID, m_manager->get(m_request));
qDebug() << "A";
connect(m_changelogRequest.value(m_versionList[counter].versionID), SIGNAL(finished()), this, SLOT(changelogDownloaded()));
}
}void Updater::changelogDownloaded()
{
qDebug() << sender();
QNetworkReply a = (QNetworkReply) sender();
qDebug() << a->isFinished();
qDebug() << a->readAll();
}
@I can say, not a pretty code. FOr some reason it keeps printing "A" even though m_versionList.size() is 2.
Still it doesn't work, a->readAll() returns nothing. But i think i'm doing it poorly. -
[quote author="Blastcore" date="1356846308"]
@
QNetworkReply a = (QNetworkReply) sender();
@
[/quote]Try a qobject_cast<> instead of a C-style cast
-
Nope, it didn't work. :/ I wonder what's going on o.O
sender() is a QNetworkReplyImpl. Both are different.
-
Using readyRead() signal from QNetworkReply fixed the issue.
-
Be aware that readyRead() only indicates that data is available, not necessarily that the reply has finished.
If you want to pass information, for example a reference to the <code>VersionInfo</code> which was used to initiate the request you can use an "attribute":http://qt-project.org/doc/qt-5.0/qtnetwork/qnetworkrequest.html#setAttribute.
QNetworkAccessManager provides a "finished":http://qt-project.org/doc/qt-5.0/qtnetwork/qnetworkaccessmanager.html#finished signal which passes the QNetworkReply.
@
struct VersionInfo
{
...
};Q_DECLARE_METATYPE(VersionInfo*)
static const QNetworkRequest::Attribute VersionInfoAttribute = QNetworkRequest::User;
...
connect(m_manager, &QNetworkAccessManager::finished, [](QNetworkReply reply)
{
VersionInfo versionInfo = reply->request().attribute(VersionInfoAttribute)
.value<VersionInfo*>();
if (versionInfo != nullptr)
{
versionInfo->...
}reply->deleteLater();
});
...
for (int counter = 0; counter < m_versionList.size(); counter++)
{
QNetworkRequest request;request.setAttribute(VersionInfoAttribute, QVariant::fromValue<VersionInfo*>(&m_versionList[counter])); request.setRawHeader("User-Agent", "Autoupdater"); request.setUrl(m_versionList[counter].changelog); m_manager->get(request);
}
@
If you don't want to connect to the finished(QNetworkReply*) signal (for instance because you are reusing the QNetworkAccessManager object and don't want to receive finished signals from other requests) you can use sender() (QNetworkReplyImpl is-a QNetworkReply).