当前位置: 首页 > news >正文

开源 C++ QT QML 开发(十五)通讯--http下载

           文章的目的为了记录使用QT QML开发学习的经历。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。

 相关链接:

开源 C++ QT QML 开发(一)基本介绍

开源 C++ QT QML 开发(二)工程结构

开源 C++ QT QML 开发(三)常用控件

开源 C++ QT QML 开发(四)复杂控件--Listview

开源 C++ QT QML 开发(五)复杂控件--Gridview

开源 C++ QT QML 开发(六)自定义控件--波形图

开源 C++ QT QML 开发(七)自定义控件--仪表盘

推荐链接:

开源 C# 快速开发(一)基础知识

开源 C# 快速开发(二)基础控件

开源 C# 快速开发(三)复杂控件

开源 C# 快速开发(四)自定义控件--波形图

开源 C# 快速开发(五)自定义控件--仪表盘

开源 C# 快速开发(六)自定义控件--圆环

开源 C# 快速开发(七)通讯--串口

开源 C# 快速开发(八)通讯--Tcp服务器端

开源 C# 快速开发(九)通讯--Tcp客户端

开源 C# 快速开发(十)通讯--http客户端

开源 C# 快速开发(十一)线程

开源 C# 快速开发(十二)进程监控

开源 C# 快速开发(十三)进程--管道通讯

开源 C# 快速开发(十四)进程--内存映射

开源 C# 快速开发(十五)进程--windows消息

开源 C# 快速开发(十六)数据库--sqlserver增删改查

本章节主要内容是:仪表盘实现了一个汽车速度表风格自定义控件,参数可设置,数据可实时更新。

1.代码分析

2.所有源码

3.效果演示

一、代码分析C++ 后端详细分析
1. 构造函数与析构函数
 

