!353 update:更新版本号为4.8.1~test2,修复更新器安装软件完成后仍然显示下载完成的问题

Merge pull request !353 from momen/dev
This commit is contained in:
2025-08-28 13:32:11 +00:00
committed by shenmo
parent 37ef14e975
commit 85564bf8ac
32 changed files with 37 additions and 1855 deletions
-321
View File
@@ -1,321 +0,0 @@
#include "appdelegate.h"
#include <QIcon>
#include <QDebug>
#include <QApplication>
#include <QDir>
#include <QProgressBar>
#include <QPushButton>
#include <QPainter>
#include <QMouseEvent>
AppDelegate::AppDelegate(QObject *parent)
: QStyledItemDelegate(parent), m_downloadManager(new DownloadManager(this)), m_installProcess(nullptr) {
connect(m_downloadManager, &DownloadManager::downloadFinished, this,
[this](const QString &packageName, bool success) {
if (m_downloads.contains(packageName)) {
m_downloads[packageName].isDownloading = false;
emit updateDisplay(packageName);
qDebug() << (success ? "下载完成:" : "下载失败:") << packageName;
if (success) {
enqueueInstall(packageName);
}
}
});
connect(m_downloadManager, &DownloadManager::downloadProgress, this,
[this](const QString &packageName, int progress) {
if (m_downloads.contains(packageName)) {
m_downloads[packageName].progress = progress;
qDebug()<<progress;
emit updateDisplay(packageName); // 实时刷新进度条
}
});
m_spinnerTimer.start();
}
void AppDelegate::setModel(QAbstractItemModel *model) {
m_model = model;
}
void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
painter->save();
if (option.state & QStyle::State_Selected)
painter->fillRect(option.rect, option.palette.highlight());
else
painter->fillRect(option.rect, QColor("#F3F4F6"));
QFont boldFont = option.font;
boldFont.setBold(true);
QFont normalFont = option.font;
QString name = index.data(Qt::DisplayRole).toString();
QString currentVersion = index.data(Qt::UserRole + 2).toString();
QString newVersion = index.data(Qt::UserRole + 3).toString();
QString iconPath = index.data(Qt::UserRole + 4).toString();
QString size = index.data(Qt::UserRole + 5).toString();
QString description = index.data(Qt::UserRole + 6).toString();
QRect rect = option.rect;
int margin = 10, spacing = 6, iconSize = 40;
QRect iconRect(rect.left() + margin, rect.top() + (rect.height() - iconSize) / 2, iconSize, iconSize);
QIcon(iconPath).paint(painter, iconRect);
int textX = iconRect.right() + margin;
int textWidth = rect.width() - textX - 100;
QRect nameRect(textX, rect.top() + margin, textWidth, 20);
painter->setFont(boldFont);
painter->setPen(QColor("#333333"));
painter->drawText(nameRect, Qt::AlignLeft | Qt::AlignVCenter, name);
QRect versionRect(textX, nameRect.bottom() + spacing, textWidth, 20);
painter->setFont(normalFont);
painter->setPen(QColor("#888888"));
painter->drawText(versionRect, Qt::AlignLeft | Qt::AlignVCenter,
QString("当前版本: %1 → 新版本: %2").arg(currentVersion, newVersion));
QRect descRect(textX, versionRect.bottom() + spacing, textWidth, 40);
painter->setFont(normalFont);
painter->setPen(QColor("#AAAAAA"));
painter->drawText(descRect, Qt::TextWordWrap,
QString("包大小:%1 MB").arg(QString::number(size.toDouble() / (1024 * 1024), 'f', 2)));
QString packageName = index.data(Qt::UserRole + 1).toString();
bool isDownloading = m_downloads.contains(packageName) && m_downloads[packageName].isDownloading;
int progress = m_downloads.value(packageName, DownloadInfo{0, false}).progress;
bool isInstalled = m_downloads.value(packageName).isInstalled;
bool isInstalling = m_downloads.value(packageName).isInstalling;
if (isDownloading) {
QRect progressRect(rect.right() - 270, rect.top() + (rect.height() - 20) / 2, 150, 20);
QStyleOptionProgressBar progressBarOption;
progressBarOption.rect = progressRect;
progressBarOption.minimum = 0;
progressBarOption.maximum = 100;
progressBarOption.progress = progress;
progressBarOption.text = QString("%1%").arg(progress);
progressBarOption.textVisible = true;
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter);
// 修改后的取消按钮绘制代码
QRect buttonRect(rect.right() - 80, rect.top() + (rect.height() - 30) / 2, 70, 30);
painter->setPen(Qt::NoPen);
painter->setBrush(QColor("#ff4444")); // 红色背景
painter->drawRoundedRect(buttonRect, 4, 4); // 圆角矩形
painter->setPen(Qt::white); // 白色文字
painter->setFont(option.font);
painter->drawText(buttonRect, Qt::AlignCenter, "取消");
} else if (isInstalling) {
// 安装中:显示转圈和文字
QRect spinnerRect(option.rect.right() - 80, option.rect.top() + (option.rect.height() - 30) / 2, 30, 30);
int angle = (m_spinnerTimer.elapsed() / 10) % 360;
QPen pen(QColor("#2563EB"), 3);
painter->setPen(pen);
painter->setRenderHint(QPainter::Antialiasing, true);
QRectF arcRect = spinnerRect.adjusted(3, 3, -3, -3);
painter->drawArc(arcRect, angle * 16, 120 * 16); // 120度弧
QRect textRect(option.rect.right() - 120, option.rect.top() + (option.rect.height() - 30) / 2, 110, 30);
painter->setPen(QColor("#2563EB"));
painter->setFont(option.font);
painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, "正在安装中");
} else {
QRect buttonRect(option.rect.right() - 80, option.rect.top() + (option.rect.height() - 30) / 2, 70, 30);
painter->setPen(Qt::NoPen);
if (isInstalled) {
painter->setBrush(QColor("#10B981"));
painter->drawRoundedRect(buttonRect, 4, 4);
painter->setPen(Qt::white);
painter->drawText(buttonRect, Qt::AlignCenter, "已安装");
} else if (m_downloads.contains(packageName) && !m_downloads[packageName].isDownloading) {
// 下载完成,按钮绿色,样式不变
painter->setBrush(QColor("#10B981"));
painter->drawRoundedRect(buttonRect, 4, 4);
painter->setPen(Qt::white);
painter->drawText(buttonRect, Qt::AlignCenter, "下载完成");
// 不需要特殊处理样式,交互在 editorEvent 控制
} else {
painter->setBrush(QColor("#e9effd"));
painter->drawRoundedRect(buttonRect, 4, 4);
painter->setPen(QColor("#2563EB"));
painter->drawText(buttonRect, Qt::AlignCenter, "更新");
}
}
painter->restore();
}
QSize AppDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const {
return QSize(option.rect.width(), 110);
}
bool AppDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
const QStyleOptionViewItem &option, const QModelIndex &index) {
if (event->type() == QEvent::MouseButtonRelease) {
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
QRect rect = option.rect;
QString packageName = index.data(Qt::UserRole + 1).toString();
if (m_downloads.contains(packageName) && m_downloads[packageName].isDownloading) {
QRect cancelButtonRect(rect.right() - 70, rect.top() + (rect.height() - 20) / 2, 60, 20);
if (cancelButtonRect.contains(mouseEvent->pos())) {
m_downloadManager->cancelDownload(packageName);
m_downloads.remove(packageName);
emit updateDisplay(packageName);
return true;
}
} else {
QRect buttonRect(rect.right() - 80, rect.top() + (rect.height() - 30) / 2, 70, 30);
if (buttonRect.contains(mouseEvent->pos())) {
// 判断是否为“下载完成”状态,如果是则不响应点击
if (m_downloads.contains(packageName) && !m_downloads[packageName].isDownloading) {
// “下载完成”状态,按钮失效,点击无效
return false;
}
QString downloadUrl = index.data(Qt::UserRole + 7).toString();
QString outputPath = QString("%1/%2.metalink").arg(QDir::tempPath(), packageName);
m_downloads[packageName] = {0, true};
m_downloadManager->startDownload(packageName, downloadUrl, outputPath);
emit updateDisplay(packageName);
return true;
}
}
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
void AppDelegate::startDownloadForAll() {
if (!m_model) return;
for (int row = 0; row < m_model->rowCount(); ++row) {
QModelIndex index = m_model->index(row, 0);
QString packageName = index.data(Qt::UserRole + 1).toString();
if (m_downloads.contains(packageName) && (m_downloads[packageName].isDownloading || m_downloads[packageName].isInstalled))
continue; // 跳过正在下载或已安装的
QString downloadUrl = index.data(Qt::UserRole + 7).toString();
QString outputPath = QString("%1/%2.metalink").arg(QDir::tempPath(), packageName);
m_downloads[packageName] = {0, true, false};
m_downloadManager->startDownload(packageName, downloadUrl, outputPath);
emit updateDisplay(packageName);
}
}
// 新增:安装队列相关实现
void AppDelegate::enqueueInstall(const QString &packageName) {
m_installQueue.enqueue(packageName);
if (!m_isInstalling) {
startNextInstall();
}
}
void AppDelegate::startNextInstall() {
if (m_installQueue.isEmpty()) {
m_isInstalling = false;
m_installingPackage.clear();
return;
}
m_isInstalling = true;
QString packageName = m_installQueue.dequeue();
m_installingPackage = packageName;
m_downloads[packageName].isInstalling = true;
emit updateDisplay(packageName);
// 查找 /tmp 下以包名开头的 .deb 文件
QDir tempDir(QDir::tempPath());
QStringList debs = tempDir.entryList(QStringList() << QString("%1_*.deb").arg(packageName), QDir::Files);
QString debPath;
if (!debs.isEmpty()) {
debPath = tempDir.absoluteFilePath(debs.first());
} else {
debs = tempDir.entryList(QStringList() << QString("%1*.deb").arg(packageName), QDir::Files);
if (!debs.isEmpty()) {
debPath = tempDir.absoluteFilePath(debs.first());
}
}
if (debPath.isEmpty()) {
qWarning() << "未找到deb文件,包名:" << packageName;
m_downloads[packageName].isInstalling = false;
emit updateDisplay(packageName);
m_installingPackage.clear();
startNextInstall();
return;
}
m_installProcess = new QProcess(this);
// 新增:准备安装日志文件
QString logPath = QString("/tmp/%1_install.log").arg(packageName);
QFile *logFile = new QFile(logPath, m_installProcess);
if (logFile->open(QIODevice::Append | QIODevice::Text)) {
// 设置权限为777
QFile::setPermissions(logPath, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner |
QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup |
QFile::ReadOther | QFile::WriteOther | QFile::ExeOther);
connect(m_installProcess, &QProcess::readyReadStandardOutput, this, [this, packageName, logFile]() {
QByteArray out = m_installProcess->readAllStandardOutput();
logFile->write(out);
logFile->flush();
QString text = QString::fromLocal8Bit(out);
qDebug().noquote() << text;
// 检查“软件包已安装”关键字
if (text.contains(QStringLiteral("软件包已安装"))) {
m_downloads[packageName].isInstalling = false;
m_downloads[packageName].isInstalled = true;
emit updateDisplay(packageName);
}
});
connect(m_installProcess, &QProcess::readyReadStandardError, this, [this, logFile]() {
QByteArray err = m_installProcess->readAllStandardError();
logFile->write(err);
logFile->flush();
qDebug().noquote() << QString::fromLocal8Bit(err);
});
connect(m_installProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, [this, packageName, logFile](int /*exitCode*/, QProcess::ExitStatus /*status*/) {
if (logFile) logFile->close();
// 若未检测到“软件包已安装”,此处兜底
if (!m_downloads[packageName].isInstalled) {
m_downloads[packageName].isInstalling = false;
}
emit updateDisplay(packageName);
m_installProcess->deleteLater();
m_installProcess = nullptr;
m_installingPackage.clear();
startNextInstall();
});
} else {
// 日志文件无法打开时,仍然要连接原有信号
connect(m_installProcess, &QProcess::readyReadStandardOutput, this, [this, packageName]() {
QByteArray out = m_installProcess->readAllStandardOutput();
QString text = QString::fromLocal8Bit(out);
qDebug().noquote() << text;
if (text.contains(QStringLiteral("软件包已安装"))) {
m_downloads[packageName].isInstalling = false;
m_downloads[packageName].isInstalled = true;
emit updateDisplay(packageName);
}
});
connect(m_installProcess, &QProcess::readyReadStandardError, this, [this]() {
QByteArray err = m_installProcess->readAllStandardError();
qDebug().noquote() << QString::fromLocal8Bit(err);
});
connect(m_installProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, [this, packageName](int /*exitCode*/, QProcess::ExitStatus /*status*/) {
emit updateDisplay(packageName);
m_installProcess->deleteLater();
m_installProcess = nullptr;
m_installingPackage.clear();
startNextInstall();
});
}
// 注意参数顺序:deb路径在前,--no-create-desktop-entry在后
QStringList args;
args << debPath << "--no-create-desktop-entry";
m_installProcess->start("ssinstall", args);
}