添加多线程下载示例
This commit is contained in:
parent
3aed340f11
commit
7eb9ffa28c
73
MultiplethreadDownload/.gitignore
vendored
Normal file
73
MultiplethreadDownload/.gitignore
vendored
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
# This file is used to ignore files which are generated
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
*~
|
||||||
|
*.autosave
|
||||||
|
*.a
|
||||||
|
*.core
|
||||||
|
*.moc
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.orig
|
||||||
|
*.rej
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*_pch.h.cpp
|
||||||
|
*_resource.rc
|
||||||
|
*.qm
|
||||||
|
.#*
|
||||||
|
*.*#
|
||||||
|
core
|
||||||
|
!core/
|
||||||
|
tags
|
||||||
|
.DS_Store
|
||||||
|
.directory
|
||||||
|
*.debug
|
||||||
|
Makefile*
|
||||||
|
*.prl
|
||||||
|
*.app
|
||||||
|
moc_*.cpp
|
||||||
|
ui_*.h
|
||||||
|
qrc_*.cpp
|
||||||
|
Thumbs.db
|
||||||
|
*.res
|
||||||
|
*.rc
|
||||||
|
/.qmake.cache
|
||||||
|
/.qmake.stash
|
||||||
|
|
||||||
|
# qtcreator generated files
|
||||||
|
*.pro.user*
|
||||||
|
|
||||||
|
# xemacs temporary files
|
||||||
|
*.flc
|
||||||
|
|
||||||
|
# Vim temporary files
|
||||||
|
.*.swp
|
||||||
|
|
||||||
|
# Visual Studio generated files
|
||||||
|
*.ib_pdb_index
|
||||||
|
*.idb
|
||||||
|
*.ilk
|
||||||
|
*.pdb
|
||||||
|
*.sln
|
||||||
|
*.suo
|
||||||
|
*.vcproj
|
||||||
|
*vcproj.*.*.user
|
||||||
|
*.ncb
|
||||||
|
*.sdf
|
||||||
|
*.opensdf
|
||||||
|
*.vcxproj
|
||||||
|
*vcxproj.*
|
||||||
|
|
||||||
|
# MinGW generated files
|
||||||
|
*.Debug
|
||||||
|
*.Release
|
||||||
|
|
||||||
|
# Python byte code
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Binaries
|
||||||
|
# --------
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
|
26
MultiplethreadDownload/MultiplethreadDownload.pro
Normal file
26
MultiplethreadDownload/MultiplethreadDownload.pro
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
QT += core gui network concurrent
|
||||||
|
|
||||||
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
|
CONFIG += c++17
|
||||||
|
|
||||||
|
# You can make your code fail to compile if it uses deprecated APIs.
|
||||||
|
# In order to do so, uncomment the following line.
|
||||||
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
main.cpp \
|
||||||
|
utils.cpp \
|
||||||
|
widget.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
utils.h \
|
||||||
|
widget.h
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
widget.ui
|
||||||
|
|
||||||
|
# Default rules for deployment.
|
||||||
|
qnx: target.path = /tmp/$${TARGET}/bin
|
||||||
|
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||||
|
!isEmpty(target.path): INSTALLS += target
|
13
MultiplethreadDownload/main.cpp
Normal file
13
MultiplethreadDownload/main.cpp
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#include "widget.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QScreen>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
Widget w;
|
||||||
|
w.show();
|
||||||
|
w.move(QApplication::screens().at(0)->geometry().center() - w.frameGeometry().center());
|
||||||
|
return a.exec();
|
||||||
|
}
|
21
MultiplethreadDownload/utils.cpp
Normal file
21
MultiplethreadDownload/utils.cpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#include "utils.h"
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QLocale>
|
||||||
|
|
||||||
|
|
||||||
|
QString Utils::sizeFormat(qint64 bytes)
|
||||||
|
{
|
||||||
|
qreal size = bytes;
|
||||||
|
QStringList list;
|
||||||
|
list << "KB" << "MB" << "GB" << "TB";
|
||||||
|
|
||||||
|
QStringListIterator i(list);
|
||||||
|
QString unit("bytes");
|
||||||
|
|
||||||
|
while (size >= 1024.0 && i.hasNext()) {
|
||||||
|
unit = i.next();
|
||||||
|
size /= 1024.0;
|
||||||
|
}
|
||||||
|
return QString().setNum(size, 'f', 2) + " " + unit;
|
||||||
|
}
|
||||||
|
|
12
MultiplethreadDownload/utils.h
Normal file
12
MultiplethreadDownload/utils.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#ifndef UTILS_H
|
||||||
|
#define UTILS_H
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
class Utils
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static QString sizeFormat(qint64 bytes);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // UTILS_H
|
240
MultiplethreadDownload/widget.cpp
Normal file
240
MultiplethreadDownload/widget.cpp
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
#include "widget.h"
|
||||||
|
#include "ui_widget.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QRegExp>
|
||||||
|
#include <QRegExpValidator>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QtConcurrent/QtConcurrent>
|
||||||
|
|
||||||
|
Widget::Widget(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, ui(new Ui::Widget)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
// 设置URL链接验证器
|
||||||
|
QRegExp regex(R"(http[s]://.+)");
|
||||||
|
auto *validator = new QRegExpValidator(regex, nullptr);
|
||||||
|
ui->urlInput->setValidator(validator);
|
||||||
|
|
||||||
|
// 设置最大的线程并发数
|
||||||
|
int processer_num = QThread::idealThreadCount();
|
||||||
|
processer_num = processer_num > maxThreadNum ? maxThreadNum : processer_num;
|
||||||
|
ui->threadCountSpinbox->setMaximum(processer_num);
|
||||||
|
ui->threadCountSpinbox->setValue(processer_num);
|
||||||
|
|
||||||
|
// 设置保存路径为当前程序运行路径
|
||||||
|
ui->savePathInput->setText(QDir::currentPath());
|
||||||
|
|
||||||
|
requestManager = new QNetworkAccessManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget::~Widget()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 展示错误信息
|
||||||
|
* @param msg
|
||||||
|
*/
|
||||||
|
void Widget::showError(const QString& msg)
|
||||||
|
{
|
||||||
|
auto msgBox = new QMessageBox(this);
|
||||||
|
msgBox->setWindowTitle(tr("请求出错"));
|
||||||
|
msgBox->setIcon(QMessageBox::Critical);
|
||||||
|
msgBox->setText(msg);
|
||||||
|
msgBox->exec();
|
||||||
|
ui->downloadBtn->setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取要下载的文件大小
|
||||||
|
* @param url
|
||||||
|
*/
|
||||||
|
qint64 Widget::getFileSize(const QString& url)
|
||||||
|
{
|
||||||
|
QEventLoop event;
|
||||||
|
QNetworkRequest request;
|
||||||
|
request.setUrl(QUrl(url));
|
||||||
|
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||||
|
QNetworkReply *reply = requestManager->head(request);
|
||||||
|
connect(reply, &QNetworkReply::errorOccurred, [this, reply](QNetworkReply::NetworkError error){
|
||||||
|
if (error != QNetworkReply::NoError) {
|
||||||
|
return showError(reply->errorString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(reply, &QNetworkReply::finished, &event, &QEventLoop::quit);
|
||||||
|
event.exec();
|
||||||
|
qint64 fileSize = 0;
|
||||||
|
if (reply->rawHeader("Accept-Ranges") == QByteArrayLiteral("bytes")
|
||||||
|
&& reply->hasRawHeader(QString("Content-Length").toLocal8Bit())) {
|
||||||
|
fileSize = reply->header(QNetworkRequest::ContentLengthHeader).toUInt();
|
||||||
|
}
|
||||||
|
reply->deleteLater();
|
||||||
|
return fileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 开始下载文件
|
||||||
|
* @param url
|
||||||
|
* @param filename
|
||||||
|
*/
|
||||||
|
void Widget::singleDownload(const QString& url, const QString& filename)
|
||||||
|
{
|
||||||
|
QFile file(filename);
|
||||||
|
if (file.exists())
|
||||||
|
file.remove();
|
||||||
|
if (!file.open(QIODevice::WriteOnly)) {
|
||||||
|
return showError(file.errorString());
|
||||||
|
}
|
||||||
|
|
||||||
|
QEventLoop event;
|
||||||
|
QNetworkAccessManager mgr;
|
||||||
|
QNetworkRequest request;
|
||||||
|
request.setUrl(QUrl(url));
|
||||||
|
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||||
|
QNetworkReply *reply = mgr.get(request);
|
||||||
|
connect(reply, &QNetworkReply::readyRead, this, [&file, reply](){
|
||||||
|
file.write(reply->readAll());
|
||||||
|
});
|
||||||
|
connect(reply, &QNetworkReply::finished, &event, &QEventLoop::quit);
|
||||||
|
connect(reply, &QNetworkReply::errorOccurred, [this, reply](QNetworkReply::NetworkError error){
|
||||||
|
if (error != QNetworkReply::NoError) showError(reply->errorString());
|
||||||
|
});
|
||||||
|
event.exec();
|
||||||
|
file.flush();
|
||||||
|
file.close();
|
||||||
|
ui->downloadBtn->setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 多线程下载
|
||||||
|
* @param url
|
||||||
|
* @param fileSize
|
||||||
|
* @param filename
|
||||||
|
* @param threadCount
|
||||||
|
*/
|
||||||
|
void Widget::multiDownload(const QString &url, qint64 fileSize, const QString &filename, int threadCount)
|
||||||
|
{
|
||||||
|
QFile file(filename);
|
||||||
|
if (file.exists())
|
||||||
|
file.remove();
|
||||||
|
if (!file.open(QIODevice::WriteOnly)) {
|
||||||
|
return showError(file.errorString());
|
||||||
|
}
|
||||||
|
file.resize(fileSize);
|
||||||
|
|
||||||
|
// 任务等分
|
||||||
|
qint64 segmentSize = fileSize / threadCount;
|
||||||
|
QVector<QPair<qint64, qint64>> vec(threadCount);
|
||||||
|
for (int i = 0; i < threadCount; i++) {
|
||||||
|
vec[i].first = i * segmentSize;
|
||||||
|
vec[i].second = i * segmentSize + segmentSize - 1;
|
||||||
|
}
|
||||||
|
vec[threadCount-1].second = fileSize; // 余数部分加入最后一个
|
||||||
|
|
||||||
|
qint64 bytesReceived = 0; // 下载接收的总字节数
|
||||||
|
// 任务队列
|
||||||
|
auto mapCaller = [&, this](const QPair<qint64, qint64>& pair) -> qint64 {
|
||||||
|
QEventLoop event;
|
||||||
|
QNetworkAccessManager mgr;
|
||||||
|
QNetworkRequest request;
|
||||||
|
request.setUrl(url);
|
||||||
|
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||||
|
request.setRawHeader("Range", QString("bytes=%1-%2").arg(pair.first).arg(pair.second).toLocal8Bit());
|
||||||
|
QNetworkReply *reply = mgr.get(request);
|
||||||
|
qint64 writePos = pair.first;
|
||||||
|
connect(reply, &QNetworkReply::readyRead, [&, reply, this](){
|
||||||
|
QByteArray data = reply->readAll();
|
||||||
|
lock.lock();
|
||||||
|
file.peek(writePos);
|
||||||
|
file.write(data);
|
||||||
|
bytesReceived += data.size();
|
||||||
|
lock.unlock();
|
||||||
|
writePos += data.size();
|
||||||
|
});
|
||||||
|
connect(reply, &QNetworkReply::finished, &event, &QEventLoop::quit);
|
||||||
|
event.exec();
|
||||||
|
return writePos - pair.first;
|
||||||
|
};
|
||||||
|
QFuture<void> future = QtConcurrent::map(vec, mapCaller);
|
||||||
|
QFutureWatcher<void> futureWatcher;
|
||||||
|
QEventLoop loop;
|
||||||
|
connect(&futureWatcher, &QFutureWatcher<void>::finished, &loop, &QEventLoop::quit, Qt::QueuedConnection);
|
||||||
|
futureWatcher.setFuture(future);
|
||||||
|
if (!future.isFinished()) {
|
||||||
|
loop.exec();
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
qDebug() << "下载完毕!!";
|
||||||
|
ui->downloadBtn->setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 点击开始下载
|
||||||
|
*/
|
||||||
|
void Widget::on_downloadBtn_clicked()
|
||||||
|
{
|
||||||
|
QString url = ui->urlInput->text().trimmed();
|
||||||
|
if (url.isEmpty()) {
|
||||||
|
return showError(tr("请求链接不允许为空!"));
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择保存路径
|
||||||
|
QString savePath = ui->savePathInput->text().trimmed();
|
||||||
|
if (savePath.isEmpty()) {
|
||||||
|
savePath = QFileDialog::getExistingDirectory();
|
||||||
|
if (savePath.isEmpty()) return;
|
||||||
|
} else {
|
||||||
|
if(!QDir(savePath).exists()) {
|
||||||
|
return showError(tr("路径不存在!"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 fileSize = getFileSize(url);
|
||||||
|
QString sizeText = fileSize == 0 ? "未知大小" : Utils::sizeFormat(fileSize);
|
||||||
|
ui->filesizeLabel->setText(sizeText);
|
||||||
|
ui->downloadBtn->setEnabled(false);
|
||||||
|
|
||||||
|
int process_num = ui->threadCountSpinbox->text().toInt();
|
||||||
|
|
||||||
|
QDir::setCurrent(savePath);
|
||||||
|
QString filename = QFileInfo(url).fileName();
|
||||||
|
if (fileSize == 0 || process_num == 1) {
|
||||||
|
singleDownload(url, filename);
|
||||||
|
} else {
|
||||||
|
multiDownload(url, fileSize, filename, process_num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置保存下载文件的路径
|
||||||
|
*/
|
||||||
|
void Widget::on_brwoserPathBtn_clicked()
|
||||||
|
{
|
||||||
|
QString savePath = QFileDialog::getExistingDirectory();
|
||||||
|
if (!savePath.isEmpty()) {
|
||||||
|
ui->savePathInput->setText(savePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Widget::download_progress_change(qint64 bytesReceived, qint64 bytesTotal)
|
||||||
|
{
|
||||||
|
if (bytesTotal <= 0)
|
||||||
|
return;
|
||||||
|
ui->downProgressBar->setMaximum(10000); // 最大值
|
||||||
|
ui->downProgressBar->setValue((bytesReceived * 10000) / bytesTotal); // 当前值
|
||||||
|
ui->downProgressBar->setTextVisible(true);
|
||||||
|
}
|
39
MultiplethreadDownload/widget.h
Normal file
39
MultiplethreadDownload/widget.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#ifndef WIDGET_H
|
||||||
|
#define WIDGET_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QMutex>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
namespace Ui { class Widget; }
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
class Widget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
Widget(QWidget *parent = nullptr);
|
||||||
|
~Widget();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void showError(const QString& msg);
|
||||||
|
qint64 getFileSize(const QString& url);
|
||||||
|
void singleDownload(const QString& url, const QString& filename);
|
||||||
|
void multiDownload(const QString& url, qint64 fileSize, const QString& filename, int threadCount);
|
||||||
|
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void on_downloadBtn_clicked();
|
||||||
|
void on_brwoserPathBtn_clicked();
|
||||||
|
void download_progress_change(qint64 bytesReceived, qint64 bytesTotal);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QNetworkAccessManager *requestManager;
|
||||||
|
const int maxThreadNum = 8;
|
||||||
|
QMutex lock;
|
||||||
|
|
||||||
|
Ui::Widget *ui;
|
||||||
|
};
|
||||||
|
#endif // WIDGET_H
|
157
MultiplethreadDownload/widget.ui
Normal file
157
MultiplethreadDownload/widget.ui
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Widget</class>
|
||||||
|
<widget class="QWidget" name="Widget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>635</width>
|
||||||
|
<height>209</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>209</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>多线程下载文件Demo</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<property name="sizeConstraint">
|
||||||
|
<enum>QLayout::SetDefaultConstraint</enum>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>URL:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="urlInput"/>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QPushButton" name="downloadBtn">
|
||||||
|
<property name="text">
|
||||||
|
<string>下载</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>保存路径:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="savePathInput"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QPushButton" name="brwoserPathBtn">
|
||||||
|
<property name="text">
|
||||||
|
<string>浏览</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>原始线程数:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QSpinBox" name="threadCountSpinbox">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="2">
|
||||||
|
<widget class="QPushButton" name="pauseBtn">
|
||||||
|
<property name="text">
|
||||||
|
<string>暂停</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>下载进度:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QProgressBar" name="downProgressBar">
|
||||||
|
<property name="value">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="textVisible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="textDirection">
|
||||||
|
<enum>QProgressBar::TopToBottom</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="2">
|
||||||
|
<widget class="QCheckBox" name="dwnFinishedOpenChbox">
|
||||||
|
<property name="text">
|
||||||
|
<string>下载完成是否打开</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>下载速度:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="downloadSpeedLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>文件大小:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="filesizeLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
@ -9,3 +9,11 @@
|
|||||||
|
|
||||||
![](./QProxyStyleTest/screenshot.png)
|
![](./QProxyStyleTest/screenshot.png)
|
||||||
|
|
||||||
|
## 多线程下载
|
||||||
|
[MultiplethreadDownload](./MultiplethreadDownload/)
|
||||||
|
|
||||||
|
测试视频下载地址 https://www.learningcontainer.com/wp-content/uploads/2020/05/sample-mp4-file.mp4
|
||||||
|
下完的文件好像是有问题的。。。。请帮忙看一下
|
||||||
|
|
||||||
|
参考内容
|
||||||
|
* CoverEars(迅雷不及掩耳,Qt版多线程下载器) https://github.com/xj361685640/CoverEars-Qt
|
Loading…
Reference in New Issue
Block a user