//Downloader.h
#pragma once
#pragma execution_character_set("utf-8")
#include <QCoreApplication>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QSslError>
#include <QTimer>
#include <QUrl>
#include <QVector>
enum DownloadResult {
DR_SUCCESS,
DR_FAILURE,
DR_ERROR,
DR_TIMEOUT,
};
/*下载器类*/
class Downloader : public QObject
{
Q_OBJECT
public:
/*构造*/
Downloader();
/*析构*/
~Downloader();
/*获取最终错误*/
const QString& getLastError();
/*获取文件大小*/
const qint64 getFileSize(const QUrl& url, int times = 0);
/*下载*/
void download(const QUrl& url);
/*设置下载超时*/
void setTimeout(const int& timeout);
/*设置保存路径*/
void setSavePath(const QString& savePath);
/*获取应答*/
QNetworkReply* getReply();
/*所用时间,单位MS*/
const ulong getElapsedTime();
/*平均网速,单位KB*/
const float getAverageSpeed();
/*文件大小,单位MB*/
const float getFileSize();
signals:
/*结果信号*/
void resultSignal(const DownloadResult& result, const QString& error);
/*进度信号*/
void progressSignal(const qint64& receive, const qint64& total, const float& speed);
public slots:
/*完成槽*/
void finishedSlot(QNetworkReply* reply);
/*SSL错误槽*/
void sslErrorsSlot(const QList<QSslError>& errors);
/*进度槽*/
void progressSlot(qint64 recvBytes, qint64 totalBytes);
/*超时槽*/
void timeoutSlot();
protected:
/*设置最终错误*/
void setLastError(const QString& error);
/*保存文件名*/
const QString saveFileName(const QUrl& url);
/*保存到磁盘*/
bool saveToDisk(const QString& filename, QIODevice* data);
private:
QString m_lastError = "No Error";
QNetworkAccessManager m_manager;
QNetworkReply* m_reply = nullptr;
QTimer m_timer;
int m_timeout = 120000;
ulong m_startTime = 0;
ulong m_endTime = 0;
qint64 m_recvBytes = 0;
qint64 m_fileSize = 0;
QVector<float> m_speedV;
QString m_savePath = "Downloader";
};
/*应答超时类*/
class QReplyTimeout : public QObject
{
Q_OBJECT
public:
QReplyTimeout(QNetworkReply* reply, const int& timeout) : QObject(reply)
{
if (reply && reply->isRunning())
{
QTimer::singleShot(timeout, this, &QReplyTimeout::onTimeout);
}
}
private slots:
void onTimeout()
{
QNetworkReply* reply = static_cast<QNetworkReply*>(parent());
if (reply->isRunning() && reply->size() == 0)
{
reply->abort();
reply->deleteLater();
emit timeout();
}
}
signals:
void timeout();
};
//Downloader.cpp
#include "Downloader.h"
#include <windows.h>
Downloader::Downloader()
{
connect(&m_manager, &QNetworkAccessManager::finished, this, &Downloader::finishedSlot);
connect(&m_timer, &QTimer::timeout, this, &Downloader::timeoutSlot);
}
Downloader::~Downloader()
{
}
const QString& Downloader::getLastError()
{
return m_lastError;
}
const qint64 Downloader::getFileSize(const QUrl& url, int times)
{
qint64 size = -1;
do
{
QNetworkAccessManager manager;
QEventLoop loop;
QTimer timer;
QNetworkReply* reply = manager.head(QNetworkRequest(url));
if (!reply) continue;
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
timer.start(3000);
loop.exec();
if (reply->error() != QNetworkReply::NoError)
{
setLastError(reply->errorString());
continue;
}
QVariant var = reply->header(QNetworkRequest::ContentLengthHeader);
size = var.toLongLong();
reply->deleteLater();
break;
} while (times-- > 0);
return size;
}
void Downloader::download(const QUrl& url)
{
m_recvBytes = 0;
m_speedV.clear();
QNetworkRequest request(url);
QNetworkReply* reply = m_manager.get(request);
m_reply = reply;
connect(reply, &QNetworkReply::downloadProgress, this, &Downloader::progressSlot);
connect(reply, &QNetworkReply::sslErrors, this, &Downloader::sslErrorsSlot);
connect(new QReplyTimeout(reply, 3000), &QReplyTimeout::timeout, this, [&]() {emit resultSignal(DR_TIMEOUT, "连接超时"); });
m_startTime = GetTickCount();
}
void Downloader::setTimeout(const int& timeout)
{
m_timeout = timeout;
}
void Downloader::setSavePath(const QString& savePath)
{
m_savePath = savePath;
}
QNetworkReply* Downloader::getReply()
{
return m_reply;
}
const ulong Downloader::getElapsedTime()
{
return m_endTime;
}
const float Downloader::getAverageSpeed()
{
float sum = 0.0f;
for (auto& x : m_speedV)
sum += x;
return sum / m_speedV.size();
}
const float Downloader::getFileSize()
{
return (float)m_fileSize / 1024.0f / 1024.0f;
}
void Downloader::sslErrorsSlot(const QList<QSslError>& sslErrors)
{
foreach(const QSslError & error, sslErrors)
setLastError(error.errorString());
}
void Downloader::progressSlot(qint64 recvBytes, qint64 totalBytes)
{
m_fileSize = totalBytes;
float speed = (recvBytes - m_recvBytes) / 1024.0 * 8;
m_speedV.push_back(speed);
emit progressSignal(recvBytes, totalBytes, speed);
m_recvBytes = recvBytes;
}
void Downloader::timeoutSlot()
{
}
void Downloader::setLastError(const QString& error)
{
#ifdef QT_DEBUG
qDebug() << error;
#endif
m_lastError = error;
}
const QString Downloader::saveFileName(const QUrl& url)
{
QString path = url.path();
QString basename = QFileInfo(path).fileName();
if (basename.isEmpty()) basename = "NoNameFile";
QString savePath = QString(".\\%1\\").arg(m_savePath);
if (!QDir(savePath).exists())
{
QDir dir;
if (!dir.mkpath(savePath))
{
setLastError("创建下载目录失败");
return basename;
}
}
if (QFile::exists(savePath + basename))
{
int i = 0;
basename += '.';
while (QFile::exists(savePath + basename + QString::number(i))) ++i;
basename += QString::number(i);
}
return basename;
}
bool Downloader::saveToDisk(const QString& filename, QIODevice* data)
{
bool result = false;
do
{
QString savePath = QString(".\\%1\\").arg(m_savePath);
if (!QDir(savePath).exists())
{
QDir dir;
if (!dir.mkpath(savePath))
{
setLastError("创建下载目录失败");
break;
}
}
QFile file(savePath + filename);
if (!file.open(QIODevice::WriteOnly))
{
setLastError(file.errorString());
break;
}
file.write(data->readAll());
file.close();
result = true;
} while (false);
return result;
}
void Downloader::finishedSlot(QNetworkReply* reply)
{
m_endTime = GetTickCount() - m_startTime;
QUrl url = reply->url();
if (reply->error())
{
emit resultSignal(DR_ERROR, reply->errorString());
}
else
{
if (saveToDisk(saveFileName(url), reply))
emit resultSignal(DR_SUCCESS, "下载成功");
else
emit resultSignal(DR_FAILURE, "下载失败," + getLastError());
}
reply->deleteLater();
}
//DownloadDlg.ui,我的Qt版本5.7.0
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DownloadDlg</class>
<widget class="QDialog" name="DownloadDlg">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>492</width>
<height>124</height>
</rect>
</property>
<property name="windowTitle">
<string>DownloadDlg</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="titleLabel">
<property name="font">
<font>
<family>华文行楷</family>
<pointsize>20</pointsize>
<weight>50</weight>
<italic>false</italic>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>正在下载更新程序</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>文件大小/已下载:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="sizeLabel">
<property name="text">
<string>00.00MB/00.00MB</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="urlLabel">
<property name="text">
<string>http://download.osgeo.org/libtiff/old/tiff-3.7.0beta.zip</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="speedLabel">
<property name="text">
<string>0.00Mb/s</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>0</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="textDirection">
<enum>QProgressBar::TopToBottom</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="abortBtn">
<property name="text">
<string>终止</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>
//DownloadDlg.h
#pragma once
#pragma execution_character_set("utf-8")
#include <QCoreApplication>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QSslError>
#include <QTimer>
#include <QUrl>
#include <QVector>
enum DownloadResult {
DR_SUCCESS,
DR_FAILURE,
DR_ERROR,
DR_TIMEOUT,
};
/*下载器类*/
class Downloader : public QObject
{
Q_OBJECT
public:
/*构造*/
Downloader();
/*析构*/
~Downloader();
/*获取最终错误*/
const QString& getLastError();
/*获取文件大小*/
const qint64 getFileSize(const QUrl& url, int times = 0);
/*下载*/
void download(const QUrl& url);
/*设置下载超时*/
void setTimeout(const int& timeout);
/*设置保存路径*/
void setSavePath(const QString& savePath);
/*获取应答*/
QNetworkReply* getReply();
/*所用时间,单位MS*/
const ulong getElapsedTime();
/*平均网速,单位KB*/
const float getAverageSpeed();
/*文件大小,单位MB*/
const float getFileSize();
signals:
/*结果信号*/
void resultSignal(const DownloadResult& result, const QString& error);
/*进度信号*/
void progressSignal(const qint64& receive, const qint64& total, const float& speed);
public slots:
/*完成槽*/
void finishedSlot(QNetworkReply* reply);
/*SSL错误槽*/
void sslErrorsSlot(const QList<QSslError>& errors);
/*进度槽*/
void progressSlot(qint64 recvBytes, qint64 totalBytes);
/*超时槽*/
void timeoutSlot();
protected:
/*设置最终错误*/
void setLastError(const QString& error);
/*保存文件名*/
const QString saveFileName(const QUrl& url);
/*保存到磁盘*/
bool saveToDisk(const QString& filename, QIODevice* data);
private:
QString m_lastError = "No Error";
QNetworkAccessManager m_manager;
QNetworkReply* m_reply = nullptr;
QTimer m_timer;
int m_timeout = 120000;
ulong m_startTime = 0;
ulong m_endTime = 0;
qint64 m_recvBytes = 0;
qint64 m_fileSize = 0;
QVector<float> m_speedV;
QString m_savePath = "Downloader";
};
/*应答超时类*/
class QReplyTimeout : public QObject
{
Q_OBJECT
public:
QReplyTimeout(QNetworkReply* reply, const int& timeout) : QObject(reply)
{
if (reply && reply->isRunning())
{
QTimer::singleShot(timeout, this, &QReplyTimeout::onTimeout);
}
}
private slots:
void onTimeout()
{
QNetworkReply* reply = static_cast<QNetworkReply*>(parent());
if (reply->isRunning() && reply->size() == 0)
{
reply->abort();
reply->deleteLater();
emit timeout();
}
}
signals:
void timeout();
};
//DownloadDlg.cpp
#include "DownloadDlg.h"
#include "Detection.h"
DownloadDlg::DownloadDlg(QWidget *parent)
: QDialog(parent)
{
ui.setupUi(this);
installEventFilter(this);
Misc::ThemeFactory::setBorderRadius(this);
connect(ui.abortBtn, &QPushButton::clicked, this, [&]() {m_manager->getReply()->abort(); });
resetNetwork();
setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
}
DownloadDlg::~DownloadDlg()
{
SAFE_DELETE(m_manager);
}
void DownloadDlg::download(const QString& title, const QString& url, const QString& path)
{
ui.progressBar->setValue(0);
ui.titleLabel->setText(title);
ui.urlLabel->setText(url);
m_manager->setSavePath(path);
m_manager->download(url);
}
bool DownloadDlg::getResult()
{
return m_result == DR_SUCCESS;
}
const ulong DownloadDlg::getElapsedTime()
{
return m_manager->getElapsedTime();
}
const float DownloadDlg::getAverageSpeed()
{
return m_manager->getAverageSpeed();
}
const float DownloadDlg::getFileSize()
{
return m_manager->getFileSize();
}
const QString& DownloadDlg::getLastError()
{
return m_lastError;
}
bool DownloadDlg::resetNetwork()
{
//Network access is disabled.Qt的BUG
SAFE_DELETE(m_manager);
m_manager = NO_THROW_NEW Downloader;
connect(m_manager, &Downloader::progressSignal, this, &DownloadDlg::progressSlot);
connect(m_manager, &Downloader::resultSignal, this, &DownloadDlg::resultSlot);
return m_manager != nullptr;
}
void DownloadDlg::setLastError(const QString& error)
{
m_lastError = error;
}
void DownloadDlg::mousePressEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton)
{
m_isPress = true;
m_point = event->globalPos();
}
}
void DownloadDlg::mouseReleaseEvent(QMouseEvent* event)
{
if (m_isPress)
{
m_isPress = false;
}
}
void DownloadDlg::mouseMoveEvent(QMouseEvent* event)
{
if (m_isPress)
{
int x = event->globalX() - m_point.x();
int y = event->globalY() - m_point.y();
m_point = event->globalPos();
move(this->x() + x, this->y() + y);
}
}
bool DownloadDlg::eventFilter(QObject* obj, QEvent* event)
{
if (event->type() == QEvent::KeyPress)
{
if (reinterpret_cast<QKeyEvent*>(event)->key() == Qt::Key_Escape)
{
return true;
}
}
return QObject::eventFilter(obj, event);
}
void DownloadDlg::resultSlot(const DownloadResult& result, const QString& error)
{
setLastError(error);
m_result = result;
this->close();
}
void DownloadDlg::progressSlot(const qint64& receive, const qint64& total, const float& speed)
{
ui.sizeLabel->setText(QString().sprintf("%.2fMB/%.2fMB", (float)total / 1024 / 1024,
(float)receive / 1024 / 1024));
ui.speedLabel->setText(QString().sprintf("%4.2fKb/s", speed));
ui.progressBar->setValue(((float)receive / total) * 100);
}
注意点:
宏定义SAFE_DELETE原型
#define SAFE_DELETE(X)\
if (X)\
{\
delete X;\
X=nullpter;\
}
Misc::ThemeFactory::setBorderRadius(this);函数原型为头文件自己去百度找吧,不贴了.
class ThemeFactory {
public:
/*构造*/
inline ThemeFactory() {}
/*析构*/
inline ~ThemeFactory() {}
/*获取主题列表*/
inline static const QStringList getThemeList()
{
return QStyleFactory::keys();
}
/*随机选择一个主题*/
inline static void randomTheme()
{
setTheme(getThemeList().value(qrand() % getThemeList().size()));
}
/*设置主题*/
inline static void setTheme(const QString& theme = QString("Fusion"))
{
qApp->setStyle(QStyleFactory::create(theme));
QPalette palette;
palette.setColor(QPalette::Window, QColor(53, 53, 53));
palette.setColor(QPalette::WindowText, Qt::white);
palette.setColor(QPalette::Base, QColor(15, 15, 15));
palette.setColor(QPalette::AlternateBase, QColor(53, 53, 53));
palette.setColor(QPalette::ToolTipBase, Qt::white);
palette.setColor(QPalette::ToolTipText, Qt::white);
palette.setColor(QPalette::Text, Qt::white);
palette.setColor(QPalette::Button, QColor(53, 53, 53));
palette.setColor(QPalette::ButtonText, Qt::white);
palette.setColor(QPalette::BrightText, Qt::red);
palette.setColor(QPalette::Highlight, QColor(142, 45, 197).lighter());
palette.setColor(QPalette::HighlightedText, Qt::black);
qApp->setPalette(palette);
}
/*设置边框为圆角*/
inline static void setBorderRadius(QWidget* widget)
{
QBitmap bmp(widget->size());
bmp.fill();
QPainter p(&bmp);
p.setPen(Qt::NoPen);
p.setBrush(Qt::black);
p.drawRoundedRect(bmp.rect(), 20, 20);
widget->setMask(bmp);
}
};
最终效果展示: