代码之家  ›  专栏  ›  技术社区  ›  Jérôme

在下载短文件时阻止qt应用程序

qt
  •  8
  • Jérôme  · 技术社区  · 16 年前

    我正在用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”。

    有什么想法吗?

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

    6 回复  |  直到 10 年前
        1
  •  4
  •   Jason Plank dvancouver    13 年前

    而不是使用线程,您只需进入一个调用 processEvents :

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

    在哪里? notFinished 是一个可以从 onRequestFinished 狭槽。

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

        2
  •  5
  •   Phil Hannent    16 年前

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

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

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

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

    另外,不要忘记刷新processhttpgetfile中的文件,因为它可能并不都在磁盘上。

        3
  •  3
  •   Jason Plank dvancouver    13 年前

    你得打电话 QThread::quit() exit() 如果你完成了-否则你的线程将永远运行…

        4
  •  1
  •   Jérôme    16 年前

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

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

    • 我必须为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
  •  0
  •   myd0    10 年前

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

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

        6
  •  -1
  •   Dusty Campbell    16 年前

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

    类似:

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

    或者…

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