#include "appdelegate.h" #include #include #include #include #include #include #include #include #include #include #include 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; // 不要提前设置 isInstalled emit updateDisplay(packageName); qDebug() << (success ? "下载完成:" : "下载失败:") << packageName; if (success) { enqueueInstall(packageName); // 安装完成后再设置 isInstalled } else { // 下载失败,删除已存在的deb包并重新下载 QDir tempDir(QDir::tempPath()); QStringList debs = tempDir.entryList(QStringList() << QString("%1_*.deb").arg(packageName) << QString("%1*.deb").arg(packageName), QDir::Files); for (const QString &deb : debs) { QString debPath = tempDir.absoluteFilePath(deb); if (QFile::exists(debPath)) { if (QFile::remove(debPath)) { qDebug() << "已删除下载失败的软件包:" << debPath; } else { qWarning() << "删除下载失败的软件包失败:" << debPath; } } } // 重新开始下载 if (m_model) { for (int row = 0; row < m_model->rowCount(); ++row) { QModelIndex index = m_model->index(row, 0); if (index.data(Qt::UserRole + 1).toString() == packageName) { 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); break; } } } } } }); connect(m_downloadManager, &DownloadManager::downloadProgress, this, [this](const QString &packageName, int progress) { if (m_downloads.contains(packageName)) { m_downloads[packageName].progress = progress; qDebug()<state() != QProcess::NotRunning) { m_installProcess->kill(); m_installProcess->waitForFinished(3000); m_installProcess->deleteLater(); m_installProcess = nullptr; } } void AppDelegate::setModel(QAbstractItemModel *model) { m_model = model; } void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { painter->save(); // 检查是否为忽略状态 bool isIgnored = index.data(Qt::UserRole + 8).toBool(); if (option.state & QStyle::State_Selected) painter->fillRect(option.rect, option.palette.highlight()); else painter->fillRect(option.rect, QColor("#F3F4F6")); // 绘制复选框 QString packageName = index.data(Qt::UserRole + 1).toString(); bool isSelected = m_selectedPackages.contains(packageName); QRect checkboxRect(option.rect.left() + 10, option.rect.top() + (option.rect.height() - 20) / 2, 20, 20); // 绘制复选框边框 QColor checkboxColor = isIgnored ? QColor("#CCCCCC") : QColor("#888888"); painter->setPen(checkboxColor); painter->setBrush(Qt::NoBrush); painter->drawRect(checkboxRect); // 如果选中,绘制勾选标记 if (isSelected && !isIgnored) { painter->setPen(QPen(QColor("#2563EB"), 2)); painter->setBrush(QColor("#2563EB")); painter->drawRect(checkboxRect.adjusted(4, 4, -4, -4)); } 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(); QString source = index.data(Qt::UserRole + 9).toString(); QRect rect = option.rect; int margin = 10, spacing = 6, iconSize = 40; // 调整图标位置,为复选框留出空间 QRect iconRect(rect.left() + 40, rect.top() + (rect.height() - iconSize) / 2, iconSize, iconSize); // 如果是忽略状态,绘制灰色图标 if (isIgnored) { // 创建灰度效果 QPixmap originalPixmap = QIcon(iconPath).pixmap(iconSize, iconSize); QPixmap grayPixmap(originalPixmap.size()); grayPixmap.fill(Qt::transparent); QPainter grayPainter(&grayPixmap); grayPainter.setOpacity(0.3); // 设置透明度使其变灰 grayPainter.drawPixmap(0, 0, originalPixmap); grayPainter.end(); painter->drawPixmap(iconRect, grayPixmap); } else { 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); QColor nameColor = isIgnored ? QColor("#999999") : QColor("#333333"); painter->setPen(nameColor); // 计算名称宽度 QFontMetrics fontMetrics(boldFont); int nameWidth = fontMetrics.horizontalAdvance(name); // 绘制名称 painter->drawText(nameRect, Qt::AlignLeft | Qt::AlignVCenter, name); // 绘制来源Tag if (!source.isEmpty()) { int tagX = textX + nameWidth + 10; QString tagText; QColor bgColor; QColor textColor; if (source == "apm") { tagText = "APM"; bgColor = QColor("#3B82F6"); // 蓝色 textColor = QColor("#FFFFFF"); } else { tagText = "传统deb"; bgColor = QColor("#F97316"); // 橙色 textColor = QColor("#FFFFFF"); } int tagWidth = fontMetrics.horizontalAdvance(tagText) + 12; int tagHeight = 18; QRect tagRect(tagX, rect.top() + margin + 1, tagWidth, tagHeight); // 绘制Tag背景 painter->setBrush(bgColor); painter->setPen(Qt::NoPen); painter->drawRoundedRect(tagRect, 9, 9); // 绘制Tag文本 painter->setFont(normalFont); painter->setPen(textColor); painter->drawText(tagRect, Qt::AlignCenter, tagText); } QRect versionRect(textX, nameRect.bottom() + spacing, textWidth, 20); painter->setFont(normalFont); QColor versionColor = isIgnored ? QColor("#AAAAAA") : QColor("#888888"); painter->setPen(versionColor); painter->drawText(versionRect, Qt::AlignLeft | Qt::AlignVCenter, QString("当前版本: %1 → 新版本: %2").arg(currentVersion, newVersion)); QRect descRect(textX, versionRect.bottom() + spacing, textWidth, 40); painter->setFont(normalFont); QColor descColor = isIgnored ? QColor("#CCCCCC") : QColor("#AAAAAA"); painter->setPen(descColor); painter->drawText(descRect, Qt::TextWordWrap, QString("包大小:%1 MB").arg(QString::number(size.toDouble() / (1024 * 1024), 'f', 2))); 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 (isIgnored) { QRect ignoredTextRect(rect.right() - 170, rect.top() + (rect.height() - 30) / 2, 80, 30); painter->setPen(QColor("#999999")); painter->setFont(option.font); painter->drawText(ignoredTextRect, Qt::AlignCenter, "已忽略"); // 绘制取消忽略按钮 QRect unignoreButtonRect(rect.right() - 80, rect.top() + (rect.height() - 30) / 2, 70, 30); painter->setPen(Qt::NoPen); painter->setBrush(QColor("#F3F4F6")); painter->drawRoundedRect(unignoreButtonRect, 4, 4); painter->setPen(QColor("#6B7280")); painter->drawText(unignoreButtonRect, Qt::AlignCenter, "取消忽略"); } else if (isDownloading) { // 绘制灰底蓝色进度条 QRect progressRect(rect.right() - 270, rect.top() + (rect.height() - 20) / 2, 150, 20); // 绘制背景 painter->setPen(Qt::NoPen); painter->setBrush(QColor("#E5E7EB")); painter->drawRoundedRect(progressRect, 10, 10); // 绘制进度 int progressWidth = progressRect.width() * progress / 100; QRect progressFillRect(progressRect.left(), progressRect.top(), progressWidth, progressRect.height()); painter->setBrush(QColor("#3B82F6")); painter->drawRoundedRect(progressFillRect, 10, 10); // 绘制进度文本 painter->setPen(Qt::white); painter->setFont(option.font); painter->drawText(progressRect, Qt::AlignCenter, QString("%1%").arg(progress)); QRect cancelButtonRect(rect.right() - 80, rect.top() + (rect.height() - 30) / 2, 70, 30); painter->setPen(Qt::NoPen); painter->setBrush(QColor("#ff4444")); painter->drawRoundedRect(cancelButtonRect, 4, 4); painter->setPen(Qt::white); painter->setFont(option.font); painter->drawText(cancelButtonRect, Qt::AlignCenter, "取消"); } else if (isInstalling) { QRect spinnerRect(option.rect.right() - 80, option.rect.top() + (option.rect.height() - 30) / 2, 30, 30); // 修改:使用 m_spinnerAngle QPen pen(QColor("#2563EB"), 3); painter->setPen(pen); painter->setRenderHint(QPainter::Antialiasing, true); QRectF arcRect = spinnerRect.adjusted(3, 3, -3, -3); painter->drawArc(arcRect, m_spinnerAngle * 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 ignoreButtonRect(option.rect.right() - 160, option.rect.top() + (option.rect.height() - 30) / 2, 70, 30); painter->setPen(Qt::NoPen); painter->setBrush(QColor("#F3F4F6")); painter->drawRoundedRect(ignoreButtonRect, 4, 4); painter->setPen(QColor("#6B7280")); painter->drawText(ignoreButtonRect, Qt::AlignCenter, "忽略"); // 绘制更新按钮 QRect updateButtonRect(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(updateButtonRect, 4, 4); painter->setPen(Qt::white); painter->drawText(updateButtonRect, Qt::AlignCenter, "已安装"); } else if (m_downloads.contains(packageName) && !m_downloads[packageName].isDownloading) { painter->setBrush(QColor("#10B981")); painter->drawRoundedRect(updateButtonRect, 4, 4); painter->setPen(Qt::white); painter->drawText(updateButtonRect, Qt::AlignCenter, "下载完成"); } else { painter->setBrush(QColor("#e9effd")); painter->drawRoundedRect(updateButtonRect, 4, 4); painter->setPen(QColor("#2563EB")); painter->drawText(updateButtonRect, 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(event); QRect rect = option.rect; QString packageName = index.data(Qt::UserRole + 1).toString(); // 检查是否为忽略状态,如果是则只允许取消忽略按钮的交互 bool isIgnored = index.data(Qt::UserRole + 8).toBool(); if (isIgnored) { QRect unignoreButtonRect(option.rect.right() - 80, option.rect.top() + (option.rect.height() - 30) / 2, 70, 30); if (unignoreButtonRect.contains(mouseEvent->pos())) { // 发送取消忽略信号 emit unignoreApp(packageName); return true; } return true; // 消耗其他事件,不允许其他交互 } // 检查是否点击了复选框 QRect checkboxRect(rect.left() + 10, rect.top() + (rect.height() - 20) / 2, 20, 20); if (checkboxRect.contains(mouseEvent->pos())) { if (m_selectedPackages.contains(packageName)) { m_selectedPackages.remove(packageName); } else { m_selectedPackages.insert(packageName); } emit updateDisplay(packageName); return true; } 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); // 删除未下载完成的软件包 QDir tempDir(QDir::tempPath()); QStringList debs = tempDir.entryList(QStringList() << QString("%1_*.deb").arg(packageName) << QString("%1*.deb").arg(packageName), QDir::Files); for (const QString &deb : debs) { QString debPath = tempDir.absoluteFilePath(deb); if (QFile::exists(debPath)) { if (QFile::remove(debPath)) { qDebug() << "已删除未下载完成的软件包:" << debPath; } else { qWarning() << "删除未下载完成的软件包失败:" << debPath; } } } emit updateDisplay(packageName); return true; } } else { // 检查是否点击了忽略按钮 QRect ignoreButtonRect(rect.right() - 160, rect.top() + (rect.height() - 30) / 2, 70, 30); if (ignoreButtonRect.contains(mouseEvent->pos())) { QString currentVersion = index.data(Qt::UserRole + 2).toString(); emit ignoreApp(packageName, currentVersion); return true; } // 检查是否点击了更新按钮 QRect updateButtonRect(rect.right() - 80, rect.top() + (rect.height() - 30) / 2, 70, 30); if (updateButtonRect.contains(mouseEvent->pos())) { if (m_downloads.contains(packageName) && !m_downloads[packageName].isDownloading) { return false; } // 检查是否为迁移项 bool isMigration = index.data(Qt::UserRole + 10).toBool(); if (isMigration) { QString migrationTarget = index.data(Qt::UserRole + 12).toString(); QMessageBox::StandardButton reply = QMessageBox::question( nullptr, "确认迁移", QString("此更新将把应用替换为 %1 版本,是否继续?").arg(migrationTarget.toUpper()), QMessageBox::Yes | QMessageBox::No ); if (reply != QMessageBox::Yes) { return true; // 用户取消,不执行更新 } // 用户确认,标记为迁移安装 m_migrationPackages.insert(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()); } } // 触发下载流程(无论是否存在deb包,都尝试续传) 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; // 收集所有迁移项,准备批量提示 QStringList migrationPackages; for (int row = 0; row < m_model->rowCount(); ++row) { QModelIndex index = m_model->index(row, 0); // 检查应用是否被忽略 bool isIgnored = index.data(Qt::UserRole + 8).toBool(); if (isIgnored) { continue; } // 检查是否为迁移项 bool isMigration = index.data(Qt::UserRole + 10).toBool(); if (isMigration) { QString packageName = index.data(Qt::UserRole + 1).toString(); QString migrationTarget = index.data(Qt::UserRole + 12).toString(); migrationPackages.append(QString("%1 → %2").arg(packageName, migrationTarget.toUpper())); } } // 如果有迁移项,弹出批量确认对话框 if (!migrationPackages.isEmpty()) { QString message = "以下应用更新后将替换为新的版本来源:\n\n"; message += migrationPackages.join("\n"); message += "\n\n是否继续更新这些应用?"; QMessageBox::StandardButton reply = QMessageBox::question( nullptr, "确认迁移", message, QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel ); if (reply == QMessageBox::Cancel) { return; // 完全取消更新全部 } // 如果用户点击 Yes,将所有迁移项加入迁移集合 if (reply == QMessageBox::Yes) { for (int row = 0; row < m_model->rowCount(); ++row) { QModelIndex index = m_model->index(row, 0); bool isMigration = index.data(Qt::UserRole + 10).toBool(); if (isMigration) { QString packageName = index.data(Qt::UserRole + 1).toString(); m_migrationPackages.insert(packageName); } } } // 如果用户点击 No,跳过迁移项,继续更新其他应用 } for (int row = 0; row < m_model->rowCount(); ++row) { QModelIndex index = m_model->index(row, 0); // 检查应用是否被忽略 bool isIgnored = index.data(Qt::UserRole + 8).toBool(); if (isIgnored) { qDebug() << "跳过被忽略的应用:" << index.data(Qt::UserRole + 1).toString(); continue; } QString packageName = index.data(Qt::UserRole + 1).toString(); // 检查是否为迁移项且用户未确认 bool isMigration = index.data(Qt::UserRole + 10).toBool(); if (isMigration && !m_migrationPackages.contains(packageName)) { qDebug() << "跳过未确认的迁移项:" << packageName; continue; } 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(); m_spinnerUpdateTimer.stop(); // 新增:停止定时器 return; } m_isInstalling = true; QString packageName = m_installQueue.dequeue(); m_installingPackage = packageName; m_downloads[packageName].isInstalling = true; m_spinnerUpdateTimer.start(); // 新增:启动定时器 emit updateDisplay(packageName); 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; } // 获取包的来源信息 QString source = "aptss"; // 默认来源 if (m_model) { for (int row = 0; row < m_model->rowCount(); ++row) { QModelIndex index = m_model->index(row, 0); if (index.data(Qt::UserRole + 1).toString() == packageName) { source = index.data(Qt::UserRole + 9).toString(); break; } } } // 检查是否为迁移场景 bool isMigration = m_migrationPackages.contains(packageName); // 如果是APM包或者是迁移场景,先检查APM中是否存在对应的包,再卸载APTSS版本 if (source == "apm" || isMigration) { // 检查APM中是否存在对应的包 QProcess checkProcess; QStringList checkArgs; checkArgs << "list" << packageName; checkProcess.start("apm", checkArgs); checkProcess.waitForFinished(30000); // 30秒超时 QString checkOutput = checkProcess.readAllStandardOutput(); if (checkOutput.contains(packageName)) { // APM中存在对应的包,卸载APTSS版本 QProcess removeProcess; QStringList removeArgs; removeArgs << "remove" << "--purge" << "-y" << packageName; removeProcess.start("aptss", removeArgs); removeProcess.waitForFinished(30000); // 30秒超时 qDebug() << "卸载APTSS版本" << packageName << "退出码:" << removeProcess.exitCode(); } else { // APM中不存在对应的包,安装失败 qWarning() << "APM中不存在对应的包:" << 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)) { 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::of(&QProcess::finished), this, [this, packageName, logFile, debPath, source](int exitCode, QProcess::ExitStatus status) { if (logFile) logFile->close(); m_downloads[packageName].isInstalling = false; if (exitCode == 0) { m_downloads[packageName].isInstalled = true; // 安装成功后删除deb包 if (QFile::exists(debPath)) { if (QFile::remove(debPath)) { qDebug() << "已删除deb包:" << debPath; } else { qWarning() << "删除deb包失败:" << debPath; } } } else { // 安装失败,尝试从APM安装 if (source == "aptss") { qDebug() << "APTSS安装失败,尝试从APM安装:" << packageName; // 检查apm命令是否存在 QProcess whichProcess; whichProcess.start("which", QStringList() << "apm"); whichProcess.waitForFinished(5000); if (whichProcess.exitCode() != 0) { // apm命令不存在,先安装apm qDebug() << "apm命令不存在,先安装apm"; QProcess installApmProcess; installApmProcess.start("aptss", QStringList() << "install" << "apm" << "-y"); installApmProcess.waitForFinished(60000); // 60秒超时 if (installApmProcess.exitCode() != 0) { qWarning() << "安装apm失败:" << packageName; emit updateDisplay(packageName); m_installProcess->deleteLater(); m_installProcess = nullptr; m_installingPackage.clear(); startNextInstall(); return; } qDebug() << "apm安装成功"; } // 检查APM中是否存在对应的包 QProcess checkProcess; QStringList checkArgs; checkArgs << "list" << packageName; checkProcess.start("apm", checkArgs); checkProcess.waitForFinished(30000); // 30秒超时 QString checkOutput = checkProcess.readAllStandardOutput(); if (checkOutput.contains(packageName)) { // APM中存在对应的包,卸载当前版本 QProcess removeProcess; QStringList removeArgs; removeArgs << "remove" << "--purge" << "-y" << packageName; removeProcess.start("aptss", removeArgs); removeProcess.waitForFinished(30000); // 从APM获取下载URL,使用与aptssupdater相同的方法 QString downloadUrl; QProcess process; QString command = QString("amber-pm-debug /usr/bin/apt -c /opt/durapps/spark-store/bin/apt-fast-conf/aptss-apt.conf download %1 --print-uris").arg(packageName); process.start("bash", QStringList() << "-c" << command); if (process.waitForFinished(30000)) { // 30秒超时 QString output = process.readAllStandardOutput(); // 解析输出格式:'URL' 文件名 大小 SHA512:哈希值 QRegularExpression regex(R"('([^']+)'\s+\S+\s+(\d+)\s+SHA512:([^\s]+))"); QRegularExpressionMatch match = regex.match(output); if (match.hasMatch()) { downloadUrl = match.captured(1); } } if (!downloadUrl.isEmpty()) { // 使用更新器的下载功能下载APM包 QString outputPath = QString("%1/%2.metalink").arg(QDir::tempPath(), packageName); m_downloads[packageName] = {0, true}; m_downloadManager->startDownload(packageName, downloadUrl, outputPath); // 等待下载完成后再安装 QEventLoop loop; connect(m_downloadManager, &DownloadManager::downloadFinished, &loop, [&loop](const QString &, bool) { loop.quit(); }); loop.exec(); // 下载完成后,使用APM安装 QDir tempDir(QDir::tempPath()); QStringList debs = tempDir.entryList(QStringList() << QString("%1_*.deb").arg(packageName) << QString("%1*.deb").arg(packageName), QDir::Files); if (!debs.isEmpty()) { QString apmDebPath = tempDir.absoluteFilePath(debs.first()); QProcess apmProcess; QStringList apmArgs; apmArgs << "ssaudit" << apmDebPath; apmProcess.start("apm", apmArgs); apmProcess.waitForFinished(60000); // 60秒超时 int apmExitCode = apmProcess.exitCode(); qDebug() << "APM安装" << packageName << "退出码:" << apmExitCode; // APM安装成功后设置状态 if (apmExitCode == 0) { m_downloads[packageName].isInstalling = false; m_downloads[packageName].isInstalled = true; // 安装成功后删除deb包 if (QFile::exists(apmDebPath)) { if (QFile::remove(apmDebPath)) { qDebug() << "已删除deb包:" << apmDebPath; } else { qWarning() << "删除deb包失败:" << apmDebPath; } } } } } else { qWarning() << "无法获取APM包的下载URL:" << packageName; } } else { // APM中不存在对应的包,不卸载aptss包 qWarning() << "APM中不存在对应的包,安装失败:" << packageName; } } } emit updateDisplay(packageName); m_installProcess->deleteLater(); m_installProcess = nullptr; m_installingPackage.clear(); startNextInstall(); }); } else { connect(m_installProcess, &QProcess::readyReadStandardOutput, this, [this, packageName, debPath]() { 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; // 安装成功后删除deb包 if (QFile::exists(debPath)) { if (QFile::remove(debPath)) { qDebug() << "已删除deb包:" << debPath; } else { qWarning() << "删除deb包失败:" << debPath; } } emit updateDisplay(packageName); } }); connect(m_installProcess, &QProcess::readyReadStandardError, this, [this]() { QByteArray err = m_installProcess->readAllStandardError(); qDebug().noquote() << QString::fromLocal8Bit(err); }); connect(m_installProcess, QOverload::of(&QProcess::finished), this, [this, packageName, debPath, source](int exitCode, QProcess::ExitStatus /*status*/) { // 如果通过退出码判断安装成功,也删除deb包 if (exitCode == 0 && QFile::exists(debPath)) { if (QFile::remove(debPath)) { qDebug() << "已删除deb包:" << debPath; } else { qWarning() << "删除deb包失败:" << debPath; } } else { // 安装失败,尝试从APM安装 if (source == "aptss") { qDebug() << "APTSS安装失败,尝试从APM安装:" << packageName; // 检查apm命令是否存在 QProcess whichProcess; whichProcess.start("which", QStringList() << "apm"); whichProcess.waitForFinished(5000); if (whichProcess.exitCode() != 0) { // apm命令不存在,先安装apm qDebug() << "apm命令不存在,先安装apm"; QProcess installApmProcess; installApmProcess.start("aptss", QStringList() << "install" << "apm" << "-y"); installApmProcess.waitForFinished(60000); // 60秒超时 if (installApmProcess.exitCode() != 0) { qWarning() << "安装apm失败:" << packageName; emit updateDisplay(packageName); m_installProcess->deleteLater(); m_installProcess = nullptr; m_installingPackage.clear(); startNextInstall(); return; } qDebug() << "apm安装成功"; } // 检查APM中是否存在对应的包 QProcess checkProcess; QStringList checkArgs; checkArgs << "list" << packageName; checkProcess.start("apm", checkArgs); checkProcess.waitForFinished(30000); // 30秒超时 QString checkOutput = checkProcess.readAllStandardOutput(); if (checkOutput.contains(packageName)) { // APM中存在对应的包,卸载当前版本 QProcess removeProcess; QStringList removeArgs; removeArgs << "remove" << "--purge" << "-y" << packageName; removeProcess.start("aptss", removeArgs); removeProcess.waitForFinished(30000); // 从APM获取下载URL,使用与aptssupdater相同的方法 QString downloadUrl; QProcess process; QString command = QString("amber-pm-debug /usr/bin/apt -c /opt/durapps/spark-store/bin/apt-fast-conf/aptss-apt.conf download %1 --print-uris").arg(packageName); process.start("bash", QStringList() << "-c" << command); if (process.waitForFinished(30000)) { // 30秒超时 QString output = process.readAllStandardOutput(); // 解析输出格式:'URL' 文件名 大小 SHA512:哈希值 QRegularExpression regex(R"('([^']+)'\s+\S+\s+(\d+)\s+SHA512:([^\s]+))"); QRegularExpressionMatch match = regex.match(output); if (match.hasMatch()) { downloadUrl = match.captured(1); } } if (!downloadUrl.isEmpty()) { // 使用更新器的下载功能下载APM包 QString outputPath = QString("%1/%2.metalink").arg(QDir::tempPath(), packageName); m_downloads[packageName] = {0, true}; m_downloadManager->startDownload(packageName, downloadUrl, outputPath); // 等待下载完成后再安装 QEventLoop loop; connect(m_downloadManager, &DownloadManager::downloadFinished, &loop, [&loop](const QString &, bool) { loop.quit(); }); loop.exec(); // 下载完成后,使用APM安装 QDir tempDir(QDir::tempPath()); QStringList debs = tempDir.entryList(QStringList() << QString("%1_*.deb").arg(packageName) << QString("%1*.deb").arg(packageName), QDir::Files); if (!debs.isEmpty()) { QString apmDebPath = tempDir.absoluteFilePath(debs.first()); QProcess apmProcess; QStringList apmArgs; apmArgs << "ssaudit" << apmDebPath; apmProcess.start("apm", apmArgs); apmProcess.waitForFinished(60000); // 60秒超时 int apmExitCode = apmProcess.exitCode(); qDebug() << "APM安装" << packageName << "退出码:" << apmExitCode; // APM安装成功后设置状态 if (apmExitCode == 0) { m_downloads[packageName].isInstalling = false; m_downloads[packageName].isInstalled = true; // 安装成功后删除deb包 if (QFile::exists(apmDebPath)) { if (QFile::remove(apmDebPath)) { qDebug() << "已删除deb包:" << apmDebPath; } else { qWarning() << "删除deb包失败:" << apmDebPath; } } } } } else { qWarning() << "无法获取APM包的下载URL:" << packageName; } } else { // APM中不存在对应的包,不卸载aptss包 qWarning() << "APM中不存在对应的包,安装失败:" << packageName; } } } emit updateDisplay(packageName); m_installProcess->deleteLater(); m_installProcess = nullptr; m_installingPackage.clear(); startNextInstall(); }); } QStringList args; if (source == "apm") { // APM 包使用 apm ssaudit 安装 args << "ssaudit" << debPath; m_installProcess->start("apm", args); } else { // APTSS 包使用 ssinstall 安装 args << debPath << "--no-create-desktop-entry" << "--delete-after-install" << "--native"; m_installProcess->start("/usr/bin/ssinstall", args); } } // 新增槽函数,用于更新旋转角度并触发刷新 void AppDelegate::updateSpinner() { m_spinnerAngle = (m_spinnerAngle + 10) % 360; // 每次增加10度 emit updateDisplay(m_installingPackage); // 仅刷新当前正在安装的项 } // 新增:更新选中应用的方法 void AppDelegate::startDownloadForSelected() { if (!m_model) return; // 首先检查选中的应用中是否有迁移项 QStringList selectedMigrationPackages; 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_selectedPackages.contains(packageName)) { bool isMigration = index.data(Qt::UserRole + 10).toBool(); if (isMigration && !m_migrationPackages.contains(packageName)) { QString migrationTarget = index.data(Qt::UserRole + 12).toString(); selectedMigrationPackages.append(QString("%1 → %2").arg(packageName, migrationTarget.toUpper())); } } } // 如果有未确认的迁移项,弹出确认对话框 if (!selectedMigrationPackages.isEmpty()) { QString message = "以下选中的应用更新后将替换为新的版本来源:\n\n"; message += selectedMigrationPackages.join("\n"); message += "\n\n是否继续更新这些应用?"; QMessageBox::StandardButton reply = QMessageBox::question( nullptr, "确认迁移", message, QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel ); if (reply == QMessageBox::Cancel) { return; // 完全取消更新 } // 如果用户点击 Yes,将选中的迁移项加入迁移集合 if (reply == QMessageBox::Yes) { 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_selectedPackages.contains(packageName)) { bool isMigration = index.data(Qt::UserRole + 10).toBool(); if (isMigration) { m_migrationPackages.insert(packageName); } } } } // 如果用户点击 No,继续更新其他应用,跳过未确认的迁移项 } for (int row = 0; row < m_model->rowCount(); ++row) { QModelIndex index = m_model->index(row, 0); QString packageName = index.data(Qt::UserRole + 1).toString(); // 检查应用是否被忽略 bool isIgnored = index.data(Qt::UserRole + 8).toBool(); if (isIgnored) { qDebug() << "跳过被忽略的应用:" << packageName; continue; } // 只下载选中的应用 if (m_selectedPackages.contains(packageName)) { // 检查是否为迁移项且用户未确认 bool isMigration = index.data(Qt::UserRole + 10).toBool(); if (isMigration && !m_migrationPackages.contains(packageName)) { qDebug() << "跳过未确认的迁移项:" << packageName; continue; } 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::setSelectedPackages(const QSet &selected) { m_selectedPackages = selected; } QSet AppDelegate::getSelectedPackages() const { return m_selectedPackages; } void AppDelegate::clearSelection() { m_selectedPackages.clear(); } // 实现获取下载状态信息的方法 const QHash& AppDelegate::getDownloads() const { return m_downloads; }