我正在尝试制作一个程序补丁系统,该系统不会阻塞我的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>