From a7e45d37da8088ea0460104e2100d682e41c67ae Mon Sep 17 00:00:00 2001 From: momen Date: Tue, 9 Sep 2025 22:28:18 +0800 Subject: [PATCH 01/32] =?UTF-8?q?update:=E5=8A=A0=E5=85=A5QDebug=E5=A4=B4?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/applistmodel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applistmodel.h b/src/applistmodel.h index 2fb36c0..9e5e79a 100644 --- a/src/applistmodel.h +++ b/src/applistmodel.h @@ -5,7 +5,7 @@ #include // 添加 QJsonObject 头文件 #include - +#include class AppListModel : public QAbstractListModel { Q_OBJECT From f07ca5aac03b9d0961ea3c7f2d444eaa893cf0bb Mon Sep 17 00:00:00 2001 From: momen Date: Wed, 10 Sep 2025 22:52:24 +0800 Subject: [PATCH 02/32] =?UTF-8?q?update:=E6=94=B9=E7=94=A8qtimer=E6=9D=A5?= =?UTF-8?q?=E5=88=B7=E6=96=B0=E5=8A=A0=E8=BD=BD=E8=BD=AC=E5=9C=88=E5=8A=A8?= =?UTF-8?q?=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appdelegate.cpp | 43 ++++++++++++++++++++++--------------------- src/appdelegate.h | 16 ++++++++++++---- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/appdelegate.cpp b/src/appdelegate.cpp index 0c93065..7720994 100644 --- a/src/appdelegate.cpp +++ b/src/appdelegate.cpp @@ -7,6 +7,7 @@ #include #include #include +#include AppDelegate::AppDelegate(QObject *parent) : QStyledItemDelegate(parent), m_downloadManager(new DownloadManager(this)), m_installProcess(nullptr) { @@ -31,7 +32,11 @@ AppDelegate::AppDelegate(QObject *parent) emit updateDisplay(packageName); // 实时刷新进度条 } }); - m_spinnerTimer.start(); + // m_spinnerTimer.start(); // 移除这行 + + // 新增:初始化和连接 QTimer + m_spinnerUpdateTimer.setInterval(20); // 刷新间隔,可以调整 + connect(&m_spinnerUpdateTimer, &QTimer::timeout, this, &AppDelegate::updateSpinner); } void AppDelegate::setModel(QAbstractItemModel *model) { @@ -100,24 +105,23 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c 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->setBrush(QColor("#ff4444")); + painter->drawRoundedRect(buttonRect, 4, 4); - painter->setPen(Qt::white); // 白色文字 + 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; + + // 修改:使用 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, angle * 16, 120 * 16); // 120度弧 + 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")); @@ -132,12 +136,10 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c 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); @@ -171,9 +173,7 @@ bool AppDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, } 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(); @@ -196,7 +196,7 @@ void AppDelegate::startDownloadForAll() { 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; // 跳过正在下载或已安装的 + 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}; @@ -205,7 +205,6 @@ void AppDelegate::startDownloadForAll() { } } -// 新增:安装队列相关实现 void AppDelegate::enqueueInstall(const QString &packageName) { m_installQueue.enqueue(packageName); if (!m_isInstalling) { @@ -217,15 +216,16 @@ 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); - // 查找 /tmp 下以包名开头的 .deb 文件 QDir tempDir(QDir::tempPath()); QStringList debs = tempDir.entryList(QStringList() << QString("%1_*.deb").arg(packageName), QDir::Files); QString debPath; @@ -249,11 +249,9 @@ void AppDelegate::startNextInstall() { 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); @@ -263,7 +261,6 @@ void AppDelegate::startNextInstall() { logFile->flush(); QString text = QString::fromLocal8Bit(out); qDebug().noquote() << text; - // 检查“软件包已安装”关键字 if (text.contains(QStringLiteral("软件包已安装"))) { m_downloads[packageName].isInstalling = false; m_downloads[packageName].isInstalled = true; @@ -281,7 +278,7 @@ void AppDelegate::startNextInstall() { if (logFile) logFile->close(); m_downloads[packageName].isInstalling = false; if (exitCode == 0) { - m_downloads[packageName].isInstalled = true; // 安装成功 + m_downloads[packageName].isInstalled = true; } emit updateDisplay(packageName); m_installProcess->deleteLater(); @@ -290,7 +287,6 @@ void AppDelegate::startNextInstall() { startNextInstall(); }); } else { - // 日志文件无法打开时,仍然要连接原有信号 connect(m_installProcess, &QProcess::readyReadStandardOutput, this, [this, packageName]() { QByteArray out = m_installProcess->readAllStandardOutput(); QString text = QString::fromLocal8Bit(out); @@ -315,8 +311,13 @@ void AppDelegate::startNextInstall() { }); } - // 注意参数顺序:deb路径在前,--no-create-desktop-entry在后 QStringList args; args << debPath << "--no-create-desktop-entry"; m_installProcess->start("ssinstall", args); } + +// 新增槽函数,用于更新旋转角度并触发刷新 +void AppDelegate::updateSpinner() { + m_spinnerAngle = (m_spinnerAngle + 10) % 360; // 每次增加10度 + emit updateDisplay(m_installingPackage); // 仅刷新当前正在安装的项 +} \ No newline at end of file diff --git a/src/appdelegate.h b/src/appdelegate.h index 0545155..216d9be 100644 --- a/src/appdelegate.h +++ b/src/appdelegate.h @@ -5,13 +5,15 @@ #include #include #include +#include + #include "downloadmanager.h" struct DownloadInfo { int progress = 0; bool isDownloading = false; bool isInstalled = false; - bool isInstalling = false; // 新增:标记是否正在安装 + bool isInstalling = false; }; class AppDelegate : public QStyledItemDelegate { @@ -30,6 +32,9 @@ public: signals: void updateDisplay(const QString &packageName); +private slots: + void updateSpinner(); // 新增槽函数 + private: DownloadManager *m_downloadManager; QHash m_downloads; @@ -38,9 +43,12 @@ private: QQueue m_installQueue; bool m_isInstalling = false; QProcess *m_installProcess = nullptr; - QString m_installingPackage; // 当前正在安装的包名 - QElapsedTimer m_spinnerTimer; // 用于转圈动画 + QString m_installingPackage; + QElapsedTimer m_spinnerTimer; + + QTimer m_spinnerUpdateTimer; // 新增定时器 + int m_spinnerAngle = 0; // 新增角度变量 void enqueueInstall(const QString &packageName); void startNextInstall(); -}; +}; \ No newline at end of file From 84c5897d9b74241fe82b12ad8e8c68d9230f1f6d Mon Sep 17 00:00:00 2001 From: momen Date: Thu, 11 Sep 2025 22:54:38 +0800 Subject: [PATCH 03/32] =?UTF-8?q?chore:=E6=9B=B4=E6=96=B0=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E6=8F=90=E9=86=92=E5=86=85=E5=AE=B9=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 102bda6..23dd379 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -258,7 +258,7 @@ void MainWindow::runAptssUpgrade() process.closeWriteChannel(); process.waitForFinished(-1); if (process.exitCode() != 0) { - QMessageBox::warning(this, "升级失败", "执行 sudo aptss ssupdate 失败,请检查系统环境。"); + QMessageBox::warning(this, "升级失败", "执行 sudo aptss ssupdate 失败,请检查系统环境或稍后再试。"); } } From a78ee36ae7e0e78ee5963025449b7423d87598cf Mon Sep 17 00:00:00 2001 From: momen Date: Fri, 12 Sep 2025 23:18:17 +0800 Subject: [PATCH 04/32] =?UTF-8?q?chore:=E7=BB=99=E5=85=B3=E9=97=AD?= =?UTF-8?q?=E7=AA=97=E5=8F=A3=E6=B7=BB=E5=8A=A0=E8=AF=A2=E9=97=AE=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mainwindow.cpp | 4 ++++ src/mainwindow.h | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 23dd379..466a730 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -261,7 +261,11 @@ void MainWindow::runAptssUpgrade() QMessageBox::warning(this, "升级失败", "执行 sudo aptss ssupdate 失败,请检查系统环境或稍后再试。"); } } +void MainWindow::closeEvent(QCloseEvent *event) +{ + QMessageBox::StandardButton reply = QMessageBox::question(this, "确认关闭", "正在更新,是否确认关闭窗口?", QMessageBox::Yes | QMessageBox::No); +} MainWindow::~MainWindow() { delete ui; diff --git a/src/mainwindow.h b/src/mainwindow.h index d754b5f..b587750 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -22,6 +22,9 @@ public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); +protected: + void closeEvent(QCloseEvent *event) override; + private: Ui::MainWindow *ui; void checkUpdates(); From 6799dff2c5f4479e69ba83c47755b00f5676b365 Mon Sep 17 00:00:00 2001 From: momen Date: Sat, 13 Sep 2025 18:16:58 +0800 Subject: [PATCH 05/32] =?UTF-8?q?update:=E5=8A=A0=E5=85=A5=E6=A3=80?= =?UTF-8?q?=E6=B5=8B=E6=9B=B4=E6=96=B0=E4=BF=A1=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appdelegate.h | 1 + src/mainwindow.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/appdelegate.h b/src/appdelegate.h index 216d9be..579b873 100644 --- a/src/appdelegate.h +++ b/src/appdelegate.h @@ -31,6 +31,7 @@ public: signals: void updateDisplay(const QString &packageName); + void updateFinished(bool success); //传递是否完成更新 private slots: void updateSpinner(); // 新增槽函数 diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 466a730..2c43e9e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -264,7 +264,7 @@ void MainWindow::runAptssUpgrade() void MainWindow::closeEvent(QCloseEvent *event) { - QMessageBox::StandardButton reply = QMessageBox::question(this, "确认关闭", "正在更新,是否确认关闭窗口?", QMessageBox::Yes | QMessageBox::No); + QMessageBox::StandardButton reply = QMessageBox::question(this, "确认关闭", "正在更新,是否确认关闭窗口?", QMessageBox::Yes | QMessageBox::No); } MainWindow::~MainWindow() { From 95ee0f538130d199970acd4e8486b70f4bea507e Mon Sep 17 00:00:00 2001 From: momen Date: Sun, 14 Sep 2025 23:29:33 +0800 Subject: [PATCH 06/32] =?UTF-8?q?chore:=E6=B7=BB=E5=8A=A0=E6=A7=BD?= =?UTF-8?q?=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mainwindow.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mainwindow.h b/src/mainwindow.h index b587750..41002cd 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -24,7 +24,9 @@ public: protected: void closeEvent(QCloseEvent *event) override; - +private slots: + // ⬅️ 声明一个槽函数,它将接收来自Updater的信号 + void handleUpdateFinished(bool success); private: Ui::MainWindow *ui; void checkUpdates(); From 4d00b0bec8f9e31e5d27af7f71d45219ecba5230 Mon Sep 17 00:00:00 2001 From: momen Date: Sun, 21 Sep 2025 22:45:12 +0800 Subject: [PATCH 07/32] =?UTF-8?q?update:=E6=B7=BB=E5=8A=A0=E6=A7=BD?= =?UTF-8?q?=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mainwindow.cpp | 16 +++++++++++++++- src/mainwindow.h | 9 +++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 2c43e9e..b5ae7ed 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -266,7 +266,21 @@ void MainWindow::closeEvent(QCloseEvent *event) QMessageBox::StandardButton reply = QMessageBox::question(this, "确认关闭", "正在更新,是否确认关闭窗口?", QMessageBox::Yes | QMessageBox::No); } +void MainWindow::handleUpdateFinished(bool success) +{ + if (success) { + // 更新成功时的处理逻辑 + QMessageBox::information(this, "更新完成", "软件更新已成功完成!"); + } else { + // 更新失败时的处理逻辑 + QMessageBox::warning(this, "更新失败", "软件更新过程中出现错误,请稍后再试。"); + } + + // 刷新应用列表 + checkUpdates(); +} + MainWindow::~MainWindow() { delete ui; -} +} \ No newline at end of file diff --git a/src/mainwindow.h b/src/mainwindow.h index 41002cd..8f1b003 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -24,9 +24,7 @@ public: protected: void closeEvent(QCloseEvent *event) override; -private slots: - // ⬅️ 声明一个槽函数,它将接收来自Updater的信号 - void handleUpdateFinished(bool success); + private: Ui::MainWindow *ui; void checkUpdates(); @@ -37,5 +35,8 @@ private: QListView *listView; // 声明 QListView 指针 QJsonArray m_allApps; // 新增:保存所有应用数据 void filterAppsByKeyword(const QString &keyword); // 新增:搜索过滤函数声明 + +private slots: + void handleUpdateFinished(bool success); // 新增:处理更新完成的槽函数 }; -#endif // MAINWINDOW_H +#endif // MAINWINDOW_H \ No newline at end of file From 7387f8af95bf5dbc8d67fd6ae60561390e903fbc Mon Sep 17 00:00:00 2001 From: momen Date: Fri, 26 Sep 2025 23:55:56 +0800 Subject: [PATCH 08/32] =?UTF-8?q?chore:=E6=9B=B4=E6=96=B0=E6=97=A5?= =?UTF-8?q?=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- debian/changelog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/debian/changelog b/debian/changelog index b414fea..46eb85d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,8 @@ +spark-update-tool (1.0.1) unstable; urgency=low + + * 修复窗口调整大小时的错误 + + -- momen Wed, 18 Jun 2025 00:00:00 +0000 spark-update-tool (1.0.0) unstable; urgency=low * Initial release. From 975e3f6afbade409bf40722541bf5abe37ddb685 Mon Sep 17 00:00:00 2001 From: momen Date: Sun, 28 Sep 2025 14:32:26 +0800 Subject: [PATCH 09/32] =?UTF-8?q?update:=E6=9B=B4=E6=96=B0=E7=AA=97?= =?UTF-8?q?=E5=8F=A3=E5=A4=A7=E5=B0=8F=E6=94=B9=E4=B8=BA800*600?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mainwindow.ui | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mainwindow.ui b/src/mainwindow.ui index a53a4b8..8c2b025 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -6,8 +6,8 @@ 0 0 - 1440 - 858 + 800 + 600 @@ -253,4 +253,4 @@ - + \ No newline at end of file From b461729fa587f4714450e4494d023c0857d530dc Mon Sep 17 00:00:00 2001 From: momen Date: Sun, 28 Sep 2025 15:22:24 +0800 Subject: [PATCH 10/32] =?UTF-8?q?feat:=E6=B7=BB=E5=8A=A0=E5=A4=8D=E9=80=89?= =?UTF-8?q?=E6=A1=86=E8=AE=A9=E7=94=A8=E6=88=B7=E9=80=89=E6=8B=A9=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=E8=BD=AF=E4=BB=B6=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appdelegate.cpp | 67 +++++++++++++++++++++++++++++++++++++++++++-- src/appdelegate.h | 9 ++++++ src/mainwindow.cpp | 30 ++++++++++++++++++-- src/mainwindow.h | 2 ++ 4 files changed, 104 insertions(+), 4 deletions(-) diff --git a/src/appdelegate.cpp b/src/appdelegate.cpp index 7720994..8a58a46 100644 --- a/src/appdelegate.cpp +++ b/src/appdelegate.cpp @@ -51,6 +51,24 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c 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); + + // 绘制复选框边框 + painter->setPen(QColor("#888888")); + painter->setBrush(Qt::NoBrush); + painter->drawRect(checkboxRect); + + // 如果选中,绘制勾选标记 + if (isSelected) { + 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; @@ -65,7 +83,8 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c QRect rect = option.rect; int margin = 10, spacing = 6, iconSize = 40; - QRect iconRect(rect.left() + margin, rect.top() + (rect.height() - iconSize) / 2, iconSize, iconSize); + // 调整图标位置,为复选框留出空间 + QRect iconRect(rect.left() + 40, rect.top() + (rect.height() - iconSize) / 2, iconSize, iconSize); QIcon(iconPath).paint(painter, iconRect); int textX = iconRect.right() + margin; @@ -88,7 +107,6 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c 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; @@ -162,6 +180,18 @@ bool AppDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, QRect rect = option.rect; QString packageName = index.data(Qt::UserRole + 1).toString(); + // 检查是否点击了复选框 + 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())) { @@ -320,4 +350,37 @@ void AppDelegate::startNextInstall() { void AppDelegate::updateSpinner() { m_spinnerAngle = (m_spinnerAngle + 10) % 360; // 每次增加10度 emit updateDisplay(m_installingPackage); // 仅刷新当前正在安装的项 +} + +// 新增:更新选中应用的方法 +void AppDelegate::startDownloadForSelected() { + 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_selectedPackages.contains(packageName)) { + 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(); } \ No newline at end of file diff --git a/src/appdelegate.h b/src/appdelegate.h index 579b873..09d0ea6 100644 --- a/src/appdelegate.h +++ b/src/appdelegate.h @@ -28,6 +28,12 @@ public: bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override; void startDownloadForAll(); + void startDownloadForSelected(); + + // 复选框相关方法 + void setSelectedPackages(const QSet &selected); + QSet getSelectedPackages() const; + void clearSelection(); signals: void updateDisplay(const QString &packageName); @@ -40,6 +46,9 @@ private: DownloadManager *m_downloadManager; QHash m_downloads; QAbstractItemModel *m_model = nullptr; + + // 复选框相关成员变量 + QSet m_selectedPackages; QQueue m_installQueue; bool m_isInstalling = false; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index b5ae7ed..ed12d2b 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -55,9 +55,20 @@ MainWindow::MainWindow(QWidget *parent) // 新增:点击“更新全部”按钮批量下载 connect(ui->updatePushButton, &QPushButton::clicked, this, [=](){ - qDebug()<<"更新全部按钮被点击"; - m_delegate->startDownloadForAll(); + qDebug()<<"更新按钮被点击"; + if (m_delegate->getSelectedPackages().isEmpty()) { + // 没有选中任何应用,更新全部 + m_delegate->startDownloadForAll(); + } else { + // 有选中应用,更新选中 + m_delegate->startDownloadForSelected(); + m_delegate->clearSelection(); + updateButtonText(); + } }); + + // 新增:监听选择变化 + connect(m_delegate, &AppDelegate::updateDisplay, this, &MainWindow::handleSelectionChanged); checkUpdates(); // 新增:监听搜索框文本变化 @@ -283,4 +294,19 @@ void MainWindow::handleUpdateFinished(bool success) MainWindow::~MainWindow() { delete ui; +} + +// 新增:更新按钮文本 +void MainWindow::updateButtonText() { + int selectedCount = m_delegate->getSelectedPackages().size(); + if (selectedCount > 0) { + ui->updatePushButton->setText(QString("更新选中(%1)").arg(selectedCount)); + } else { + ui->updatePushButton->setText("更新全部"); + } +} + +// 新增:处理选择变化 +void MainWindow::handleSelectionChanged() { + updateButtonText(); } \ No newline at end of file diff --git a/src/mainwindow.h b/src/mainwindow.h index 8f1b003..7a44f7a 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -35,8 +35,10 @@ private: QListView *listView; // 声明 QListView 指针 QJsonArray m_allApps; // 新增:保存所有应用数据 void filterAppsByKeyword(const QString &keyword); // 新增:搜索过滤函数声明 + void updateButtonText(); // 新增:更新按钮文本 private slots: void handleUpdateFinished(bool success); // 新增:处理更新完成的槽函数 + void handleSelectionChanged(); // 新增:处理选择变化的槽函数 }; #endif // MAINWINDOW_H \ No newline at end of file From 0f82cc54d018e689f6b7afd259ddd823da80df5a Mon Sep 17 00:00:00 2001 From: momen Date: Mon, 29 Sep 2025 16:53:41 +0800 Subject: [PATCH 11/32] =?UTF-8?q?chore:=E6=9B=B4=E6=96=B0changelog?= =?UTF-8?q?=E4=B8=BA1.0.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index 46eb85d..9f4abb3 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +spark-update-tool (1.0.2) unstable; urgency=low + + * 添加复选框,选择多个包更新 + * 修复缩放问题 + + -- momen Mon, 29 Sep 2025 00:00:00 +0000 spark-update-tool (1.0.1) unstable; urgency=low * 修复窗口调整大小时的错误 From 7b26c6dd9c44e552ce518fc49cc179fe76582f1c Mon Sep 17 00:00:00 2001 From: momen Date: Tue, 30 Sep 2025 22:10:05 +0800 Subject: [PATCH 12/32] =?UTF-8?q?chore:=E6=B7=BB=E5=8A=A0=E5=BF=BD?= =?UTF-8?q?=E7=95=A5=E6=9B=B4=E6=96=B0=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 3 +- src/ignoreconfig.cpp | 102 +++++++++++++++++++++++++++++++++++++++++++ src/ignoreconfig.h | 39 +++++++++++++++++ 3 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 src/ignoreconfig.cpp create mode 100644 src/ignoreconfig.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c624b4..ab9cd9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) src/appdelegate.h src/appdelegate.cpp src/applistmodel.h src/applistmodel.cpp src/downloadmanager.h src/downloadmanager.cpp + src/ignoreconfig.h src/ignoreconfig.cpp ) # Define target properties for Android with Qt 6 as: # set_property(TARGET spark-update-tool APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR @@ -72,4 +73,4 @@ install(TARGETS spark-update-tool if(QT_VERSION_MAJOR EQUAL 6) qt_finalize_executable(spark-update-tool) -endif() +endif() \ No newline at end of file diff --git a/src/ignoreconfig.cpp b/src/ignoreconfig.cpp new file mode 100644 index 0000000..4bebe18 --- /dev/null +++ b/src/ignoreconfig.cpp @@ -0,0 +1,102 @@ +#include "ignoreconfig.h" +#include +#include +#include +#include +#include + +IgnoreConfig::IgnoreConfig(QObject *parent) + : QObject(parent) +{ + // 设置配置文件路径 + QString configDir = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); + QDir dir(configDir); + if (!dir.exists()) { + dir.mkpath("."); + } + m_configFilePath = dir.filePath("spark-update-tool/ignored_apps.conf"); + + // 确保目录存在 + QFileInfo fileInfo(m_configFilePath); + QDir configDirPath = fileInfo.dir(); + if (!configDirPath.exists()) { + configDirPath.mkpath("."); + } + + // 加载现有配置 + loadConfig(); +} + +void IgnoreConfig::addIgnoredApp(const QString &packageName, const QString &version) +{ + m_ignoredApps.insert(qMakePair(packageName, version)); + saveConfig(); +} + +void IgnoreConfig::removeIgnoredApp(const QString &packageName) +{ + // 移除所有该包名的版本 + auto it = m_ignoredApps.begin(); + while (it != m_ignoredApps.end()) { + if (it->first == packageName) { + it = m_ignoredApps.erase(it); + } else { + ++it; + } + } + saveConfig(); +} + +bool IgnoreConfig::isAppIgnored(const QString &packageName, const QString &version) const +{ + return m_ignoredApps.contains(qMakePair(packageName, version)); +} + +QSet> IgnoreConfig::getIgnoredApps() const +{ + return m_ignoredApps; +} + +bool IgnoreConfig::saveConfig() +{ + QFile file(m_configFilePath); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qDebug() << "无法打开配置文件进行写入:" << m_configFilePath; + return false; + } + + QTextStream out(&file); + for (const auto &app : m_ignoredApps) { + out << app.first << "|" << app.second << "\n"; + } + file.close(); + return true; +} + +bool IgnoreConfig::loadConfig() +{ + QFile file(m_configFilePath); + if (!file.exists()) { + // 配置文件不存在,这是正常的,返回true表示没有错误 + return true; + } + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qDebug() << "无法打开配置文件进行读取:" << m_configFilePath; + return false; + } + + m_ignoredApps.clear(); + QTextStream in(&file); + while (!in.atEnd()) { + QString line = in.readLine().trimmed(); + if (!line.isEmpty()) { + QStringList parts = line.split('|'); + if (parts.size() == 2) { + m_ignoredApps.insert(qMakePair(parts[0], parts[1])); + } + } + } + file.close(); + return true; +} \ No newline at end of file diff --git a/src/ignoreconfig.h b/src/ignoreconfig.h new file mode 100644 index 0000000..610fff5 --- /dev/null +++ b/src/ignoreconfig.h @@ -0,0 +1,39 @@ +#ifndef IGNORECONFIG_H +#define IGNORECONFIG_H + +#include +#include +#include +#include + +class IgnoreConfig : public QObject +{ + Q_OBJECT + +public: + explicit IgnoreConfig(QObject *parent = nullptr); + + // 添加忽略的应用(包名和版本号) + void addIgnoredApp(const QString &packageName, const QString &version); + + // 移除忽略的应用 + void removeIgnoredApp(const QString &packageName); + + // 检查应用是否被忽略 + bool isAppIgnored(const QString &packageName, const QString &version) const; + + // 获取所有被忽略的应用 + QSet> getIgnoredApps() const; + + // 保存配置到文件 + bool saveConfig(); + + // 从文件加载配置 + bool loadConfig(); + +private: + QSet> m_ignoredApps; + QString m_configFilePath; +}; + +#endif // IGNORECONFIG_H \ No newline at end of file From 2459224c7e68c2eb384aa104982635c98eb6a038 Mon Sep 17 00:00:00 2001 From: momen Date: Wed, 1 Oct 2025 22:18:56 +0800 Subject: [PATCH 13/32] =?UTF-8?q?chore:=E6=B7=BB=E5=8A=A0=E5=BF=BD?= =?UTF-8?q?=E7=95=A5=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appdelegate.cpp | 42 ++++++++++++++++++++++++++++++------------ src/appdelegate.h | 1 + src/mainwindow.cpp | 21 +++++++++++++++++++++ src/mainwindow.h | 3 +++ 4 files changed, 55 insertions(+), 12 deletions(-) diff --git a/src/appdelegate.cpp b/src/appdelegate.cpp index 8a58a46..e29ee8d 100644 --- a/src/appdelegate.cpp +++ b/src/appdelegate.cpp @@ -123,14 +123,14 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c progressBarOption.textVisible = true; QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter); - QRect buttonRect(rect.right() - 80, rect.top() + (rect.height() - 30) / 2, 70, 30); + QRect cancelButtonRect(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->drawRoundedRect(cancelButtonRect, 4, 4); painter->setPen(Qt::white); painter->setFont(option.font); - painter->drawText(buttonRect, Qt::AlignCenter, "取消"); + painter->drawText(cancelButtonRect, Qt::AlignCenter, "取消"); } else if (isInstalling) { QRect spinnerRect(option.rect.right() - 80, option.rect.top() + (option.rect.height() - 30) / 2, 30, 30); @@ -146,23 +146,32 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c 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); + // 绘制忽略按钮 + 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(buttonRect, 4, 4); + painter->drawRoundedRect(updateButtonRect, 4, 4); painter->setPen(Qt::white); - painter->drawText(buttonRect, Qt::AlignCenter, "已安装"); + painter->drawText(updateButtonRect, Qt::AlignCenter, "已安装"); } else if (m_downloads.contains(packageName) && !m_downloads[packageName].isDownloading) { painter->setBrush(QColor("#10B981")); - painter->drawRoundedRect(buttonRect, 4, 4); + painter->drawRoundedRect(updateButtonRect, 4, 4); painter->setPen(Qt::white); - painter->drawText(buttonRect, Qt::AlignCenter, "下载完成"); + painter->drawText(updateButtonRect, Qt::AlignCenter, "下载完成"); } else { painter->setBrush(QColor("#e9effd")); - painter->drawRoundedRect(buttonRect, 4, 4); + painter->drawRoundedRect(updateButtonRect, 4, 4); painter->setPen(QColor("#2563EB")); - painter->drawText(buttonRect, Qt::AlignCenter, "更新"); + painter->drawText(updateButtonRect, Qt::AlignCenter, "更新"); } } @@ -201,8 +210,17 @@ bool AppDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, return true; } } else { - QRect buttonRect(rect.right() - 80, rect.top() + (rect.height() - 30) / 2, 70, 30); - if (buttonRect.contains(mouseEvent->pos())) { + // 检查是否点击了忽略按钮 + 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; } diff --git a/src/appdelegate.h b/src/appdelegate.h index 09d0ea6..15972c1 100644 --- a/src/appdelegate.h +++ b/src/appdelegate.h @@ -38,6 +38,7 @@ public: signals: void updateDisplay(const QString &packageName); void updateFinished(bool success); //传递是否完成更新 + void ignoreApp(const QString &packageName, const QString &version); // 新增:忽略应用信号 private slots: void updateSpinner(); // 新增槽函数 diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index ed12d2b..aa3fa03 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -12,6 +12,7 @@ MainWindow::MainWindow(QWidget *parent) , ui(new Ui::MainWindow) , m_model(new AppListModel(this)) , m_delegate(new AppDelegate(this)) + , m_ignoreConfig(new IgnoreConfig(this)) { QIcon icon(":/resources/128*128/spark-update-tool.png"); setWindowIcon(icon); @@ -53,6 +54,9 @@ MainWindow::MainWindow(QWidget *parent) } }); + // 连接忽略应用信号 + connect(m_delegate, &AppDelegate::ignoreApp, this, &MainWindow::onIgnoreApp); + // 新增:点击“更新全部”按钮批量下载 connect(ui->updatePushButton, &QPushButton::clicked, this, [=](){ qDebug()<<"更新按钮被点击"; @@ -309,4 +313,21 @@ void MainWindow::updateButtonText() { // 新增:处理选择变化 void MainWindow::handleSelectionChanged() { updateButtonText(); +} + +// 新增:处理忽略应用的槽函数 +void MainWindow::onIgnoreApp(const QString &packageName, const QString &version) { + // 将应用添加到忽略配置中 + m_ignoreConfig->addIgnoredApp(packageName, version); + + // 从模型中移除被忽略的应用 + QJsonArray filteredApps; + for (const auto &item : m_allApps) { + QJsonObject obj = item.toObject(); + if (obj["package"].toString() != packageName) { + filteredApps.append(item); + } + } + m_allApps = filteredApps; + m_model->setUpdateData(filteredApps); } \ No newline at end of file diff --git a/src/mainwindow.h b/src/mainwindow.h index 7a44f7a..c00714b 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -5,6 +5,7 @@ #include "aptssupdater.h" #include "applistmodel.h" #include "appdelegate.h" +#include "ignoreconfig.h" #include #include // 添加头文件 #include @@ -32,6 +33,7 @@ private: void runAptssUpgrade(); AppListModel *m_model; AppDelegate *m_delegate; + IgnoreConfig *m_ignoreConfig; // 新增:忽略配置管理 QListView *listView; // 声明 QListView 指针 QJsonArray m_allApps; // 新增:保存所有应用数据 void filterAppsByKeyword(const QString &keyword); // 新增:搜索过滤函数声明 @@ -40,5 +42,6 @@ private: private slots: void handleUpdateFinished(bool success); // 新增:处理更新完成的槽函数 void handleSelectionChanged(); // 新增:处理选择变化的槽函数 + void onIgnoreApp(const QString &packageName, const QString &version); // 新增:处理忽略应用的槽函数 }; #endif // MAINWINDOW_H \ No newline at end of file From 3036749c693be7d18a62ae96842bd0b892366ba7 Mon Sep 17 00:00:00 2001 From: momen Date: Thu, 2 Oct 2025 23:57:10 +0800 Subject: [PATCH 14/32] =?UTF-8?q?chore:=E6=9B=B4=E6=96=B0changelog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- debian/changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/changelog b/debian/changelog index 9f4abb3..170f5ef 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,6 +2,7 @@ spark-update-tool (1.0.2) unstable; urgency=low * 添加复选框,选择多个包更新 * 修复缩放问题 + * 添加忽略应用功能 -- momen Mon, 29 Sep 2025 00:00:00 +0000 spark-update-tool (1.0.1) unstable; urgency=low From bd2a0ba7266280d88a59fff578842452d1655604 Mon Sep 17 00:00:00 2001 From: momen Date: Fri, 3 Oct 2025 23:50:52 +0800 Subject: [PATCH 15/32] =?UTF-8?q?chore:=E8=AF=BB=E5=8F=96=E5=BF=BD?= =?UTF-8?q?=E7=95=A5=E7=9A=84=E5=BA=94=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ignoreconfig.cpp | 31 ++++++++++++++++++++++++++++++- src/ignoreconfig.h | 3 +++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/ignoreconfig.cpp b/src/ignoreconfig.cpp index 4bebe18..53d0c75 100644 --- a/src/ignoreconfig.cpp +++ b/src/ignoreconfig.cpp @@ -4,12 +4,23 @@ #include #include #include +#include // for geteuid IgnoreConfig::IgnoreConfig(QObject *parent) : QObject(parent) { // 设置配置文件路径 - QString configDir = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); + QString configDir; + + // 检查是否以 root 权限运行 + if (geteuid() == 0) { + // 以 root 权限运行,使用 root 的配置目录 + configDir = "/root/.config"; + } else { + // 普通用户,使用标准配置目录 + configDir = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); + } + QDir dir(configDir); if (!dir.exists()) { dir.mkpath("."); @@ -25,6 +36,9 @@ IgnoreConfig::IgnoreConfig(QObject *parent) // 加载现有配置 loadConfig(); + + // 输出忽略列表到 qDebug + printIgnoredApps(); } void IgnoreConfig::addIgnoredApp(const QString &packageName, const QString &version) @@ -57,6 +71,21 @@ QSet> IgnoreConfig::getIgnoredApps() const return m_ignoredApps; } +void IgnoreConfig::printIgnoredApps() const +{ + qDebug() << "=== 忽略的应用列表 ==="; + qDebug() << "配置文件路径:" << m_configFilePath; + + if (m_ignoredApps.isEmpty()) { + qDebug() << "没有忽略的应用"; + } else { + for (const auto &app : m_ignoredApps) { + qDebug() << "忽略的应用:" << app.first << "版本:" << app.second; + } + } + qDebug() << "===================="; +} + bool IgnoreConfig::saveConfig() { QFile file(m_configFilePath); diff --git a/src/ignoreconfig.h b/src/ignoreconfig.h index 610fff5..2952eca 100644 --- a/src/ignoreconfig.h +++ b/src/ignoreconfig.h @@ -25,6 +25,9 @@ public: // 获取所有被忽略的应用 QSet> getIgnoredApps() const; + // 输出所有被忽略的应用到 qDebug + void printIgnoredApps() const; + // 保存配置到文件 bool saveConfig(); From a285c02cc9c49eb2bd3af7e68d272426c60b5be0 Mon Sep 17 00:00:00 2001 From: momen Date: Sat, 4 Oct 2025 23:49:32 +0800 Subject: [PATCH 16/32] =?UTF-8?q?chore:=E8=BE=93=E5=87=BA=E6=97=A5?= =?UTF-8?q?=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/aptssupdater.cpp | 26 +++++++++++++++++--------- src/mainwindow.cpp | 9 ++++++++- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/aptssupdater.cpp b/src/aptssupdater.cpp index c4bd906..60e697a 100644 --- a/src/aptssupdater.cpp +++ b/src/aptssupdater.cpp @@ -18,8 +18,9 @@ QStringList aptssUpdater::getUpdateablePackages() QString command = R"(env LANGUAGE=en_US /usr/bin/apt -c /opt/durapps/spark-store/bin/apt-fast-conf/aptss-apt.conf list --upgradable -o Dir::Etc::sourcelist="/opt/durapps/spark-store/bin/apt-fast-conf/sources.list.d/sparkstore.list" -o Dir::Etc::sourceparts="/dev/null" -o APT::Get::List-Cleanup="0" | awk 'NR>1')"; process.start("bash", QStringList() << "-c" << command); - if (!process.waitForFinished()) { - qWarning() << "Process failed to finish."; + if (!process.waitForFinished(30000)) { // 30秒超时 + qWarning() << "Process failed to finish within 30 seconds."; + process.kill(); return packageDetails; } @@ -83,8 +84,9 @@ QStringList aptssUpdater::getPackageSizes() "-o Dir::Etc::sourceparts=\"/dev/null\"").arg(packageName); process.start("bash", QStringList() << "-c" << command); - if (!process.waitForFinished()) { - qWarning() << "获取包信息失败:" << packageName; + if (!process.waitForFinished(30000)) { // 30秒超时 + qWarning() << "获取包信息失败:" << packageName << "(超时)"; + process.kill(); continue; } @@ -130,7 +132,11 @@ QStringList aptssUpdater::getDesktopAppNames() // 获取包文件列表 dpkgProcess.start("dpkg", QStringList() << "-L" << packageName); - dpkgProcess.waitForFinished(); + if (!dpkgProcess.waitForFinished(30000)) { // 30秒超时 + qWarning() << "获取包文件列表失败:" << packageName << "(超时)"; + dpkgProcess.kill(); + continue; + } QStringList files = QString(dpkgProcess.readAllStandardOutput()).split('\n', Qt::SkipEmptyParts); // 先检查常规应用目录 @@ -236,7 +242,11 @@ QStringList aptssUpdater::getPackageIcons() // 获取包文件列表 dpkgProcess.start("dpkg", QStringList() << "-L" << packageName); - dpkgProcess.waitForFinished(); + if (!dpkgProcess.waitForFinished(30000)) { // 30秒超时 + qWarning() << "获取包文件列表失败:" << packageName << "(超时)"; + dpkgProcess.kill(); + continue; + } QStringList files = QString(dpkgProcess.readAllStandardOutput()).split('\n', Qt::SkipEmptyParts); // 查找.desktop文件 @@ -393,6 +403,4 @@ QJsonArray aptssUpdater::getUpdateInfoAsJson() } qDebug()< Date: Sun, 5 Oct 2025 02:34:30 +0800 Subject: [PATCH 17/32] =?UTF-8?q?fix:root=E8=B4=A6=E6=88=B7=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=8D=A1=E6=AD=BB=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/aptssupdater.cpp | 9 ++++++--- src/mainwindow.cpp | 18 +++++++++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/aptssupdater.cpp b/src/aptssupdater.cpp index 60e697a..2ba3f33 100644 --- a/src/aptssupdater.cpp +++ b/src/aptssupdater.cpp @@ -69,7 +69,6 @@ QStringList aptssUpdater::getUpdateablePackages() QStringList aptssUpdater::getPackageSizes() { QStringList packageDetails; - QProcess process; // 获取可更新包名列表 QStringList updateablePackages; @@ -78,6 +77,8 @@ QStringList aptssUpdater::getPackageSizes() } foreach (const QString &packageName, updateablePackages) { + QProcess process; // 在循环内部创建新的QProcess实例 + // 构建新命令(包含包名参数) QString command = QString("apt download %1 --print-uris -c /opt/durapps/spark-store/bin/apt-fast-conf/aptss-apt.conf " "-o Dir::Etc::sourcelist=\"/opt/durapps/spark-store/bin/apt-fast-conf/sources.list.d/sparkstore.list\" " @@ -118,7 +119,6 @@ QStringList aptssUpdater::getPackageSizes() QStringList aptssUpdater::getDesktopAppNames() { QStringList appNames; - QProcess dpkgProcess; // 获取当前系统语言环境 QString lang = QLocale().name().replace("_", "-"); @@ -127,6 +127,8 @@ QStringList aptssUpdater::getDesktopAppNames() QStringList packages = packageName; foreach (const QString &package, packages) { + QProcess dpkgProcess; // 在循环内部创建新的QProcess实例 + QString packageName = package.split(":")[0]; QString finalName = packageName; // 默认使用包名 @@ -231,12 +233,13 @@ bool aptssUpdater::checkDesktopFiles(const QStringList &desktopFiles, QString &a QStringList aptssUpdater::getPackageIcons() { QStringList packageIcons; - QProcess dpkgProcess; // 遍历所有可更新包 QStringList packages = packageName; foreach (const QString &package, packages) { + QProcess dpkgProcess; // 在循环内部创建新的QProcess实例 + QString packageName = package.split(":")[0]; QString iconPath = ":/resources/default_icon.svg"; // 默认图标 diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 1b333ff..e2801ac 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -7,6 +7,7 @@ #include // 新增 #include #include +#include // for geteuid MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) @@ -262,11 +263,18 @@ void MainWindow::filterAppsByKeyword(const QString &keyword) void MainWindow::runAptssUpgrade() { QProcess process; - QStringList args; - args << "sudo" <<"aptss" << "ssupdate"; - process.start("sudo", args); + + // 检查是否已经是root用户,如果是则直接执行命令,否则使用sudo + if (geteuid() == 0) { + // root用户直接执行 + process.start("aptss", QStringList() << "ssupdate"); + } else { + // 非root用户使用sudo + process.start("sudo", QStringList() << "aptss" << "ssupdate"); + } + if (!process.waitForStarted(5000)) { - QMessageBox::warning(this, "升级失败", "无法启动 sudo aptss ssupdate"); + QMessageBox::warning(this, "升级失败", "无法启动 aptss ssupdate"); return; } process.write("n\n"); @@ -280,7 +288,7 @@ void MainWindow::runAptssUpgrade() } if (process.exitCode() != 0) { - QMessageBox::warning(this, "升级失败", "执行 sudo aptss ssupdate 失败,请检查系统环境或稍后再试。"); + QMessageBox::warning(this, "升级失败", "执行 aptss ssupdate 失败,请检查系统环境或稍后再试。"); } } void MainWindow::closeEvent(QCloseEvent *event) From 207d42153f38725dbd0a88fdc98ce6208ff934e6 Mon Sep 17 00:00:00 2001 From: momen Date: Sun, 5 Oct 2025 02:40:17 +0800 Subject: [PATCH 18/32] =?UTF-8?q?fix:root=E8=B4=A6=E6=88=B7=E4=B8=8B?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E6=AD=A3=E7=A1=AE=E8=8E=B7=E5=BE=97=E5=8C=85?= =?UTF-8?q?=E5=A4=A7=E5=B0=8F=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/aptssupdater.cpp | 51 +++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/src/aptssupdater.cpp b/src/aptssupdater.cpp index 2ba3f33..c6ddd7d 100644 --- a/src/aptssupdater.cpp +++ b/src/aptssupdater.cpp @@ -79,8 +79,14 @@ QStringList aptssUpdater::getPackageSizes() foreach (const QString &packageName, updateablePackages) { QProcess process; // 在循环内部创建新的QProcess实例 - // 构建新命令(包含包名参数) - QString command = QString("apt download %1 --print-uris -c /opt/durapps/spark-store/bin/apt-fast-conf/aptss-apt.conf " + // 设置环境变量确保在root权限下也能正确执行 + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert("HOME", "/tmp"); // 设置HOME环境变量 + env.insert("LANGUAGE", "en_US"); // 设置语言环境 + process.setProcessEnvironment(env); + + // 使用apt-cache show获取包信息,包括大小 + QString command = QString("apt-cache show %1 -c /opt/durapps/spark-store/bin/apt-fast-conf/aptss-apt.conf " "-o Dir::Etc::sourcelist=\"/opt/durapps/spark-store/bin/apt-fast-conf/sources.list.d/sparkstore.list\" " "-o Dir::Etc::sourceparts=\"/dev/null\"").arg(packageName); @@ -92,19 +98,38 @@ QStringList aptssUpdater::getPackageSizes() } QString output = process.readAllStandardOutput(); - // 使用正则匹配所有信息 - // 调整正则表达式匹配分组 - QRegularExpression regex(R"('([^']+)'\s+(\S+)\s+(\d+)\s+SHA512:([^\s]+))"); // 分组1:URL 分组2:文件名 分组3:大小 分组4:SHA512 - QRegularExpressionMatch match = regex.match(output); - - if (match.hasMatch()) { - QString url = match.captured(1); - QString fileName = match.captured(2); - QString size = match.captured(3); - QString sha512 = match.captured(4); - + QString errorOutput = process.readAllStandardError(); + + // 如果有错误输出,打印出来以便调试 + if (!errorOutput.isEmpty()) { + qWarning() << "apt-cache show命令错误输出:" << errorOutput; + } + + // 解析apt-cache show的输出获取包大小 + QStringList lines = output.split('\n'); + QString size; + QString filename; + QString sha512; + + for (const QString &line : lines) { + if (line.startsWith("Size:")) { + size = line.mid(6).trimmed(); // 跳过"Size:"前缀 + } else if (line.startsWith("Filename:")) { + filename = line.mid(10).trimmed(); // 跳过"Filename:"前缀 + } else if (line.startsWith("SHA512:")) { + sha512 = line.mid(8).trimmed(); // 跳过"SHA512:"前缀 + } + } + + // 构造下载URL + QString url = "http://sucdn.jerrywang.top/sparkstore/" + filename; + + // 如果获取到大小信息,则添加到结果中 + if (!size.isEmpty()) { // 调整字段顺序:包名 | 大小 | URL | SHA512 packageDetails << QString("%1: %2|%3|%4").arg(packageName, size, url, sha512); + } else { + qWarning() << "无法获取包大小信息:" << packageName; } } From c6b1bcf4a8fd6a677d153a37efa3b0f449962b45 Mon Sep 17 00:00:00 2001 From: momen Date: Mon, 6 Oct 2025 14:59:13 +0800 Subject: [PATCH 19/32] =?UTF-8?q?chore:=E6=99=AE=E9=80=9A=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=8F=90=E6=9D=83=E5=90=8E=E7=9A=84=E5=BF=BD=E7=95=A5?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=AD=98=E6=94=BE=E5=88=B0=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ignoreconfig.cpp | 21 +++++++++++++++++++-- src/main.cpp | 2 ++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/ignoreconfig.cpp b/src/ignoreconfig.cpp index 53d0c75..32089cd 100644 --- a/src/ignoreconfig.cpp +++ b/src/ignoreconfig.cpp @@ -14,8 +14,25 @@ IgnoreConfig::IgnoreConfig(QObject *parent) // 检查是否以 root 权限运行 if (geteuid() == 0) { - // 以 root 权限运行,使用 root 的配置目录 - configDir = "/root/.config"; + // 首先检查是否有 SUDO_USER_HOME 环境变量(表示是通过 pkexec 提权的普通用户) + QByteArray sudoUserHomeEnv = qgetenv("SUDO_USER_HOME"); + if (!sudoUserHomeEnv.isEmpty()) { + // 通过 pkexec 提权的普通用户,使用原用户的配置目录 + QString sudoUserHomePath = QString::fromLocal8Bit(sudoUserHomeEnv); + configDir = sudoUserHomePath + "/.config"; + } else { + // 获取实际的 HOME 目录来判断是真正的 root 用户还是其他方式提权的用户 + QByteArray homeEnv = qgetenv("HOME"); + QString homePath = QString::fromLocal8Bit(homeEnv); + + if (homePath == "/root") { + // 真正的 root 用户,使用 /root/.config + configDir = "/root/.config"; + } else { + // 其他方式提权的用户,使用 HOME 目录下的配置 + configDir = homePath + "/.config"; + } + } } else { // 普通用户,使用标准配置目录 configDir = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); diff --git a/src/main.cpp b/src/main.cpp index a3df5a4..f70a913 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,11 +16,13 @@ bool elevateToRoot() { QByteArray display = qgetenv("DISPLAY"); QByteArray xauthority = qgetenv("XAUTHORITY"); + QByteArray home = qgetenv("HOME"); // 获取原始用户的 HOME 目境变量 QStringList args; args << "env" << "DISPLAY=" + display << "XAUTHORITY=" + xauthority + << "SUDO_USER_HOME=" + home // 传递原始用户的 HOME 路径 << program; QProcess process; From 01d2a2f1d83e73c301a0d75efdb70385d68e0907 Mon Sep 17 00:00:00 2001 From: momen Date: Tue, 7 Oct 2025 16:52:56 +0800 Subject: [PATCH 20/32] =?UTF-8?q?update:=E5=85=81=E8=AE=B8=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E5=BF=BD=E7=95=A5=E5=BA=94=E7=94=A8=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appdelegate.cpp | 61 +++++++++++++++++++++++--- src/appdelegate.h | 1 + src/applistmodel.cpp | 14 +++++- src/applistmodel.h | 5 ++- src/mainwindow.cpp | 102 +++++++++++++++++++++++++++++++++++++------ src/mainwindow.h | 1 + 6 files changed, 161 insertions(+), 23 deletions(-) diff --git a/src/appdelegate.cpp b/src/appdelegate.cpp index e29ee8d..e51bff8 100644 --- a/src/appdelegate.cpp +++ b/src/appdelegate.cpp @@ -46,6 +46,9 @@ void AppDelegate::setModel(QAbstractItemModel *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 @@ -58,12 +61,13 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c QRect checkboxRect(option.rect.left() + 10, option.rect.top() + (option.rect.height() - 20) / 2, 20, 20); // 绘制复选框边框 - painter->setPen(QColor("#888888")); + QColor checkboxColor = isIgnored ? QColor("#CCCCCC") : QColor("#888888"); + painter->setPen(checkboxColor); painter->setBrush(Qt::NoBrush); painter->drawRect(checkboxRect); // 如果选中,绘制勾选标记 - if (isSelected) { + if (isSelected && !isIgnored) { painter->setPen(QPen(QColor("#2563EB"), 2)); painter->setBrush(QColor("#2563EB")); painter->drawRect(checkboxRect.adjusted(4, 4, -4, -4)); @@ -85,25 +89,42 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c // 调整图标位置,为复选框留出空间 QRect iconRect(rect.left() + 40, rect.top() + (rect.height() - iconSize) / 2, iconSize, iconSize); - QIcon(iconPath).paint(painter, iconRect); + + // 如果是忽略状态,绘制灰色图标 + 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); - painter->setPen(QColor("#333333")); + QColor nameColor = isIgnored ? QColor("#999999") : QColor("#333333"); + painter->setPen(nameColor); painter->drawText(nameRect, Qt::AlignLeft | Qt::AlignVCenter, name); QRect versionRect(textX, nameRect.bottom() + spacing, textWidth, 20); painter->setFont(normalFont); - painter->setPen(QColor("#888888")); + 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); - painter->setPen(QColor("#AAAAAA")); + 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))); @@ -112,7 +133,21 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c bool isInstalled = m_downloads.value(packageName).isInstalled; bool isInstalling = m_downloads.value(packageName).isInstalling; - if (isDownloading) { + // 如果是忽略状态,显示"已忽略"文本和"取消忽略"按钮 + 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); QStyleOptionProgressBar progressBarOption; progressBarOption.rect = progressRect; @@ -188,6 +223,18 @@ bool AppDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, 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); diff --git a/src/appdelegate.h b/src/appdelegate.h index 15972c1..9385a33 100644 --- a/src/appdelegate.h +++ b/src/appdelegate.h @@ -39,6 +39,7 @@ signals: void updateDisplay(const QString &packageName); void updateFinished(bool success); //传递是否完成更新 void ignoreApp(const QString &packageName, const QString &version); // 新增:忽略应用信号 + void unignoreApp(const QString &packageName); // 新增:取消忽略应用信号 private slots: void updateSpinner(); // 新增槽函数 diff --git a/src/applistmodel.cpp b/src/applistmodel.cpp index 31e244a..9d7c83d 100644 --- a/src/applistmodel.cpp +++ b/src/applistmodel.cpp @@ -32,6 +32,8 @@ QVariant AppListModel::data(const QModelIndex &index, int role) const return map.value("description"); case Qt::UserRole + 7: // 下载 URL return map.value("download_url"); // 返回下载 URL + case Qt::UserRole + 8: // 忽略状态 + return map.value("ignored"); default: return QVariant(); } @@ -52,11 +54,21 @@ void AppListModel::setUpdateData(const QJsonArray &updateInfo) map["icon"] = obj["icon"].toString(); map["size"] = obj["size"].toString(); map["download_url"] = obj["download_url"].toString(); // 确保设置下载 URL + map["ignored"] = obj["ignored"].toBool(); // 设置忽略状态 m_data.append(map); // 添加到 QList - qDebug() << "设置到模型的包名:" << map["package"].toString(); + qDebug() << "设置到模型的包名:" << map["package"].toString() << "忽略状态:" << map["ignored"].toBool(); qDebug() << "设置到模型的下载 URL:" << map["download_url"].toString(); // 检查设置的数据 } endResetModel(); } + +bool AppListModel::isAppIgnored(const QModelIndex &index) const +{ + if (!index.isValid() || index.row() >= m_data.size()) + return false; + + const QVariantMap &map = m_data.at(index.row()); + return map.value("ignored").toBool(); +} \ No newline at end of file diff --git a/src/applistmodel.h b/src/applistmodel.h index 9e5e79a..d3f304d 100644 --- a/src/applistmodel.h +++ b/src/applistmodel.h @@ -18,9 +18,12 @@ public: // 设置更新数据 void setUpdateData(const QJsonArray &data); + + // 获取忽略状态 + bool isAppIgnored(const QModelIndex &index) const; private: QList m_data; // 修改类型为 QList }; -#endif // APPLISTMODEL_H +#endif // APPLISTMODEL_H \ No newline at end of file diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index e2801ac..4e76371 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -55,8 +55,9 @@ MainWindow::MainWindow(QWidget *parent) } }); - // 连接忽略应用信号 - connect(m_delegate, &AppDelegate::ignoreApp, this, &MainWindow::onIgnoreApp); + // 连接应用委托的信号 + connect(m_delegate, &AppDelegate::ignoreApp, this, &MainWindow::onIgnoreApp); + connect(m_delegate, &AppDelegate::unignoreApp, this, &MainWindow::onUnignoreApp); // 新增:点击“更新全部”按钮批量下载 connect(ui->updatePushButton, &QPushButton::clicked, this, [=](){ @@ -229,12 +230,42 @@ void MainWindow::checkUpdates() { aptssUpdater updater; QJsonArray updateInfo = updater.getUpdateInfoAsJson(); - m_allApps = updateInfo; // 保存所有应用数据 - m_model->setUpdateData(updateInfo); - + + // 分离正常应用和忽略应用 + QJsonArray normalApps; + QJsonArray ignoredApps; + for (const auto &item : updateInfo) { QJsonObject obj = item.toObject(); - qDebug() << "模型设置的包名:" << obj["package"].toString(); + QString packageName = obj["package"].toString(); + QString currentVersion = obj["current_version"].toString(); + + // 检查应用是否被忽略 + if (m_ignoreConfig->isAppIgnored(packageName, currentVersion)) { + // 标记为忽略状态 + obj["ignored"] = true; + ignoredApps.append(obj); + } else { + obj["ignored"] = false; + normalApps.append(obj); + } + } + + // 合并数组:正常应用在前,忽略应用在后 + QJsonArray finalApps; + for (const auto &item : normalApps) { + finalApps.append(item); + } + for (const auto &item : ignoredApps) { + finalApps.append(item); + } + + m_allApps = finalApps; // 保存所有应用数据 + m_model->setUpdateData(finalApps); + + for (const auto &item : finalApps) { + QJsonObject obj = item.toObject(); + qDebug() << "模型设置的包名:" << obj["package"].toString() << "忽略状态:" << obj["ignored"].toBool(); qDebug() << "模型设置的下载 URL:" << obj["download_url"].toString(); // 检查模型数据 } } @@ -246,7 +277,11 @@ void MainWindow::filterAppsByKeyword(const QString &keyword) m_model->setUpdateData(m_allApps); return; } - QJsonArray filtered; + + // 分离正常应用和忽略应用 + QJsonArray normalApps; + QJsonArray ignoredApps; + for (const auto &item : m_allApps) { QJsonObject obj = item.toObject(); // 可根据需要匹配更多字段 @@ -254,9 +289,25 @@ void MainWindow::filterAppsByKeyword(const QString &keyword) QString package = obj.value("package").toString(); if (name.contains(keyword, Qt::CaseInsensitive) || package.contains(keyword, Qt::CaseInsensitive)) { - filtered.append(item); + + // 检查是否为忽略状态 + if (obj.value("ignored").toBool()) { + ignoredApps.append(item); + } else { + normalApps.append(item); + } } } + + // 合并数组:正常应用在前,忽略应用在后 + QJsonArray filtered; + for (const auto &item : normalApps) { + filtered.append(item); + } + for (const auto &item : ignoredApps) { + filtered.append(item); + } + m_model->setUpdateData(filtered); } @@ -335,14 +386,37 @@ void MainWindow::onIgnoreApp(const QString &packageName, const QString &version) // 将应用添加到忽略配置中 m_ignoreConfig->addIgnoredApp(packageName, version); - // 从模型中移除被忽略的应用 - QJsonArray filteredApps; + // 更新模型中应用的状态,而不是移除 + QJsonArray updatedApps; for (const auto &item : m_allApps) { QJsonObject obj = item.toObject(); - if (obj["package"].toString() != packageName) { - filteredApps.append(item); + if (obj["package"].toString() == packageName) { + obj["ignored"] = true; // 标记为忽略状态 } + updatedApps.append(obj); } - m_allApps = filteredApps; - m_model->setUpdateData(filteredApps); + m_allApps = updatedApps; + + // 重新排序:正常应用在前,忽略应用在后 + checkUpdates(); +} + +// 新增:处理取消忽略应用的槽函数 +void MainWindow::onUnignoreApp(const QString &packageName) { + // 从忽略配置中移除应用 + m_ignoreConfig->removeIgnoredApp(packageName); + + // 更新模型中应用的状态 + QJsonArray updatedApps; + for (const auto &item : m_allApps) { + QJsonObject obj = item.toObject(); + if (obj["package"].toString() == packageName) { + obj["ignored"] = false; // 标记为非忽略状态 + } + updatedApps.append(obj); + } + m_allApps = updatedApps; + + // 重新排序:正常应用在前,忽略应用在后 + checkUpdates(); } \ No newline at end of file diff --git a/src/mainwindow.h b/src/mainwindow.h index c00714b..5bf2f0a 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -43,5 +43,6 @@ private slots: void handleUpdateFinished(bool success); // 新增:处理更新完成的槽函数 void handleSelectionChanged(); // 新增:处理选择变化的槽函数 void onIgnoreApp(const QString &packageName, const QString &version); // 新增:处理忽略应用的槽函数 + void onUnignoreApp(const QString &packageName); // 新增:处理取消忽略应用 }; #endif // MAINWINDOW_H \ No newline at end of file From 47dea9b3886f96cfffc7a5efd3204676b933f98c Mon Sep 17 00:00:00 2001 From: momen Date: Thu, 9 Oct 2025 16:06:16 +0800 Subject: [PATCH 21/32] =?UTF-8?q?chore:=E5=9B=9E=E9=80=80=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E5=9C=B0=E5=9D=80=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/aptssupdater.cpp | 51 +++++++++++--------------------------------- 1 file changed, 13 insertions(+), 38 deletions(-) diff --git a/src/aptssupdater.cpp b/src/aptssupdater.cpp index c6ddd7d..2ba3f33 100644 --- a/src/aptssupdater.cpp +++ b/src/aptssupdater.cpp @@ -79,14 +79,8 @@ QStringList aptssUpdater::getPackageSizes() foreach (const QString &packageName, updateablePackages) { QProcess process; // 在循环内部创建新的QProcess实例 - // 设置环境变量确保在root权限下也能正确执行 - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - env.insert("HOME", "/tmp"); // 设置HOME环境变量 - env.insert("LANGUAGE", "en_US"); // 设置语言环境 - process.setProcessEnvironment(env); - - // 使用apt-cache show获取包信息,包括大小 - QString command = QString("apt-cache show %1 -c /opt/durapps/spark-store/bin/apt-fast-conf/aptss-apt.conf " + // 构建新命令(包含包名参数) + QString command = QString("apt download %1 --print-uris -c /opt/durapps/spark-store/bin/apt-fast-conf/aptss-apt.conf " "-o Dir::Etc::sourcelist=\"/opt/durapps/spark-store/bin/apt-fast-conf/sources.list.d/sparkstore.list\" " "-o Dir::Etc::sourceparts=\"/dev/null\"").arg(packageName); @@ -98,38 +92,19 @@ QStringList aptssUpdater::getPackageSizes() } QString output = process.readAllStandardOutput(); - QString errorOutput = process.readAllStandardError(); - - // 如果有错误输出,打印出来以便调试 - if (!errorOutput.isEmpty()) { - qWarning() << "apt-cache show命令错误输出:" << errorOutput; - } - - // 解析apt-cache show的输出获取包大小 - QStringList lines = output.split('\n'); - QString size; - QString filename; - QString sha512; - - for (const QString &line : lines) { - if (line.startsWith("Size:")) { - size = line.mid(6).trimmed(); // 跳过"Size:"前缀 - } else if (line.startsWith("Filename:")) { - filename = line.mid(10).trimmed(); // 跳过"Filename:"前缀 - } else if (line.startsWith("SHA512:")) { - sha512 = line.mid(8).trimmed(); // 跳过"SHA512:"前缀 - } - } - - // 构造下载URL - QString url = "http://sucdn.jerrywang.top/sparkstore/" + filename; - - // 如果获取到大小信息,则添加到结果中 - if (!size.isEmpty()) { + // 使用正则匹配所有信息 + // 调整正则表达式匹配分组 + QRegularExpression regex(R"('([^']+)'\s+(\S+)\s+(\d+)\s+SHA512:([^\s]+))"); // 分组1:URL 分组2:文件名 分组3:大小 分组4:SHA512 + QRegularExpressionMatch match = regex.match(output); + + if (match.hasMatch()) { + QString url = match.captured(1); + QString fileName = match.captured(2); + QString size = match.captured(3); + QString sha512 = match.captured(4); + // 调整字段顺序:包名 | 大小 | URL | SHA512 packageDetails << QString("%1: %2|%3|%4").arg(packageName, size, url, sha512); - } else { - qWarning() << "无法获取包大小信息:" << packageName; } } From 89fb73ae0cafa323406bca22cb3b0f6f95083713 Mon Sep 17 00:00:00 2001 From: momen Date: Fri, 10 Oct 2025 11:03:21 +0800 Subject: [PATCH 22/32] =?UTF-8?q?update:=E6=9B=B4=E6=96=B0=E6=9E=84?= =?UTF-8?q?=E5=BB=BA=E5=8C=85=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 17 +++++++++++------ debian/compat | 2 +- debian/rules | 24 ++++++++++++++++++++++-- src/appdelegate.h | 3 ++- 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ab9cd9c..cb94870 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,18 +17,23 @@ set(PROJECT_SOURCES src/mainwindow.cpp src/mainwindow.h src/mainwindow.ui + src/aptssupdater.h + src/aptssupdater.cpp + src/icons.qrc + src/appdelegate.h + src/appdelegate.cpp + src/applistmodel.h + src/applistmodel.cpp + src/downloadmanager.h + src/downloadmanager.cpp + src/ignoreconfig.h + src/ignoreconfig.cpp ) if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) qt_add_executable(spark-update-tool MANUAL_FINALIZATION ${PROJECT_SOURCES} - src/aptssupdater.h src/aptssupdater.cpp - src/icons.qrc - src/appdelegate.h src/appdelegate.cpp - src/applistmodel.h src/applistmodel.cpp - src/downloadmanager.h src/downloadmanager.cpp - src/ignoreconfig.h src/ignoreconfig.cpp ) # Define target properties for Android with Qt 6 as: # set_property(TARGET spark-update-tool APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR diff --git a/debian/compat b/debian/compat index f11c82a..ca7bf83 100644 --- a/debian/compat +++ b/debian/compat @@ -1 +1 @@ -9 \ No newline at end of file +13 \ No newline at end of file diff --git a/debian/rules b/debian/rules index 1159ac4..d9cedc7 100755 --- a/debian/rules +++ b/debian/rules @@ -1,7 +1,27 @@ #!/usr/bin/make -f -%: - dh $@ +# 声明兼容性级别 +export DH_VERBOSE=1 +%: + dh $@ --buildsystem=cmake + +# 确保使用CMake进行配置 override_dh_auto_configure: dh_auto_configure -- -DCMAKE_INSTALL_PREFIX=/usr + +# 确保使用CMake进行构建 +override_dh_auto_build: + dh_auto_build + +# 确保使用CMake进行安装 +override_dh_auto_install: + dh_auto_install + +# 确保使用CMake进行清理 +override_dh_auto_clean: + dh_auto_clean + +# 确保使用CMake进行依赖解析 +override_dh_shlibdeps: + dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info \ No newline at end of file diff --git a/src/appdelegate.h b/src/appdelegate.h index 9385a33..bedd645 100644 --- a/src/appdelegate.h +++ b/src/appdelegate.h @@ -5,7 +5,8 @@ #include #include #include -#include +#include +#include #include "downloadmanager.h" From 8f240db798ebb6a94f888b355aed484a8480b280 Mon Sep 17 00:00:00 2001 From: momen Date: Sat, 11 Oct 2025 16:57:39 +0800 Subject: [PATCH 23/32] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E5=85=B3=E9=97=AD?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=E5=BC=B9=E7=AA=97=E6=97=B6=E6=9C=BA=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appdelegate.cpp | 5 +++++ src/appdelegate.h | 4 ++++ src/mainwindow.cpp | 27 +++++++++++++++++++++++++-- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/appdelegate.cpp b/src/appdelegate.cpp index e51bff8..c9e4540 100644 --- a/src/appdelegate.cpp +++ b/src/appdelegate.cpp @@ -448,4 +448,9 @@ QSet AppDelegate::getSelectedPackages() const { void AppDelegate::clearSelection() { m_selectedPackages.clear(); +} + +// 实现获取下载状态信息的方法 +const QHash& AppDelegate::getDownloads() const { + return m_downloads; } \ No newline at end of file diff --git a/src/appdelegate.h b/src/appdelegate.h index bedd645..25ea337 100644 --- a/src/appdelegate.h +++ b/src/appdelegate.h @@ -35,6 +35,10 @@ public: void setSelectedPackages(const QSet &selected); QSet getSelectedPackages() const; void clearSelection(); + + // 获取下载状态信息 + const QHash& getDownloads() const; + signals: void updateDisplay(const QString &packageName); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 4e76371..c93f09a 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -344,8 +344,31 @@ void MainWindow::runAptssUpgrade() } void MainWindow::closeEvent(QCloseEvent *event) { - - QMessageBox::StandardButton reply = QMessageBox::question(this, "确认关闭", "正在更新,是否确认关闭窗口?", QMessageBox::Yes | QMessageBox::No); + // 检查是否正在进行更新 + bool isUpdating = false; + + // 通过AppDelegate检查是否有正在下载或安装的应用 + const QHash& downloads = m_delegate->getDownloads(); + for (auto it = downloads.constBegin(); it != downloads.constEnd(); ++it) { + if (it.value().isDownloading || it.value().isInstalling) { + isUpdating = true; + break; + } + } + + // 如果正在更新,才显示确认对话框 + if (isUpdating) { + QMessageBox::StandardButton reply = QMessageBox::question(this, "确认关闭", "正在更新,是否确认关闭窗口?", QMessageBox::Yes | QMessageBox::No); + + if (reply == QMessageBox::Yes) { + event->accept(); + } else { + event->ignore(); + } + } else { + // 如果没有更新,直接关闭窗口 + event->accept(); + } } void MainWindow::handleUpdateFinished(bool success) { From 35f34dd46992ed14a961ad1d638bb9d43ce42be9 Mon Sep 17 00:00:00 2001 From: momen Date: Sun, 12 Oct 2025 11:05:33 +0800 Subject: [PATCH 24/32] fix: Can't do app upgrade in mint by shenmo --- src/aptssupdater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aptssupdater.cpp b/src/aptssupdater.cpp index 2ba3f33..44b3110 100644 --- a/src/aptssupdater.cpp +++ b/src/aptssupdater.cpp @@ -80,7 +80,7 @@ QStringList aptssUpdater::getPackageSizes() QProcess process; // 在循环内部创建新的QProcess实例 // 构建新命令(包含包名参数) - QString command = QString("apt download %1 --print-uris -c /opt/durapps/spark-store/bin/apt-fast-conf/aptss-apt.conf " + QString command = QString("/usr/bin/apt download %1 --print-uris -c /opt/durapps/spark-store/bin/apt-fast-conf/aptss-apt.conf " "-o Dir::Etc::sourcelist=\"/opt/durapps/spark-store/bin/apt-fast-conf/sources.list.d/sparkstore.list\" " "-o Dir::Etc::sourceparts=\"/dev/null\"").arg(packageName); From b157f134d4ca45a43ac047ff510769b97147071e Mon Sep 17 00:00:00 2001 From: momen Date: Mon, 13 Oct 2025 16:58:00 +0800 Subject: [PATCH 25/32] =?UTF-8?q?chore:=E6=9B=B4=E6=96=B0=E8=BD=AF?= =?UTF-8?q?=E4=BB=B6=E5=90=8E=EF=BC=8C=E5=88=A0=E9=99=A4deb=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appdelegate.cpp | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/appdelegate.cpp b/src/appdelegate.cpp index c9e4540..c9f851e 100644 --- a/src/appdelegate.cpp +++ b/src/appdelegate.cpp @@ -369,11 +369,20 @@ void AppDelegate::startNextInstall() { qDebug().noquote() << QString::fromLocal8Bit(err); }); connect(m_installProcess, QOverload::of(&QProcess::finished), - this, [this, packageName, logFile](int exitCode, QProcess::ExitStatus status) { + this, [this, packageName, logFile, debPath](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; + } + } } emit updateDisplay(packageName); m_installProcess->deleteLater(); @@ -382,13 +391,23 @@ void AppDelegate::startNextInstall() { startNextInstall(); }); } else { - connect(m_installProcess, &QProcess::readyReadStandardOutput, this, [this, packageName]() { + 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); } }); @@ -397,7 +416,16 @@ void AppDelegate::startNextInstall() { qDebug().noquote() << QString::fromLocal8Bit(err); }); connect(m_installProcess, QOverload::of(&QProcess::finished), - this, [this, packageName](int /*exitCode*/, QProcess::ExitStatus /*status*/) { + this, [this, packageName, debPath](int exitCode, QProcess::ExitStatus /*status*/) { + // 如果通过退出码判断安装成功,也删除deb包 + if (exitCode == 0 && QFile::exists(debPath)) { + if (QFile::remove(debPath)) { + qDebug() << "已删除deb包:" << debPath; + } else { + qWarning() << "删除deb包失败:" << debPath; + } + } + emit updateDisplay(packageName); m_installProcess->deleteLater(); m_installProcess = nullptr; From 5754b3cfc2113d2490b5f0502891e377f3a18581 Mon Sep 17 00:00:00 2001 From: momen Date: Tue, 14 Oct 2025 11:57:40 +0800 Subject: [PATCH 26/32] =?UTF-8?q?fix:=E6=97=A0icon=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E9=BB=98=E8=AE=A4=E5=9B=BE=E6=A0=87=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appdelegate.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/appdelegate.cpp b/src/appdelegate.cpp index c9f851e..fcaf2ac 100644 --- a/src/appdelegate.cpp +++ b/src/appdelegate.cpp @@ -90,10 +90,16 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c // 调整图标位置,为复选框留出空间 QRect iconRect(rect.left() + 40, rect.top() + (rect.height() - iconSize) / 2, iconSize, iconSize); + // 检查图标路径是否存在,如果不存在则使用默认图标 + QString finalIconPath = iconPath; + if (iconPath.isEmpty() || !QFile::exists(iconPath)) { + finalIconPath = ":/resources/default_icon.svg"; + } + // 如果是忽略状态,绘制灰色图标 if (isIgnored) { // 创建灰度效果 - QPixmap originalPixmap = QIcon(iconPath).pixmap(iconSize, iconSize); + QPixmap originalPixmap = QIcon(finalIconPath).pixmap(iconSize, iconSize); QPixmap grayPixmap(originalPixmap.size()); grayPixmap.fill(Qt::transparent); QPainter grayPainter(&grayPixmap); @@ -102,7 +108,7 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c grayPainter.end(); painter->drawPixmap(iconRect, grayPixmap); } else { - QIcon(iconPath).paint(painter, iconRect); + QIcon(finalIconPath).paint(painter, iconRect); } int textX = iconRect.right() + margin; From e8ea4ed1a99dbe2791dd010d57ed3b9973d88ddf Mon Sep 17 00:00:00 2001 From: momen Date: Wed, 15 Oct 2025 17:03:55 +0800 Subject: [PATCH 27/32] =?UTF-8?q?feat:=E5=BD=93/tmp=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=E4=B8=8B=E5=AD=98=E5=9C=A8=E5=8C=85=E6=97=B6=EF=BC=8C=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E5=AE=89=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appdelegate.cpp | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/appdelegate.cpp b/src/appdelegate.cpp index fcaf2ac..61f9136 100644 --- a/src/appdelegate.cpp +++ b/src/appdelegate.cpp @@ -277,12 +277,33 @@ bool AppDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, 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); + + // 检查/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包,直接进行安装 + if (!debPath.isEmpty() && QFile::exists(debPath)) { + qDebug() << "发现已存在的deb包,直接进行安装:" << debPath; + enqueueInstall(packageName); + } else { + // 否则触发下载流程 + 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); + m_downloads[packageName] = {0, true}; + m_downloadManager->startDownload(packageName, downloadUrl, outputPath); + emit updateDisplay(packageName); + } return true; } } From 585d0582b88bca8e1cefc740ac40217382933953 Mon Sep 17 00:00:00 2001 From: momen Date: Thu, 16 Oct 2025 17:00:42 +0800 Subject: [PATCH 28/32] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E6=97=A0icon?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E5=8A=A0=E8=BD=BD=E9=BB=98=E8=AE=A4=E5=9B=BE?= =?UTF-8?q?=E6=A0=87=E5=A4=B1=E6=95=88=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appdelegate.cpp | 10 ++++++++++ src/aptssupdater.cpp | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/src/appdelegate.cpp b/src/appdelegate.cpp index 61f9136..83649e8 100644 --- a/src/appdelegate.cpp +++ b/src/appdelegate.cpp @@ -92,8 +92,18 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c // 检查图标路径是否存在,如果不存在则使用默认图标 QString finalIconPath = iconPath; + qDebug() << "原始图标路径:" << iconPath; if (iconPath.isEmpty() || !QFile::exists(iconPath)) { finalIconPath = ":/resources/default_icon.svg"; + qDebug() << "图标文件不存在,使用默认图标:" << finalIconPath; + } else { + qDebug() << "使用图标文件:" << finalIconPath; + } + + // 额外检查资源文件是否存在 + if (finalIconPath.startsWith(":/") && QIcon(finalIconPath).isNull()) { + qDebug() << "资源图标无法加载,使用备用默认图标"; + finalIconPath = ":/resources/default_icon.svg"; } // 如果是忽略状态,绘制灰色图标 diff --git a/src/aptssupdater.cpp b/src/aptssupdater.cpp index 44b3110..3a5b5bb 100644 --- a/src/aptssupdater.cpp +++ b/src/aptssupdater.cpp @@ -248,6 +248,7 @@ QStringList aptssUpdater::getPackageIcons() if (!dpkgProcess.waitForFinished(30000)) { // 30秒超时 qWarning() << "获取包文件列表失败:" << packageName << "(超时)"; dpkgProcess.kill(); + packageIcons << QString("%1: %2").arg(packageName, iconPath); continue; } QStringList files = QString(dpkgProcess.readAllStandardOutput()).split('\n', Qt::SkipEmptyParts); @@ -278,6 +279,7 @@ QStringList aptssUpdater::getPackageIcons() foreach (const QString &path, iconPaths) { if (QFile::exists(path)) { iconPath = path; + qDebug() << "找到图标文件:" << path; break; } } @@ -285,6 +287,7 @@ QStringList aptssUpdater::getPackageIcons() // 已经是绝对路径 if (QFile::exists(iconName)) { iconPath = iconName; + qDebug() << "使用绝对路径图标文件:" << iconName; } } break; @@ -296,9 +299,13 @@ QStringList aptssUpdater::getPackageIcons() // 如果.desktop中没有找到图标,尝试直接查找包中的图标文件 if (iconPath == ":/resources/default_icon.svg") { + qDebug() << "未在.desktop文件中找到图标,尝试直接查找包中的图标文件"; QStringList iconFiles = files.filter(QRegularExpression("/(usr/share/pixmaps|usr/share/icons|opt/apps/.*/entries/icons)/.*\\.(png|svg)$")); if (!iconFiles.isEmpty()) { iconPath = iconFiles.first(); + qDebug() << "从包中找到图标文件:" << iconPath; + } else { + qDebug() << "未在包中找到图标文件,使用默认图标"; } } From 8004cea8be01fa63545e5952ab80eca25304432c Mon Sep 17 00:00:00 2001 From: momen Date: Fri, 17 Oct 2025 12:25:11 +0000 Subject: [PATCH 29/32] update debian/changelog. Signed-off-by: momen --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index 170f5ef..01eb32d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +spark-update-tool (1.0.3) unstable; urgency=low + + * 修复默认图标加载失败的问题 + * 修复更新器在安装阶段强制关闭窗口后再次更新无法安装软件包的问题。 + + -- momen Fri, 17 Oct 2025 00:00:00 +0000 spark-update-tool (1.0.2) unstable; urgency=low * 添加复选框,选择多个包更新 From 1b722e78a5d81ed17ca48ddda4c8f914d9c0ef16 Mon Sep 17 00:00:00 2001 From: momen Date: Mon, 10 Nov 2025 10:49:56 +0800 Subject: [PATCH 30/32] =?UTF-8?q?update:=E6=B7=BB=E5=8A=A0png=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/default_icon.png | Bin 0 -> 100127 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/default_icon.png diff --git a/resources/default_icon.png b/resources/default_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..bffa3792fe5a1fa3ac39f465d2e3ca18732ede1d GIT binary patch literal 100127 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 literal 0 HcmV?d00001 From c3cd29bb66fefba0a59832a18a3878f8d3f32fc9 Mon Sep 17 00:00:00 2001 From: momen Date: Mon, 10 Nov 2025 10:55:44 +0800 Subject: [PATCH 31/32] =?UTF-8?q?update:=E4=BF=AE=E5=A4=8D=E8=B0=83?= =?UTF-8?q?=E7=94=A8=E9=BB=98=E8=AE=A4svg=E5=87=BA=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appdelegate.cpp | 4 ++-- src/aptssupdater.cpp | 4 ++-- src/icons.qrc | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/appdelegate.cpp b/src/appdelegate.cpp index 83649e8..d4b70f0 100644 --- a/src/appdelegate.cpp +++ b/src/appdelegate.cpp @@ -94,7 +94,7 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c QString finalIconPath = iconPath; qDebug() << "原始图标路径:" << iconPath; if (iconPath.isEmpty() || !QFile::exists(iconPath)) { - finalIconPath = ":/resources/default_icon.svg"; + finalIconPath = ":/resources/default_icon.png"; qDebug() << "图标文件不存在,使用默认图标:" << finalIconPath; } else { qDebug() << "使用图标文件:" << finalIconPath; @@ -103,7 +103,7 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c // 额外检查资源文件是否存在 if (finalIconPath.startsWith(":/") && QIcon(finalIconPath).isNull()) { qDebug() << "资源图标无法加载,使用备用默认图标"; - finalIconPath = ":/resources/default_icon.svg"; + finalIconPath = ":/resources/default_icon.png"; } // 如果是忽略状态,绘制灰色图标 diff --git a/src/aptssupdater.cpp b/src/aptssupdater.cpp index 3a5b5bb..8dcde53 100644 --- a/src/aptssupdater.cpp +++ b/src/aptssupdater.cpp @@ -241,7 +241,7 @@ QStringList aptssUpdater::getPackageIcons() QProcess dpkgProcess; // 在循环内部创建新的QProcess实例 QString packageName = package.split(":")[0]; - QString iconPath = ":/resources/default_icon.svg"; // 默认图标 + QString iconPath = ":/resources/default_icon.png"; // 默认图标 // 获取包文件列表 dpkgProcess.start("dpkg", QStringList() << "-L" << packageName); @@ -298,7 +298,7 @@ QStringList aptssUpdater::getPackageIcons() } // 如果.desktop中没有找到图标,尝试直接查找包中的图标文件 - if (iconPath == ":/resources/default_icon.svg") { + if (iconPath == ":/resources/default_icon.png") { qDebug() << "未在.desktop文件中找到图标,尝试直接查找包中的图标文件"; QStringList iconFiles = files.filter(QRegularExpression("/(usr/share/pixmaps|usr/share/icons|opt/apps/.*/entries/icons)/.*\\.(png|svg)$")); if (!iconFiles.isEmpty()) { diff --git a/src/icons.qrc b/src/icons.qrc index f81e1f8..9bbc9d3 100644 --- a/src/icons.qrc +++ b/src/icons.qrc @@ -4,5 +4,6 @@ ../resources/default_icon.svg ../resources/spark-update-tool.svg ../resources/128*128/spark-update-tool.png + ../resources/default_icon.png From 4f73289602742a7d0ed7a54f50dfef9d538dccb5 Mon Sep 17 00:00:00 2001 From: momen Date: Mon, 10 Nov 2025 11:03:12 +0800 Subject: [PATCH 32/32] =?UTF-8?q?update:=E6=9B=B4=E6=AD=A3=E4=B9=8B?= =?UTF-8?q?=E5=89=8D=E6=96=B0=E5=A2=9E=E7=9A=84=E5=8A=A0=E8=BD=BD=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=9B=BE=E6=A0=87=E4=BB=A3=E7=A0=81=EF=BC=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appdelegate.cpp | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/src/appdelegate.cpp b/src/appdelegate.cpp index d4b70f0..f80ed05 100644 --- a/src/appdelegate.cpp +++ b/src/appdelegate.cpp @@ -90,26 +90,11 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c // 调整图标位置,为复选框留出空间 QRect iconRect(rect.left() + 40, rect.top() + (rect.height() - iconSize) / 2, iconSize, iconSize); - // 检查图标路径是否存在,如果不存在则使用默认图标 - QString finalIconPath = iconPath; - qDebug() << "原始图标路径:" << iconPath; - if (iconPath.isEmpty() || !QFile::exists(iconPath)) { - finalIconPath = ":/resources/default_icon.png"; - qDebug() << "图标文件不存在,使用默认图标:" << finalIconPath; - } else { - qDebug() << "使用图标文件:" << finalIconPath; - } - - // 额外检查资源文件是否存在 - if (finalIconPath.startsWith(":/") && QIcon(finalIconPath).isNull()) { - qDebug() << "资源图标无法加载,使用备用默认图标"; - finalIconPath = ":/resources/default_icon.png"; - } // 如果是忽略状态,绘制灰色图标 if (isIgnored) { // 创建灰度效果 - QPixmap originalPixmap = QIcon(finalIconPath).pixmap(iconSize, iconSize); + QPixmap originalPixmap = QIcon(iconPath).pixmap(iconSize, iconSize); QPixmap grayPixmap(originalPixmap.size()); grayPixmap.fill(Qt::transparent); QPainter grayPainter(&grayPixmap); @@ -118,7 +103,7 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c grayPainter.end(); painter->drawPixmap(iconRect, grayPixmap); } else { - QIcon(finalIconPath).paint(painter, iconRect); + QIcon(iconPath).paint(painter, iconRect); } int textX = iconRect.right() + margin;