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.


  • Moderators

    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.


  • Moderators

    [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).


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.