tags:

views:

1676

answers:

5

I'm writing an application using Qt4.

I need to download a very short text file from a given http address.

The file is short and is needed for my app to be able to continue, so I would like to make sure the download is blocking (or will timeout after a few seconds if the file in not found/not available).

I wanted to use QHttp::get(), but this is a non-blocking method.

I thought I could use a thread : my app would start it, and wait for it to finish. The thread would handle the download and quit when the file is downloaded or after a timeout.

But I cannot make it work :

class JSHttpGetterThread : public QThread
{
  Q_OBJECT

public:
  JSHttpGetterThread(QObject* pParent = NULL);
  ~JSHttpGetterThread();

  virtual void run()
  {
    m_pHttp = new QHttp(this);
    connect(m_pHttp, SIGNAL(requestFinished(int, bool)), this, SLOT(onRequestFinished(int, bool)));

    m_pHttp->setHost("127.0.0.1");
    m_pHttp->get("Foo.txt", &m_GetBuffer);
    exec();
  }

  const QString& getDownloadedFileContent() const
  {
    return m_DownloadedFileContent;
  }

private:
  QHttp* m_pHttp;

  QBuffer m_GetBuffer;
  QString m_DownloadedFileContent;

private slots:
  void onRequestFinished(int Id, bool Error)
  {
    m_DownloadedFileContent = "";
    m_DownloadedFileContent.append(m_GetBuffer.buffer());
  }
};

In the method creating the thread to initiate the download, here is what I'm doing :

JSHttpGetterThread* pGetter = new JSHttpGetterThread(this);
pGetter->start();
pGetter->wait();

But that doesn't work and my app keeps waiting. It looks lit the slot 'onRequestFinished' is never called.

Any idea ?

Is there a better way to do what I'm trying to do ?

+2  A: 

Hi,

you have to call QThread::quit() or exit() if you are done - otherwise your thread will run forever...

HTH,

Andreas

A: 

How about giving the GUI some amount of time to wait on the thread and then give up.

Something like:

JSHttpGetterThread* pGetter = new JSHttpGetterThread(this);
pGetter->start();
pGetter->wait(10000);  //give the thread 10 seconds to download

Or...

Why does the GUI thread have to wait for the "downloader thread" at all? When the app fires up create the downloader thread, connect the finished() signal to some other object, start the downloader thread, and return. When the thread has finished, it will signal the other object which can resume your process.

Dusty Campbell
+5  A: 

Instead of using a thread you can just go into a loop which calls processEvents

while (notFinished) { qApp->processEvents(QEventLoop::WaitForMore | QEventLoop::ExcludeUserInput); }

Where notFinished is a flag which can be set from the onRequestFinished slot.

The ExcludeUserInput will ensure that GUI related events are ignored while waiting.

David Dibben
+1  A: 

I chose to implement David's solution, which seemed to be the easiest.

However, I had handle a few more things :

  • I had to adapt the QEventLoop enum values for Qt4.3.3 (the version I'm using);
  • I had to track the request Id, to make sure to exit the while loop when the download request is finished, and not when another request is finished;
  • I added a timeout, to make sure to exit the while loop if there is any problem.

Here is the result as (more or less) pseudo-code :

class BlockingDownloader : public QObject
{
  Q_OBJECT
public:
    BlockingDownloaderBlockingDownloader()
    {
      m_pHttp = new QHttp(this);
      connect(m_pHttp, SIGNAL(requestFinished(int, bool)), this, SLOT(onRequestFinished(int, bool)));
    }

    ~BlockingDownloader()
    {
      delete m_pHttp;
    }

    QString getFileContent()
    {
      m_pHttp->setHost("www.xxx.com");
      m_DownloadId = m_pHttp->get("/myfile.txt", &m_GetBuffer);

      QTimer::singleShot(m_TimeOutTime, this, SLOT(onTimeOut()));
      while (!m_FileIsDownloaded)
      {
        qApp->processEvents(QEventLoop::WaitForMoreEvents | QEventLoop::ExcludeUserInputEvents);
      }
      return m_DownloadedFileContent;
    }

private slots:
    void BlockingDownloader::onRequestFinished(int Id, bool Error)
    {
      if (Id == m_DownloadId)
      {
        m_DownloadedFileContent = "";
        m_DownloadedFileContent.append(m_GetBuffer.buffer());
        m_FileIsDownloaded = true;
      }
    }

  void BlockingDownloader::onTimeOut()
  {
    m_FileIsDownloaded = true;
  }

private:
  QHttp* m_pHttp;
  bool m_FileIsDownloaded;
  QBuffer m_GetBuffer;
  QString m_DownloadedFileContent;
  int m_DownloadId;
};
Jérôme
thanks, this helped
Wahnfrieden
+3  A: 

A little late but: Do not use these wait loops, the correct way is to use the done() signal from QHttp.

The requestFinished signal from what I have seen is just for when your application has finished the request, the data may still be on its way down.

You do not need a new thread, just setup the qhttp:

httpGetFile= new QHttp();
connect(httpGetFile, SIGNAL(done(bool)), this, SLOT(processHttpGetFile(bool)));

Also do not forget to flush the file in processHttpGetFile as it might not all be on the disk.

Phil Hannent