// 默认构造函数
DownloadTool::DownloadTool(QObject* parent): QObject(parent)  // 调用基类构造函数
{// 简单的初始化,成员变量使用默认值
}// 参数化构造函数
DownloadTool::DownloadTool(const QString& downloadUrl, const QString& savePath, QObject* parent): QObject(parent)
{m_downloadUrl = downloadUrl;  // 直接赋值,不触发信号m_savePath = savePath;        // 直接赋值,不触发信号
}// 析构函数
DownloadTool::~DownloadTool() 
{if (reply) {reply->deleteLater();  // 安全删除网络回复对象// 使用 deleteLater() 而不是 delete,避免在事件处理过程中删除}// file 智能指针会自动释放文件资源
}


2. 属性访问器函数
 

QString DownloadTool::downloadUrl() const
{return m_downloadUrl;  // 返回当前下载URL
}void DownloadTool::setDownloadUrl(const QString& url)
{if (m_downloadUrl != url) {  // 只有值改变时才更新m_downloadUrl = url;emit downloadUrlChanged();  // 通知QML属性已改变}
}QString DownloadTool::savePath() const
{return m_savePath;  // 返回当前保存路径
}void DownloadTool::setSavePath(const QString& path)
{if (m_savePath != path) {  // 值改变检查m_savePath = path;emit savePathChanged();  // 通知QML属性已改变}
}bool DownloadTool::downloading() const
{// 检查下载状态:有回复对象且正在运行return reply && reply->isRunning();
}


3. 核心下载逻辑函数
getDefaultDownloadPath()
 

QString DownloadTool::getDefaultDownloadPath() const
{// 使用 QCoreApplication 获取应用程序目录// 避免依赖 QApplication (QtWidgets模块)return QCoreApplication::applicationDirPath() + "/downloads";// 示例结果: "C:/Program Files/MyApp/downloads"
}


startDownload() - 主下载入口
 

void DownloadTool::startDownload()
{// 1. 状态检查 - 防止重复下载if (downloading()) {return;  // 如果正在下载,直接返回}// 2. URL验证和解析const QUrl newUrl = QUrl::fromUserInput(m_downloadUrl);// QUrl::fromUserInput() 能处理各种格式的URL输入if (!newUrl.isValid()) {// URL无效,发出错误信号
#ifdef DOWNLOAD_DEBUGqDebug() << QString("Invalid URL: %1: %2").arg(m_downloadUrl, newUrl.errorString());
#endifemit sigDownloadError("Invalid URL: " + newUrl.errorString());return;}// 3. 文件名处理QString fileName = newUrl.fileName();  // 从URL提取文件名if (fileName.isEmpty()) {fileName = defaultFileName;  // 使用默认文件名 "download.file"}// 4. 保存路径处理if (m_savePath.isEmpty()) {m_savePath = getDefaultDownloadPath();  // 使用默认路径}// 5. 创建目录if (!QFileInfo(m_savePath).isDir()) {QDir dir;dir.mkpath(m_savePath);  // 递归创建目录}// 6. 构建完整文件路径fileName.prepend(m_savePath + '/');  // 在前面添加路径// 示例: "downloads/file.zip"// 7. 处理已存在文件if (QFile::exists(fileName)) {QFile::remove(fileName);  // 删除已存在的文件}// 8. 创建文件file = openFileForWrite(fileName);if (!file) {// 文件创建失败emit sigDownloadError("Unable to create file for writing");return;}// 9. 开始网络请求startRequest(newUrl);// 10. 通知状态改变emit downloadingChanged();  // downloading() 现在返回 true
}


openFileForWrite() - 文件创建
 

std::unique_ptr<QFile> DownloadTool::openFileForWrite(const QString& fileName)
{// 创建 unique_ptr 管理的 QFile 对象std::unique_ptr<QFile> file(new QFile(fileName));// 以只写方式打开文件if (!file->open(QIODevice::WriteOnly)) {// 文件打开失败
#ifdef DOWNLOAD_DEBUGqDebug() << QString("Unable to save the file %1: %2.").arg(QDir::toNativeSeparators(fileName), file->errorString());
#endifreturn nullptr;  // 返回空指针表示失败}return file;  // 返回文件对象
}


startRequest() - 网络请求发起
 

void DownloadTool::startRequest(const QUrl& requestedUrl)
{url = requestedUrl;           // 保存当前URLhttpRequestAborted = false;   // 重置取消标志// 发起GET请求reply = qnam.get(QNetworkRequest(url));// 连接信号槽 - 关键的网络事件处理connect(reply, &QNetworkReply::finished, this, &DownloadTool::httpFinished);          // 请求完成connect(reply, &QIODevice::readyRead, this, &DownloadTool::httpReadyRead);         // 数据可读connect(reply, &QNetworkReply::downloadProgress, this, &DownloadTool::networkReplyProgress);  // 下载进度#ifdef DOWNLOAD_DEBUGqDebug() << QString("Downloading %1...").arg(url.toString());
#endif
}


4. 网络事件处理函数
httpReadyRead() - 数据接收
 

void DownloadTool::httpReadyRead()
{// 当有数据可读时,立即写入文件if (file) {file->write(reply->readAll());  // 读取所有可用数据并写入文件}// 这种实时写入方式:// 优点:内存占用小,适合大文件// 缺点:频繁的磁盘IO操作
}


networkReplyProgress() - 进度更新
 

void DownloadTool::networkReplyProgress(qint64 bytesRead, qint64 totalBytes)
{// 计算进度百分比,避免除零错误qreal progress = totalBytes > 0 ? qreal(bytesRead) / qreal(totalBytes) : 0;// 发出进度信号emit sigProgress(bytesRead, totalBytes, progress);#ifdef DOWNLOAD_DEBUG// 调试信息:进度百分比和MB显示qDebug() << QString::number(progress * 100, 'f', 2) << "%    "<< bytesRead / (1024 * 1024) << "MB" << "/" << totalBytes / (1024 * 1024) << "MB";
#endif
}


httpFinished() - 请求完成处理
 

void DownloadTool::httpFinished()
{// 1. 通知状态改变emit downloadingChanged();  // downloading() 现在返回 false// 2. 文件信息保存和清理QFileInfo fi;if (file) {fi.setFile(file->fileName());  // 保存文件信息file->close();                 // 关闭文件file.reset();                  // 释放文件指针}// 3. 检查是否用户取消if (httpRequestAborted) {return;  // 如果是用户取消,直接返回}// 4. 检查网络错误if (reply->error()) {// 删除不完整的文件QFile::remove(fi.absoluteFilePath());QString errorString = QString("Download failed: %1.").arg(reply->errorString());
#ifdef DOWNLOAD_DEBUGqDebug() << errorString;
#endifemit sigDownloadError(errorString);return;}// 5. 处理HTTP重定向const QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);if (!redirectionTarget.isNull()) {// 构建重定向URLconst QUrl redirectedUrl = url.resolved(redirectionTarget.toUrl());// 重新打开文件(同一个文件)file = openFileForWrite(fi.absoluteFilePath());if (!file) {emit sigDownloadError("Unable to create file for redirection");return;}// 重新发起请求到重定向URLstartRequest(redirectedUrl);return;  // 注意这里的return,重定向时不会发出完成信号}// 6. 清理网络回复对象reply->deleteLater();  // 安全删除reply = nullptr;       // 置空指针// 7. 发出下载完成信号emit sigDownloadFinished();#ifdef DOWNLOAD_DEBUGqDebug() << QString("Downloaded %1 bytes to %2 in %3").arg(fi.size()).arg(fi.fileName(), QDir::toNativeSeparators(fi.absolutePath()));qDebug() << "Finished";
#endif
}


cancelDownload() - 取消下载
 

void DownloadTool::cancelDownload()
{// 1. 状态检查if (!downloading()) {return;  // 如果不在下载状态,直接返回}// 2. 设置取消标志httpRequestAborted = true;// 3. 中止网络请求reply->abort();  // 这会触发 reply->error() 和 httpFinished()// 4. 通知状态改变emit downloadingChanged();
}


QML 前端函数详细分析
1. 工具函数
formatBytes() - 字节格式化
 

function formatBytes(bytes) {if (bytes === 0) return "0 B";  // 处理零值const k = 1024;  // 使用1024进制const sizes = ['B', 'KB', 'MB', 'GB'];  // 单位数组// 计算单位索引:log1024(bytes)const i = Math.floor(Math.log(bytes) / Math.log(k));// 格式化显示:值保留2位小数 + 单位return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}


// 示例:
// formatBytes(1024) → "1.00 KB"
// formatBytes(1536) → "1.50 KB"
// formatBytes(1048576) → "1.00 MB"
updateButtonStates() - 按钮状态管理
qml
function updateButtonStates() {
    // 下载按钮:有URL且不在下载状态时可用
    downloadButton.enabled = urlTextField.text !== "" && !downloadTool.downloading
    
    // 取消按钮:只在下载状态时可用
    cancelButton.enabled = downloadTool.downloading
    
    // 暂停按钮:预留功能
    pauseResumeButton.enabled = downloadTool.downloading
}
2. 信号处理函数
进度信号处理
 

onSigProgress: {// 1. 更新进度条progressBar.value = progress// 2. 更新进度文本progressText.text = formatBytes(bytesRead) + " / " + formatBytes(totalBytes) +" (" + (progress * 100).toFixed(1) + "%)"// 3. 计算下载速度(每秒)if (speedTimer.running) {var currentTime = new Date().getTime()  // 当前时间戳(毫秒)var timeDiff = (currentTime - speedTimer.lastTime) / 1000  // 转换为秒if (timeDiff > 0) {var bytesDiff = bytesRead - speedTimer.lastBytes  // 字节差var speed = bytesDiff / timeDiff  // 速度(字节/秒)speedText.text = "Speed: " + formatBytes(speed) + "/s"}// 更新计时器状态speedTimer.lastBytes = bytesReadspeedTimer.lastTime = currentTime}
}


状态信号处理
 

// 下载完成
onSigDownloadFinished: {statusText.text = "✓ 下载完成!"statusText.color = successColor  // 绿色updateButtonStates()progressBar.Material.accent = successColor  // 进度条变绿色speedText.text = ""  // 清空速度显示
}// 下载错误
onSigDownloadError: {statusText.text = "✗ Error: " + errorMessagestatusText.color = errorColor  // 红色updateButtonStates()progressBar.value = 0  // 重置进度条speedText.text = ""  // 清空速度显示
}// 下载状态改变
onDownloadingChanged: {if (downloading) {// 开始下载statusText.text = "⏳ 下载中..."statusText.color = primaryColor  // 蓝色progressBar.Material.accent = primaryColorspeedTimer.start()  // 启动速度计时器} else {// 下载停止speedTimer.stop()  // 停止速度计时器}updateButtonStates()
}


3. 用户交互函数
下载按钮点击
 

onClicked: {progressBar.value = 0  // 重置进度条progressText.text = "开始下载..."  // 更新进度文本// 重置速度计时器状态speedTimer.lastBytes = 0speedTimer.lastTime = new Date().getTime()// 调用C++下载方法downloadTool.startDownload()
}


取消按钮点击
 

onClicked: {// 调用C++取消方法downloadTool.cancelDownload()// 更新UI状态statusText.text = "下载取消"statusText.color = warningColor  // 橙色
}

二、所有源码

DownloadTool.h文件源码

#ifndef DOWNLOADTOOL_H
#define DOWNLOADTOOL_H#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QFile>
#include <QDir>
#include <QUrl>
#include <QFileInfo>
#include <memory>// 可以取消注释下面的宏来启用调试输出
// #define DOWNLOAD_DEBUGclass DownloadTool : public QObject
{Q_OBJECTQ_PROPERTY(QString downloadUrl READ downloadUrl WRITE setDownloadUrl NOTIFY downloadUrlChanged)Q_PROPERTY(QString savePath READ savePath WRITE setSavePath NOTIFY savePathChanged)Q_PROPERTY(bool downloading READ downloading NOTIFY downloadingChanged)public:explicit DownloadTool(QObject* parent = nullptr);DownloadTool(const QString& downloadUrl, const QString& savePath, QObject* parent = nullptr);~DownloadTool();QString downloadUrl() const;void setDownloadUrl(const QString& url);QString savePath() const;void setSavePath(const QString& path);bool downloading() const;Q_INVOKABLE void startDownload();Q_INVOKABLE void cancelDownload();signals:void downloadUrlChanged();void savePathChanged();void downloadingChanged();void sigProgress(qint64 bytesRead, qint64 totalBytes, qreal progress);void sigDownloadFinished();void sigDownloadError(const QString& errorMessage);private slots:void httpFinished();void httpReadyRead();void networkReplyProgress(qint64 bytesRead, qint64 totalBytes);private:void startRequest(const QUrl& requestedUrl);std::unique_ptr<QFile> openFileForWrite(const QString& fileName);QString getDefaultDownloadPath() const;private:QString m_downloadUrl;QString m_savePath;QNetworkAccessManager qnam;QNetworkReply* reply = nullptr;std::unique_ptr<QFile> file;QUrl url;bool httpRequestAborted = false;const QString defaultFileName = "download.file";
};#endif // DOWNLOADTOOL_H

DownloadTool.cpp文件源码

#include "DownloadTool.h"
#include <QCoreApplication>
#include <QDebug>DownloadTool::DownloadTool(QObject* parent): QObject(parent)
{
}DownloadTool::DownloadTool(const QString& downloadUrl, const QString& savePath, QObject* parent): QObject(parent)
{m_downloadUrl = downloadUrl;m_savePath = savePath;
}DownloadTool::~DownloadTool()
{if (reply) {reply->deleteLater();}
}QString DownloadTool::downloadUrl() const
{return m_downloadUrl;
}void DownloadTool::setDownloadUrl(const QString& url)
{if (m_downloadUrl != url) {m_downloadUrl = url;emit downloadUrlChanged();}
}QString DownloadTool::savePath() const
{return m_savePath;
}void DownloadTool::setSavePath(const QString& path)
{if (m_savePath != path) {m_savePath = path;emit savePathChanged();}
}bool DownloadTool::downloading() const
{return reply && reply->isRunning();
}QString DownloadTool::getDefaultDownloadPath() const
{// 使用 QCoreApplication 而不是 QApplicationreturn QCoreApplication::applicationDirPath() + "/downloads";
}void DownloadTool::startDownload()
{if (downloading()) {return;}const QUrl newUrl = QUrl::fromUserInput(m_downloadUrl);if (!newUrl.isValid()) {
#ifdef DOWNLOAD_DEBUGqDebug() << QString("Invalid URL: %1: %2").arg(m_downloadUrl, newUrl.errorString());
#endif // DOWNLOAD_DEBUGemit sigDownloadError("Invalid URL: " + newUrl.errorString());return;}QString fileName = newUrl.fileName();if (fileName.isEmpty()) fileName = defaultFileName;if (m_savePath.isEmpty()) {m_savePath = getDefaultDownloadPath();}if (!QFileInfo(m_savePath).isDir()) {QDir dir;dir.mkpath(m_savePath);}fileName.prepend(m_savePath + '/');if (QFile::exists(fileName)) {QFile::remove(fileName);}file = openFileForWrite(fileName);if (!file) {emit sigDownloadError("Unable to create file for writing");return;}startRequest(newUrl);emit downloadingChanged();
}void DownloadTool::cancelDownload()
{if (!downloading()) {return;}httpRequestAborted = true;reply->abort();emit downloadingChanged();
}void DownloadTool::httpFinished()
{emit downloadingChanged();QFileInfo fi;if (file) {fi.setFile(file->fileName());file->close();file.reset();}if (httpRequestAborted) {return;}if (reply->error()) {QFile::remove(fi.absoluteFilePath());QString errorString = QString("Download failed: %1.").arg(reply->errorString());
#ifdef DOWNLOAD_DEBUGqDebug() << errorString;
#endif // DOWNLOAD_DEBUGemit sigDownloadError(errorString);return;}const QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);if (!redirectionTarget.isNull()) {const QUrl redirectedUrl = url.resolved(redirectionTarget.toUrl());file = openFileForWrite(fi.absoluteFilePath());if (!file) {emit sigDownloadError("Unable to create file for redirection");return;}startRequest(redirectedUrl);return;}reply->deleteLater();reply = nullptr;emit sigDownloadFinished();#ifdef DOWNLOAD_DEBUGqDebug() << QString("Downloaded %1 bytes to %2 in %3").arg(fi.size()).arg(fi.fileName(), QDir::toNativeSeparators(fi.absolutePath()));qDebug() << "Finished";
#endif // DOWNLOAD_DEBUG
}void DownloadTool::httpReadyRead()
{if (file) file->write(reply->readAll());
}void DownloadTool::networkReplyProgress(qint64 bytesRead, qint64 totalBytes)
{qreal progress = totalBytes > 0 ? qreal(bytesRead) / qreal(totalBytes) : 0;emit sigProgress(bytesRead, totalBytes, progress);#ifdef DOWNLOAD_DEBUGqDebug() << QString::number(progress * 100, 'f', 2) << "%    "<< bytesRead / (1024 * 1024) << "MB" << "/" << totalBytes / (1024 * 1024) << "MB";
#endif // DOWNLOAD_DEBUG
}void DownloadTool::startRequest(const QUrl& requestedUrl)
{url = requestedUrl;httpRequestAborted = false;reply = qnam.get(QNetworkRequest(url));connect(reply, &QNetworkReply::finished, this, &DownloadTool::httpFinished);connect(reply, &QIODevice::readyRead, this, &DownloadTool::httpReadyRead);connect(reply, &QNetworkReply::downloadProgress, this, &DownloadTool::networkReplyProgress);#ifdef DOWNLOAD_DEBUGqDebug() << QString("Downloading %1...").arg(url.toString());
#endif // DOWNLOAD_DEBUG
}std::unique_ptr<QFile> DownloadTool::openFileForWrite(const QString& fileName)
{std::unique_ptr<QFile> file(new QFile(fileName));if (!file->open(QIODevice::WriteOnly)) {
#ifdef DOWNLOAD_DEBUGqDebug() << QString("Unable to save the file %1: %2.").arg(QDir::toNativeSeparators(fileName), file->errorString());
#endif // DOWNLOAD_DEBUGreturn nullptr;}return file;
}

main.qml文件源码

import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls.Material 2.12
import DownloadTool 1.0ApplicationWindow {id: windowwidth: 700height: 550minimumWidth: 600minimumHeight: 450title: "HTTP 文件下载器"visible: true// 颜色定义property color primaryColor: "#2196F3"property color successColor: "#4CAF50"property color errorColor: "#F44336"property color warningColor: "#FF9800"property color textColor: "#333333"property color lightGray: "#F5F5F5"property color borderColor: "#E0E0E0"Material.theme: Material.LightMaterial.accent: primaryColorDownloadTool {id: downloadTooldownloadUrl: urlTextField.textsavePath: pathTextField.textonSigProgress: {progressBar.value = progressprogressText.text = formatBytes(bytesRead) + " / " + formatBytes(totalBytes) +" (" + (progress * 100).toFixed(1) + "%)"// 计算下载速度if (speedTimer.running) {var currentTime = new Date().getTime()var timeDiff = (currentTime - speedTimer.lastTime) / 1000 // 转换为秒if (timeDiff > 0) {var bytesDiff = bytesRead - speedTimer.lastBytesvar speed = bytesDiff / timeDiffspeedText.text = "Speed: " + formatBytes(speed) + "/s"}speedTimer.lastBytes = bytesReadspeedTimer.lastTime = currentTime}}onSigDownloadFinished: {statusText.text = "✓ 下载完成!"statusText.color = successColorupdateButtonStates()progressBar.Material.accent = successColorspeedText.text = ""}onSigDownloadError: {statusText.text = "✗ Error: " + errorMessagestatusText.color = errorColorupdateButtonStates()progressBar.value = 0speedText.text = ""}onDownloadingChanged: {if (downloading) {statusText.text = "⏳ 下载中..."statusText.color = primaryColorprogressBar.Material.accent = primaryColorspeedTimer.start()} else {speedTimer.stop()}updateButtonStates()}}Timer {id: speedTimerinterval: 1000repeat: truerunning: falseproperty real lastBytes: 0property real lastTime: new Date().getTime()onTriggered: {// 定时更新速度显示}}function formatBytes(bytes) {if (bytes === 0) return "0 B";const k = 1024;const sizes = ['B', 'KB', 'MB', 'GB'];const i = Math.floor(Math.log(bytes) / Math.log(k));return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];}function updateButtonStates() {downloadButton.enabled = urlTextField.text !== "" && !downloadTool.downloadingcancelButton.enabled = downloadTool.downloadingpauseResumeButton.enabled = downloadTool.downloading}Rectangle {anchors.fill: parentcolor: lightGray}ColumnLayout {anchors.fill: parentanchors.margins: 25spacing: 20// 标题区域Rectangle {Layout.fillWidth: trueLayout.preferredHeight: 80color: primaryColorradius: 12RowLayout {anchors.fill: parentanchors.margins: 20Label {text: "📥"font.pixelSize: 32Layout.alignment: Qt.AlignVCenter}ColumnLayout {Layout.fillWidth: truespacing: 4Label {text: "http 文件下载器"font.pixelSize: 24font.bold: truecolor: "white"Layout.fillWidth: true}Label {text: "下载文件"font.pixelSize: 14color: "white"opacity: 0.9Layout.fillWidth: true}}}}// 下载设置卡片Rectangle {Layout.fillWidth: trueLayout.preferredHeight: 140color: "white"radius: 12border.color: borderColorborder.width: 1ColumnLayout {anchors.fill: parentanchors.margins: 20spacing: 15Label {text: "下载设置"font.pixelSize: 16font.bold: truecolor: textColorLayout.fillWidth: true}ColumnLayout {Layout.fillWidth: truespacing: 12RowLayout {Layout.fillWidth: truespacing: 10Label {text: "🔗"font.pixelSize: 16Layout.preferredWidth: 30}TextField {id: urlTextFieldLayout.fillWidth: trueplaceholderText: "https://dl.360safe.com/pclianmeng/n/3__3112523__3f7372633d6c6d266c733d6e33366163663466393961__68616f2e3336302e636e__0cd2.exe"font.pixelSize: 14selectByMouse: trueonTextChanged: updateButtonStates()background: Rectangle {radius: 6border.color: urlTextField.activeFocus ? primaryColor : borderColorborder.width: 1color: "transparent"}}}RowLayout {Layout.fillWidth: truespacing: 10Label {text: "📁"font.pixelSize: 16Layout.preferredWidth: 30}TextField {id: pathTextFieldLayout.fillWidth: trueplaceholderText: "Enter save directory path"font.pixelSize: 14selectByMouse: truebackground: Rectangle {radius: 6border.color: pathTextField.activeFocus ? primaryColor : borderColorborder.width: 1color: "transparent"}}}}}}// 进度卡片Rectangle {Layout.fillWidth: trueLayout.preferredHeight: 180color: "white"radius: 12border.color: borderColorborder.width: 1ColumnLayout {anchors.fill: parentanchors.margins: 20spacing: 15Label {text: "下载进度"font.pixelSize: 16font.bold: truecolor: textColorLayout.fillWidth: true}ColumnLayout {Layout.fillWidth: truespacing: 12ProgressBar {id: progressBarLayout.fillWidth: truefrom: 0to: 1value: 0background: Rectangle {implicitHeight: 8color: "#e0e0e0"radius: 4}contentItem: Item {implicitHeight: 8Rectangle {width: progressBar.visualPosition * parent.widthheight: parent.heightradius: 4color: progressBar.Material.accent}}}RowLayout {Layout.fillWidth: trueLabel {id: progressTexttext: "准备下载"font.pixelSize: 13color: textColoropacity: 0.8Layout.fillWidth: true}Label {id: speedTexttext: ""font.pixelSize: 13color: primaryColorfont.bold: true}}// 状态显示Rectangle {Layout.fillWidth: trueLayout.preferredHeight: 50color: "transparent"border.color: borderColorborder.width: 1radius: 8Label {id: statusTextanchors.centerIn: parenttext: "输入url 点击下载"font.pixelSize: 14color: textColoropacity: 0.7}}}}}// 按钮区域RowLayout {Layout.alignment: Qt.AlignHCenterspacing: 15Button {id: downloadButtontext: "🚀 下载"Material.background: primaryColorMaterial.foreground: "white"font.pixelSize: 14font.bold: trueenabled: urlTextField.text !== "" && !downloadTool.downloadingonClicked: {progressBar.value = 0progressText.text = "开始下载..."speedTimer.lastBytes = 0speedTimer.lastTime = new Date().getTime()downloadTool.startDownload()}contentItem: Label {text: downloadButton.textfont: downloadButton.fontcolor: downloadButton.enabled ? "white" : "#999"horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenter}background: Rectangle {radius: 8color: downloadButton.enabled ? primaryColor : "#E0E0E0"}}Button {id: pauseResumeButtontext: "⏸️ 暂停"Material.background: warningColorMaterial.foreground: "white"font.pixelSize: 14enabled: false // 暂停功能需要额外实现visible: false // 暂时隐藏,需要额外实现暂停功能background: Rectangle {radius: 8color: pauseResumeButton.enabled ? warningColor : "#E0E0E0"}}Button {id: cancelButtontext: "❌ 取消"Material.background: errorColorMaterial.foreground: "white"font.pixelSize: 14font.bold: trueenabled: downloadTool.downloadingonClicked: {downloadTool.cancelDownload()statusText.text = "下载取消"statusText.color = warningColor}background: Rectangle {radius: 8color: cancelButton.enabled ? errorColor : "#E0E0E0"}}}// 底部信息Rectangle {Layout.fillWidth: trueLayout.preferredHeight: 40color: "transparent"Label {anchors.centerIn: parenttext: "v1.0"font.pixelSize: 12color: textColoropacity: 0.5}}}Component.onCompleted: {// 设置默认下载路径if (pathTextField.text === "") {pathTextField.text = "downloads"}updateButtonStates()// 设置示例URL(可选)// urlTextField.text = "https://www.example.com/sample-file.zip"}
}

main.cpp文件源码

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QIcon>
#include "DownloadTool.h"int main(int argc, char *argv[])
{QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);QGuiApplication app(argc, argv);app.setOrganizationName("YourCompany");app.setApplicationName("FileDownloader");// 注册 C++ 类型到 QMLqmlRegisterType<DownloadTool>("DownloadTool", 1, 0, "DownloadTool");QQmlApplicationEngine engine;engine.load(QUrl(QStringLiteral("qrc:/main.qml")));if (engine.rootObjects().isEmpty())return -1;return app.exec();
}

三、效果演示

输入下载文件的url,点击下载,下载开始同时显示进度条。

http://www.dtcms.com/a/465158.html

相关文章:

  • 【CANN训练营】+开源之星+GitCode算子开发环境快速搭建手册
  • 西安网站建设网站网站集约化建设
  • 设计网站名称wordpress无法发送
  • python获取国内股票数据
  • Win64下MSYS2开发环境完整配置指南
  • Linux进程第九讲——进程状态深度解析(三):僵尸进程(Z态)的本质、风险与实验验证
  • 系统之间文件同步方案
  • VTK实战:vtkImplicitSelectionLoop——用隐式函数实现“环选”的核心逻辑与工程实践
  • 使用compose和WheelView实现仿IOS中的3D滚轮控件-三级联动
  • Burpsuite工具使用
  • 做网站设计电脑需要什么配置企业如何建设网站呢
  • 旅游网站制作内容淘宝网站小视频怎么做的
  • 关于 Qt 6.10.0 中 FolderListModel 返回 undefined 路径
  • 做展会怎么引流到自己的网站小程序生成器
  • 【第五章:计算机视觉-项目实战之生成式算法实战:扩散模型】3.生成式算法实战:扩散模型-(1)从零开始训练自己的扩散模型
  • [VoiceRAG] 集成向量化 | Azure AI Search中建立自动化系统
  • 从效能革命到体验重构,易路 AI Agent 破局 HR 三重困境
  • 计算机视觉(opencv)——基于 OpenCV DNN 的实时人脸检测 + 年龄与性别识别
  • Flink 状态后端(State Backends)实战原理、选型、配置与调优
  • Node.js HTTP开发
  • 在 Mac 上使用 Docker 安装 Milvus 2.6.2
  • 福州市住房和城乡建设部网站wordpress 数据导入
  • 北京网站设计技术wordpress 评论验证
  • 亚马逊测评总踩雷?自养号技术筑牢安全防线,避开封号坑
  • Ubuntu 20.04 使用 Issac Gym 进行宇树G1人形机器人进行强化学习训练(Linux仿真)
  • 制造业工艺文档安全协作与集中管理方案
  • 场景美术师的“无限画板”:UE5中非破坏性的材质混合(Material Blending)工作流
  • 黑马微服务P3快速入门入门案例无法跑通解决方案,本文解决了数据库连接和java版本不匹配的问题
  • 遗留系统微服务改造(三):监控运维与最佳实践总结
  • 四川建设招标网站首页自己做的网站显示不安全怎么回事