Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QNetworkAccessManager finished signal emitted to late



  • I am trying to save a reply in other object which doesn't have anything to do with the downloader class but the finished signal is emitted at the end of the program so I am passing an empty string to the other object. Any ideas how to make response = m_response; work?

    #include "downloader.h"
    
    Downloader::Downloader(QObject *parent) : QObject(parent)
    {
    
    }
    
    void Downloader::doDownload(QUrl &url, QString &response)
    {
        p_manager = new QNetworkAccessManager(this);
    
        connect(p_manager, SIGNAL(finished(QNetworkReply*)),
               this, SLOT(replyFinished(QNetworkReply*)));
    
        p_manager->get(QNetworkRequest(url));
        
        response = m_response;
    }
    
    void Downloader::replyFinished (QNetworkReply *reply)
    {
        if(reply->error())
        {
            m_response = reply->errorString();
        }
        else
        {
            qDebug() << reply->header(QNetworkRequest::ContentTypeHeader).toString();
            qDebug() << reply->header(QNetworkRequest::LastModifiedHeader).toDateTime().toString();;
            qDebug() << reply->header(QNetworkRequest::ContentLengthHeader).toULongLong();
            qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
            qDebug() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
    
            m_response = reply->readAll();
        }
    
        reply->deleteLater();
    }
    

  • Moderators

    @Radu.Amh said in QNetworkAccessManager finished signal emitted to late:

    response = m_response;

    What is response and m_response? You do not set either of them to any value in doDownload().



  • in void Downloader::replyFinished (QNetworkReply *reply) you can find m_response = reply->errorString();
    response is what i want to pass to the other object


  • Lifetime Qt Champion

    Hi
    Why not hook up the other class to finished signal too ?
    I assume there cant be any response to copy before finished has been seen.

    Also you sentence.

    • the finished signal is emitted at the end of the program
      sounds a bit odd. does it means it takes LONG time to download or
      WHY first in the end of the program ?

  • Moderators

    @Radu.Amh said in QNetworkAccessManager finished signal emitted to late:

    response is what i want to pass to the other object

    Connect that object to finished() signal, then.

    Or set the response in the replyFinished() method. Doing it in doDownload() is way too soon.


  • Moderators

    @Radu.Amh said in QNetworkAccessManager finished signal emitted to late:

    void Downloader::doDownload(QUrl &url, QString &response)
    {
    p_manager = new QNetworkAccessManager(this);
    connect(p_manager, SIGNAL(finished(QNetworkReply*)),
           this, SLOT(replyFinished(QNetworkReply*)));
    
    p_manager->get(QNetworkRequest(url));
    
    response = m_response;
    }
    

    hi p_manager->get is not a synchronus function, therefore m_response in the next line will always be an empty string!

    only after the replyFinishedsignal is fired, is m_response not empty anymore and you can pass it along.

    also your p_manager leaks memory!



  • Well, this is what I am trying to do:

    class WebPage
    {
    private:
    QUrl m_url;
    QString m_name;
    QString m_location;
    QString m_response;
    QFile p_file;

    public:
    WebPage(const QString &url, const QString &name, const QString &location);
    WebPage(const WebPage &)=delete;
    void download(Downloader &down);

    };

    void WebPage::download(Downloader &down)
    {
    down.doDownload(m_url, m_response);
    if(p_file.open(QIODevice::ReadWrite))
    {
    QTextStream stream( &p_file );
    stream << m_response;
    p_file.flush();
    p_file.close();
    }
    }

    downloading a webpage but also being able to download another webpage later in the same program.



  • @J.Hilk I understood get is not a synchronous function but I need the reply for the other object, webpage, so how do I make it to deliver it in time?
    @sierdzio how do I connect the object to the finished() signal?
    @mrjj why at the end, I've watched it with the debugger and void Downloader::replyFinished (QNetworkReply *reply) is the last function called, after that deconstructors are called


  • Moderators

    @Radu.Amh
    we would have to see a good part more of your implementation.

    however, this quick and dirty solution should also work. But I recommend rethinking your approach:

    void Downloader::doDownload(QUrl url, QString &response)
    {
        QNetworkAccessManager *manager = new QNetworkAccessManager();
        QString str;
        QNetworkReply *reply = manager->get(QNetworkRequest(url));
        connect(reply, &QNetworkReply::finished, this, [reply, &str]{
            str = reply->readAll();
            reply->deleteLater();
        });
    
    
        QEventLoop loop;
        connect(manager, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
        loop.exec();
    
        manager->deleteLater();
    
        response = str;
    }
    


  • @J.Hilk Actually it is something I had in mind but I didn't know about QEventLoop. Thank you! Until a better solution comes this is the best.


  • Moderators

    @Radu.Amh
    It is a Bad solution
    It forces synchronus behaviour on an asynchronus method

    Better would be a Signal from your download Class that sends the recieved Data to a Slot in your other class
    Signals can have arguments, for example a String, to send Data across


Log in to reply