Qt DownloadManager在单独的线程块主GUI上

时间:2018-12-19 19:30:22

标签: c++ qt

我正在尝试制作一个程序补丁系统,该系统不会阻塞我的qt应用程序的主GUI。 (这只是和Qt一起玩)。为了测试起见,将PatchManager设置为运行无限循环以确保它不会阻塞。

即使我的PatchManager / DownloadManager被划分为一个新线程(与GUI线程不同),我也不理解为什么我的程序仍会阻塞。

有人可以在这里帮助我吗?这是我的代码...

downloadmanager.h

#ifndef DOWNLOADMANAGER_H
#define DOWNLOADMANAGER_H

#include <QObject>
#include <QtCore>
#include <QtNetwork>

class DownloadManager : public QObject
{
    Q_OBJECT
public:
    explicit DownloadManager(
            QObject* parent = nullptr);

    void connectProgressSlot(
            std::function<void(qint64, qint64)>& progress_slot);

    static bool isHttpRedirect(
            QNetworkReply *reply);

signals:
    void progressChanged(
            const int& current,
            const int& maximum);

public slots:
    void startDownloading();
    void downloadFinished(
            QNetworkReply* reply);
    void sslErrors(
            const QList<QSslError> &errors);

protected:
    QQueue<QString> m_remaining_urls;

private:
    QNetworkAccessManager m_manager;
    std::function<void(qint64, qint64)>* m_progress_slot;
};

#endif // DOWNLOADMANAGER_H

downloadmanager.cpp

#include "downloadmanager.h"

DownloadManager::DownloadManager(
        QObject* parent) :
    QObject(parent),
    m_manager(NULL),
    m_progress_slot(NULL)
{
    connect(&m_manager, SIGNAL(finished(QNetworkReply*)), SLOT(downloadFinished(QNetworkReply*)));
}

bool DownloadManager::isHttpRedirect(
        QNetworkReply *reply)
{
    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    return statusCode == 301 || statusCode == 302 || statusCode == 303
           || statusCode == 305 || statusCode == 307 || statusCode == 308;
}

void DownloadManager::connectProgressSlot(
        std::function<void(qint64, qint64)>& progress_slot)
{
    m_progress_slot = &progress_slot;
}

void DownloadManager::startDownloading()
{
    // Download the file at the top of the queue...
    if(!m_remaining_urls.isEmpty())
    {
        QString download_url = m_remaining_urls.dequeue();

        QNetworkRequest request(download_url);
        QNetworkReply* reply = m_manager.get(request);

        #if QT_CONFIG(ssl)
            connect(reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(sslErrors(QList<QSslError>)));
        #endif

        if(m_progress_slot)
        {
            connect(reply, &QNetworkReply::downloadProgress, *m_progress_slot);
        }
    }
    else {
        // FL: Emit download finished
    }
}

void DownloadManager::downloadFinished(
        QNetworkReply* reply)
{
    QUrl url = reply->url();
    if (reply->error()) {
        fprintf(stderr, "Download of %s failed: %s\n",
                url.toEncoded().constData(),
                qPrintable(reply->errorString()));
    } else {
        if (isHttpRedirect(reply)) {
            fputs("Request was redirected.\n", stderr);
        } else {
            //QString filename = saveFileName(url);
            //if (saveToDisk(filename, reply)) {
                qDebug() << "Download of " << url << "succeeded";
            //}
        }
    }

    reply->deleteLater();

    startDownloading();
}

void DownloadManager::sslErrors(
        const QList<QSslError>& errors)
{
#if QT_CONFIG(ssl)
    for (const QSslError &error : errors)
        fprintf(stderr, "SSL error: %s\n", qPrintable(error.errorString()));
#else
    Q_UNUSED(sslErrors);
#endif
}

补丁管理器.h

#ifndef PATCHMANAGER_H
#define PATCHMANAGER_H

#include "downloadmanager.h"

class PatchManager : public DownloadManager
{
public:
    explicit PatchManager(
            QObject* parent = 0);

    void startPatching();

signals:
    void progressChanged(
            const int& current,
            const int& maximum);

public slots:
    void downloadFinished(
            QNetworkReply* reply);

private:
    QVector<QString> m_files_to_download;
};

#endif // PATCHMANAGER_H

patchmanager.cpp

#include "patchmanager.h"

PatchManager::PatchManager(
        QObject* parent) :
    DownloadManager(parent)
{

}

void PatchManager::startPatching()
{
    // This loop should NOT block because we were placed into a different thread...
    while(true)
    {
    // Just for testing, give sample URLs.
    const QString patch_server_url = "https://www.w3.org/";
    const QList<QString> files_to_patch = {"TR/PNG/iso_8859-1.txt"};

    for(int i = 0; i < files_to_patch.size(); i++)
    {
        m_remaining_urls.enqueue(patch_server_url + files_to_patch[i]);
    }

    startDownloading();
    }
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

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

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

main.cpp

#include "mainwindow.h"
#include <QApplication>

#include "patchmanager.h"

int main(int argc, char *argv[])
{   
    QApplication a(argc, argv);

    MainWindow w;
    w.show();

    PatchManager patcher;

    QThread patch_thread;
    patch_thread.start();

    patcher.moveToThread(&patch_thread);

    patcher.startPatching();

    return a.exec();
}

mainwindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>526</width>
    <height>245</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <widget class="QPushButton" name="pushButton">
    <property name="geometry">
     <rect>
      <x>20</x>
      <y>160</y>
      <width>491</width>
      <height>21</height>
     </rect>
    </property>
    <property name="text">
     <string>PushButton</string>
    </property>
   </widget>
   <widget class="QPlainTextEdit" name="plainTextEdit">
    <property name="geometry">
     <rect>
      <x>20</x>
      <y>10</y>
      <width>491</width>
      <height>101</height>
     </rect>
    </property>
   </widget>
   <widget class="QPlainTextEdit" name="plainTextEdit_2">
    <property name="geometry">
     <rect>
      <x>20</x>
      <y>120</y>
      <width>491</width>
      <height>31</height>
     </rect>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>526</width>
     <height>20</height>
    </rect>
   </property>
  </widget>
  <widget class="QToolBar" name="mainToolBar">
   <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
  </widget>
  <widget class="QStatusBar" name="statusBar"/>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <resources/>
 <connections/>
</ui>

0 个答案:

没有答案