【问题标题】:Blocking a Qt application during downloading a short file在下载短文件期间阻止 Qt 应用程序
【发布时间】:2008-10-30 16:06:01
【问题描述】:

我正在使用 Qt4 编写应用程序。

我需要从给定的 http 地址下载一个非常短的文本文件。

该文件很短,我的应用程序需要该文件才能继续运行,因此我想确保下载被阻止(如果文件未找到/不可用,几秒钟后将超时)。

我想使用 QHttp::get(),但这是一个非阻塞的方法。

我想我可以使用线程:我的应用程序会启动它,然后等待它完成。当文件被下载或超时后,线程将处理下载并退出。

但我不能让它工作:

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());
  }
};

在创建线程以启动下载的方法中,这是我正在做的事情:

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

但这不起作用,我的应用程序一直在等待。看起来从未调用过插槽“onRequestFinished”。

有什么想法吗?

有没有更好的方法来做我想做的事情?

【问题讨论】:

    标签: qt


    【解决方案1】:

    有点晚了,但是: 不要使用这些等待循环,正确的方法是使用来自 QHttp 的 done() 信号。

    我所看到的 requestFinished 信号仅用于当您的应用程序完成请求时,数据可能仍在下降中。

    您不需要新线程,只需设置 qhttp:

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

    也不要忘记在 processHttpGetFile 中刷新文件,因为它可能并不都在磁盘上。

    【讨论】:

      【解决方案2】:

      您可以直接进入调用processEvents 的循环,而不是使用线程:

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

      其中notFinished 是一个可以从onRequestFinished 插槽设置的标志。

      ExcludeUserInput 将确保在等待时忽略与 GUI 相关的事件。

      【讨论】:

        【解决方案3】:

        如果你完成了,你必须调用 QThread::quit()exit() - 否则你的线程将永远运行......

        【讨论】:

          【解决方案4】:

          我选择实现 David 的解决方案,这似乎是最简单的。

          不过,我还处理了一些事情:

          • 我不得不为 Qt4.3.3(我正在使用的版本)调整 QEventLoop 枚举值;
          • 我必须跟踪请求 ID,以确保在下载请求完成时退出 while 循环,而不是在另一个请求完成时退出;
          • 我添加了超时,以确保在出现任何问题时退出 while 循环。

          这是(或多或少)伪代码的结果:

          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;
          };
          

          【讨论】:

            【解决方案5】:

            出于同样的需要,我使用了 QNetworkAccsessManager。因为这个类管理连接 RFC base(6 个进程同时处理)和非阻塞。

            http://qt-project.org/doc/qt-4.8/qnetworkaccessmanager.html

            【讨论】:

              【解决方案6】:

              给 GUI 一些时间来等待线程然后放弃怎么样。

              类似:

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

              或者……

              为什么 GUI 线程必须等待“下载器线程”?当应用程序启动时,创建下载线程,将 finished() 信号连接到其他对象,启动下载线程,然后返回。当线程完成时,它会通知另一个可以恢复您的进程的对象。

              【讨论】:

                猜你喜欢
                • 2014-01-20
                • 2023-03-03
                • 2019-05-27
                • 1970-01-01
                • 2015-03-17
                • 1970-01-01
                • 2016-01-17
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多