Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. [Solved] Cannot download using QNetworkAccessManager
Forum Updated to NodeBB v4.3 + New Features

[Solved] Cannot download using QNetworkAccessManager

Scheduled Pinned Locked Moved General and Desktop
14 Posts 2 Posters 3.4k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • I Offline
    I Offline
    igorland
    wrote on last edited by
    #1

    Hello. QNetworkAccessManager proved to be a pretty tough task. I have been fighting it for more than a week already... The following code is not working. It does not give any errors but it does not load the file either.

    Downloader.h
    [code]
    #ifndef DOWNLOADER_H
    #define DOWNLOADER_H

    #include <QObject>
    #include <QNetworkAccessManager>
    #include <QNetworkRequest>
    #include <QNetworkReply>
    #include <QUrl>
    #include <QDateTime>
    #include <QFile>
    #include <QDebug>

    class Downloader : public QObject
    {
    Q_OBJECT
    public:
    explicit Downloader(QObject *parent = 0);
    void doDownload();
    QString getData();

    signals:

    public slots:
    void replyFinished (QNetworkReply *reply);

    private:
    QNetworkAccessManager *manager;
    QByteArray data;
    QString dataFinal;
    };

    #endif // DOWNLOADER_H
    [/code]

    Downloader.cpp

    [code]
    #include "Downloader.h"

    Downloader::Downloader(QObject *parent) :
    QObject(parent)
    {
    }

    void Downloader::doDownload()
    {
    manager = new QNetworkAccessManager(this);
    connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
    manager->get(QNetworkRequest(QUrl ("ftp://someftp.com/somefile.TXT")));
    }

    void Downloader::replyFinished (QNetworkReply *reply)
    {
    if(reply->error())
    {
    qDebug() << "ERROR!";
    qDebug() << reply->errorString();
    }
    else
    {
    data = reply->readAll();
    QString dataStr(data);
    dataFinal = dataStr;
    reply->deleteLater();
    }
    }

    QString Downloader::getData()
    {
    return dataFinal;
    }
    [/code]

    mainWindow.cpp

    [code]
    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include "Downloader.h"

    MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
    ui->setupUi(this);
    }

    MainWindow::~MainWindow()
    {
    delete ui;
    }

    void MainWindow::on_pushButton_clicked()
    {
    Downloader d;
    d.doDownload();

    QString metarData;
    metarData = d.getData();
    ui->plainTextEdit->setPlainText(metarData);
    

    }
    [/code]

    Thank you so much in advance!

    Igor

    1 Reply Last reply
    0
    • sierdzioS Offline
      sierdzioS Offline
      sierdzio
      Moderators
      wrote on last edited by
      #2

      You are reading the data too soon. QNAM is asynchronous. You need to wait for the reply to come, before you can read it.

      (Z(:^

      1 Reply Last reply
      0
      • I Offline
        I Offline
        igorland
        wrote on last edited by
        #3

        Hm, thanks! Too bad the examples that I have seen omit that. I will search for the solution.
        Again, thanks a lot!

        EDIT:
        So I have found that I need to have
        [code]
        QEventLoop loop;
        loop.exec();
        [/code]

        Can you prompt please where exactly it goes in the code?

        Thanks!

        1 Reply Last reply
        0
        • sierdzioS Offline
          sierdzioS Offline
          sierdzio
          Moderators
          wrote on last edited by
          #4

          They don't. You are simply doing it wrong in your MainWindow. Downloader class is done correctly.

          (Z(:^

          1 Reply Last reply
          0
          • I Offline
            I Offline
            igorland
            wrote on last edited by
            #5

            sierdzio. Thank you again. Can you please elaborate what exactly I am doing wrong? Cheers!

            1 Reply Last reply
            0
            • sierdzioS Offline
              sierdzioS Offline
              sierdzio
              Moderators
              wrote on last edited by
              #6

              @
              Downloader d;
              d.doDownload();

              QString metarData;
              // This is WAY too soon!
              metarData = d.getData();
              @

              instead of getting the data in the very same method, you need to wait until the reply comes. Either use a forever loop (ugly solution!), or, just like you are doing in Downloader: respond to replyFinished().

              (Z(:^

              1 Reply Last reply
              0
              • I Offline
                I Offline
                igorland
                wrote on last edited by
                #7

                sierdzio,

                Sorry, I am trying to understand what you mean by "like you are doing in Downloader: respond to replyFinished()."

                I have been searching more on this and another suggestion that I have come across with was to put the d object on the heap. I changed it to:

                [code]
                Downloader* d = new Downloader;
                d->doDownload();

                QString metarData;
                metarData = d->getData();
                ui->plainTextEdit->setPlainText(metarData);
                

                [/code]

                Still no luck... Sorry for being so n00b.. ))

                1 Reply Last reply
                0
                • sierdzioS Offline
                  sierdzioS Offline
                  sierdzio
                  Moderators
                  wrote on last edited by
                  #8

                  When the button is clicked, only send the request (doDownload). Then getData in a slot connected to finished(). You need to refactor the code bit. Here is a small helper:
                  @
                  // Downloader
                  signals:
                  void downloadFinished(Downloader *d);
                  ...
                  void Downloader::replyFinished (QNetworkReply *reply) {
                  ...
                  emit downloadFinished(this);
                  }

                  // MainWindow
                  void MainWindow::on_pushButton_clicked()
                  {
                  Downloader *d = new Downloader(this);
                  connect (d, SIGNAL(downloadFinished(Downloader *)), this, SLOT(someSlot(Downloader *)));
                  d->doDownload();
                  ...
                  }
                  ...
                  void MainWindow::someSlot(Downloader *down)
                  {
                  QString metarData;
                  metarData = down->getData();
                  // Optional:
                  down->deleteLater();
                  }
                  @

                  (Z(:^

                  1 Reply Last reply
                  0
                  • I Offline
                    I Offline
                    igorland
                    wrote on last edited by
                    #9

                    sierdzio. You do belong to the Hall of Fame for your support and knowledge. I am still trying to nail it down. This is what I have now:

                    [code]
                    #include "Downloader.h"

                    Downloader::Downloader(QObject *parent) :
                    QObject(parent)
                    {
                    }

                    void Downloader::doDownload()
                    {
                    manager = new QNetworkAccessManager(this);
                    connect(manager, SIGNAL(finished(QNetworkReply*)),this, SLOT(replyFinished(QNetworkReply*)));
                    manager->get(QNetworkRequest(QUrl("ftp://ftp.com/ftp.TXT")));

                    }

                    void Downloader::replyFinished (QNetworkReply *reply)
                    {
                    if(reply->error())
                    {
                    qDebug() << "ERROR!";
                    qDebug() << reply->errorString();
                    }
                    else
                    {
                    dataByte = reply->readAll();
                    emit downloadFinished(this);
                    reply->deleteLater();
                    }
                    }

                    void Downloader::downloadFinished(Downloader *dfinished)
                    {
                    QString dataStr(dataByte);
                    dataFinal = dataStr;
                    dfinished->deleteLater();
                    }

                    QString Downloader::getData()
                    {
                    return dataFinal;
                    }

                    [/code]

                    MainWindow.cpp
                    [code]
                    #include "mainwindow.h"
                    #include "ui_mainwindow.h"
                    #include "Downloader.h"

                    MainWindow::MainWindow(QWidget *parent) :
                    QMainWindow(parent),
                    ui(new Ui::MainWindow)
                    {
                    ui->setupUi(this);
                    }

                    MainWindow::~MainWindow()
                    {
                    delete ui;
                    }

                    void MainWindow::on_pushButton_clicked()
                    {
                    d = new Downloader(this); // declared in header
                    connect(d, SIGNAL(downloadFinished(Downloader *)), this, SLOT(someSlot(Downloader *)));
                    d->doDownload();

                    ui->plainTextEdit->setPlainText(metarData);
                    

                    }

                    void MainWindow::someSlot(Downloader* down)
                    {
                    metarData = d->getData();
                    down->deleteLater();
                    }
                    [/code]

                    Now, I get a weird error saying that
                    [code]
                    multiple definition of Downloader::downloadFInished(Downloader*)
                    first define here
                    [/code]

                    Weird, because I only have it defined in Downloader.h
                    [code]
                    #ifndef DOWNLOADER_H
                    #define DOWNLOADER_H

                    #include <QObject>
                    #include <QNetworkAccessManager>
                    #include <QNetworkRequest>
                    #include <QNetworkReply>
                    #include <QUrl>
                    #include <QDateTime>
                    #include <QFile>
                    #include <QDebug>
                    #include <QEventLoop>

                    class Downloader : public QObject
                    {
                    Q_OBJECT
                    public:
                    explicit Downloader(QObject *parent = 0);
                    void doDownload();
                    QString getData();

                    signals:
                    void downloadFinished(Downloader *dfinished);

                    public slots:
                    void replyFinished (QNetworkReply *reply);

                    private:
                    QNetworkAccessManager *manager;
                    QByteArray dataByte;
                    QString dataFinal;
                    };

                    #endif // DOWNLOADER_H
                    [/code]

                    I love Qt, but it is so discouraging to spend many days on something that took me a day to figure out how to do it in libcurl and Java.

                    sierdzio. Thank you again!

                    1 Reply Last reply
                    0
                    • sierdzioS Offline
                      sierdzioS Offline
                      sierdzio
                      Moderators
                      wrote on last edited by
                      #10

                      If you want easy, go with QHttp or QFtp ;) QNAM is powerful, but also more complicated. The asynchronous paradigm is much better, but also more complicated and harder to get.

                      In your case, you've hit a small detail that some people new to Qt do stumble upon: a signal is not a method, only the slots need implementation. So, you should delete all this:
                      @
                      // Delete!
                      void Downloader::downloadFinished(Downloader *dfinished)
                      {
                      QString dataStr(dataByte);
                      dataFinal = dataStr;
                      dfinished->deleteLater();
                      }
                      @

                      And only leave this line in the header file:
                      @
                      signals:
                      void downloadFinished(Downloader *dfinished);
                      @

                      (Z(:^

                      1 Reply Last reply
                      0
                      • I Offline
                        I Offline
                        igorland
                        wrote on last edited by
                        #11

                        sierdzio. You are the MAN! Thank you. Now the file is getting downloaded. I see it in qDebug. One problem still remains though due the asynchronous process, I believe. The thing is that when in the following code:

                        [code]
                        void MainWindow::on_pushButton_clicked()
                        {
                        d = new Downloader(this);
                        d->doDownload();
                        connect(d, SIGNAL(downloadFinished(Downloader *)), this, SLOT(someSlot(Downloader *)));

                        qDebug() << "MetarData: " <&lt; metarData;
                        ui-&gt;plainTextEdit->setPlainText(metarData);
                        

                        }

                        void MainWindow::someSlot(Downloader* down)
                        {
                        metarData = d->getData();
                        down->deleteLater();
                        }
                        [/code]

                        when I do this:
                        [code]
                        ui->plainTextEdit->setPlainText(metarData);
                        [/code]

                        it does not do anything. Apparently, it does not wait for the file to be first downloaded and goes straight to setPlainText because

                        [code]
                        qDebug() << "MetarData: " << metarData
                        [/code]

                        returns an empty string. In my main application, where metarData is further passed to other functions, I even get a CTD.
                        I thought that connect should take care of this and the code would wait for the full data to be downloaded first.

                        Thanks!

                        1 Reply Last reply
                        0
                        • I Offline
                          I Offline
                          igorland
                          wrote on last edited by
                          #12

                          On the other hand, I can move it to the someSlot function and let it be there. Anyways, thanks a lot for your great help!

                          1 Reply Last reply
                          0
                          • sierdzioS Offline
                            sierdzioS Offline
                            sierdzio
                            Moderators
                            wrote on last edited by
                            #13

                            You assign the variable at the wrong time. Do this instead:
                            @
                            void MainWindow::someSlot(Downloader* down)
                            {
                            metarData = d->getData();
                            ui->plainTextEdit->setPlainText(metarData);
                            down->deleteLater();
                            }
                            @

                            (Z(:^

                            1 Reply Last reply
                            0
                            • I Offline
                              I Offline
                              igorland
                              wrote on last edited by
                              #14

                              Yes. Thanks a lot. Very much appreciated.

                              1 Reply Last reply
                              0

                              • Login

                              • Login or register to search.
                              • First post
                                Last post
                              0
                              • Categories
                              • Recent
                              • Tags
                              • Popular
                              • Users
                              • Groups
                              • Search
                              • Get Qt Extensions
                              • Unsolved