From 94dc22819fa28dce9155baea0f09d306adc9478e Mon Sep 17 00:00:00 2001 From: KenanZhu <3471685733@qq.com> Date: Tue, 17 Mar 2026 14:51:55 +0800 Subject: [PATCH] =?UTF-8?q?optimize(gui):=20=E4=BC=98=E5=8C=96=E5=AE=9A?= =?UTF-8?q?=E6=97=B6=E4=BB=BB=E5=8A=A1=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 优化任务历史查看对话框的界面布局和交互体验 - 新增任务状态枚举值以支持更完整的状态管理 - 统一重复任务执行后的历史记录处理逻辑 - 增强删除任务时的确认机制,删除可重复任务前展示详细执行记录 - 完善批量清除任务的验证流程,检查运行中任务并确认重复任务删除 --- src/gui/ALTimerTaskAddDialog.py | 1 + src/gui/ALTimerTaskHistoryDialog.py | 89 +++++++++------ src/gui/ALTimerTaskManageWidget.py | 162 ++++++++++++++++++++-------- 3 files changed, 175 insertions(+), 77 deletions(-) diff --git a/src/gui/ALTimerTaskAddDialog.py b/src/gui/ALTimerTaskAddDialog.py index c5af2e0..dde52b9 100644 --- a/src/gui/ALTimerTaskAddDialog.py +++ b/src/gui/ALTimerTaskAddDialog.py @@ -27,6 +27,7 @@ class ALTimerTaskStatus(Enum): EXECUTED = "已执行" ERROR = "执行失败" OUTDATED = "已过期" + UNKNOWN = "未知" class ALTimerTaskAddDialog(QDialog, Ui_ALTimerTaskAddDialog): diff --git a/src/gui/ALTimerTaskHistoryDialog.py b/src/gui/ALTimerTaskHistoryDialog.py index e125a9b..99195bb 100644 --- a/src/gui/ALTimerTaskHistoryDialog.py +++ b/src/gui/ALTimerTaskHistoryDialog.py @@ -12,9 +12,12 @@ from datetime import datetime from PySide6.QtCore import Slot, Qt from PySide6.QtWidgets import ( QDialog, QTableWidget, QTableWidgetItem, - QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QHeaderView + QVBoxLayout, QHBoxLayout, QGridLayout, + QPushButton, QLabel, QHeaderView ) +from gui.ALTimerTaskAddDialog import ALTimerTaskStatus + class ALTimerTaskHistoryDialog(QDialog): @@ -30,85 +33,105 @@ class ALTimerTaskHistoryDialog(QDialog): self.__history = task_data.get("history", []) self.modifyUi() + self.connectSignals() + def modifyUi( self ): self.setWindowTitle("定时任务执行历史 - AutoLibrary") - self.setMinimumSize(600, 400) + self.setMinimumSize(300, 300) + self.setMaximumSize(500, 400) MainLayout = QVBoxLayout(self) - - InfoLayout = QHBoxLayout() + InfoLayout = QGridLayout() TaskNameLabel = QLabel(f"任务: {self.__task_data.get('name', '未命名')}") TaskNameLabel.setStyleSheet("font-weight: bold; font-size: 14px;") - InfoLayout.addWidget(TaskNameLabel) - InfoLayout.addStretch() + InfoLayout.addWidget(TaskNameLabel, 0, 0) + TaskUUIDLabel = QLabel(f"UUID: {self.__task_data.get('task_uuid', '未命名')}") + TaskUUIDLabel.setStyleSheet("font-size: 10px;") + InfoLayout.addWidget(TaskUUIDLabel, 1, 0) + InfoLayout.setColumnStretch(0, 1) if self.__task_data.get("repeat", False): - repeat_label = QLabel("重复任务") - repeat_label.setStyleSheet("color: #2294FF; font-weight: bold;") - InfoLayout.addWidget(repeat_label) + RepeatLabel = QLabel("重复任务") + RepeatLabel.setStyleSheet("color: #2294FF; font-weight: bold; font-size: 12px;") + InfoLayout.addWidget(RepeatLabel, 0, 1) MainLayout.addLayout(InfoLayout) self.HistoryTableWidget = QTableWidget() - self.HistoryTableWidget.setColumnCount(4) - self.HistoryTableWidget.setHorizontalHeaderLabels(["执行时间", "结果", "耗时(秒/s)", "uuid"]) + self.HistoryTableWidget.setColumnCount(3) + self.HistoryTableWidget.setHorizontalHeaderLabels(["执行时间", "结果", "耗时(秒/s)"]) self.HistoryTableWidget.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch) self.HistoryTableWidget.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeMode.ResizeToContents) - self.HistoryTableWidget.horizontalHeader().setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents) - self.HistoryTableWidget.horizontalHeader().setSectionResizeMode(3, QHeaderView.ResizeMode.ResizeToContents) + self.HistoryTableWidget.horizontalHeader().setSectionResizeMode(2, QHeaderView.ResizeMode.Stretch) self.HistoryTableWidget.verticalHeader().setVisible(False) self.HistoryTableWidget.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers) self.HistoryTableWidget.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows) - self._loadHistory() + self.loadHistory() MainLayout.addWidget(self.HistoryTableWidget) ButtonLayout = QHBoxLayout() ButtonLayout.addStretch() self.CloseButton = QPushButton("关闭") self.CloseButton.setFixedSize(80, 25) - self.CloseButton.clicked.connect(self.accept) + self.CloseButton.setDefault(True) self.ClearHistoryButton = QPushButton("清空历史") self.ClearHistoryButton.setFixedSize(80, 25) - self.ClearHistoryButton.clicked.connect(self._clearHistory) + self.ClearHistoryButton.setStyleSheet("color: #DC0000;") ButtonLayout.addWidget(self.ClearHistoryButton) ButtonLayout.addWidget(self.CloseButton) MainLayout.addLayout(ButtonLayout) - def _loadHistory( + def connectSignals( + self + ): + + self.CloseButton.clicked.connect(self.accept) + self.ClearHistoryButton.clicked.connect(self.onClearHistoryButtonClicked) + + + def loadHistory( self ): self.HistoryTableWidget.setRowCount(len(self.__history)) for row, record in enumerate(self.__history): - self._addHistoryRow(row, record) + self.addHistoryRow(row, record) - def _addHistoryRow( + def addHistoryRow( self, row: int, record: dict ): - execute_time_str = record.get("execute_time", "") - result = record.get("result", "未知") + execute_time = record.get("execute_time", "") + result = record.get("result", ALTimerTaskStatus.UNKNOWN) duration = record.get("duration", 0) - uuid = record.get("uuid", "") - self.HistoryTableWidget.setItem(row, 0, QTableWidgetItem(execute_time_str)) - self.HistoryTableWidget.setItem(row, 1, QTableWidgetItem(result)) - duration_item = QTableWidgetItem(f"{duration:.2f}") - duration_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) - self.HistoryTableWidget.setItem(row, 2, duration_item) - self.HistoryTableWidget.setItem(row, 3, QTableWidgetItem(uuid)) - if result == "成功": - self.HistoryTableWidget.item(row, 1).setForeground(Qt.GlobalColor.green) - elif result == "失败": - self.HistoryTableWidget.item(row, 1).setForeground(Qt.GlobalColor.red) + ExecuteTimeItem = QTableWidgetItem(execute_time) + ExecuteTimeItem.setTextAlignment(Qt.AlignmentFlag.AlignCenter) + self.HistoryTableWidget.setItem(row, 0, ExecuteTimeItem) + ResultItem = QTableWidgetItem(result.value) + ResultItem.setTextAlignment(Qt.AlignmentFlag.AlignCenter) + match result: + case ALTimerTaskStatus.EXECUTED: + ResultItem.setForeground(Qt.GlobalColor.green) + case ALTimerTaskStatus.ERROR: + ResultItem.setForeground(Qt.GlobalColor.red) + case ALTimerTaskStatus.OUTDATED: + ResultItem.setForeground(Qt.GlobalColor.red) + case _: + ResultItem.setForeground(Qt.GlobalColor.black) + self.HistoryTableWidget.setItem(row, 1, ResultItem) + DurationItem = QTableWidgetItem(f"{duration:.2f}") + DurationItem.setTextAlignment(Qt.AlignmentFlag.AlignCenter) + self.HistoryTableWidget.setItem(row, 2, DurationItem) + self.HistoryTableWidget.setRowHeight(row, 25) @Slot() - def _clearHistory( + def onClearHistoryButtonClicked( self ): diff --git a/src/gui/ALTimerTaskManageWidget.py b/src/gui/ALTimerTaskManageWidget.py index 4ad9dae..a5536d9 100644 --- a/src/gui/ALTimerTaskManageWidget.py +++ b/src/gui/ALTimerTaskManageWidget.py @@ -224,6 +224,9 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): task["add_time"] = datetime.strptime(task["add_time"], "%Y-%m-%d %H:%M:%S") task["execute_time"] = datetime.strptime(task["execute_time"], "%Y-%m-%d %H:%M:%S") task["status"] = ALTimerTaskStatus(task["status"]) + if "history" in task: + for item in task["history"]: + item["result"] = ALTimerTaskStatus(item["result"]) return timer_tasks["timer_tasks"] raise Exception("定时任务配置文件格式错误") except Exception as e: @@ -245,6 +248,9 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): task["add_time"] = task["add_time"].strftime("%Y-%m-%d %H:%M:%S") task["execute_time"] = task["execute_time"].strftime("%Y-%m-%d %H:%M:%S") task["status"] = task["status"].value + if "history" in task: + for item in task["history"]: + item["result"] = item["result"].value self.__cfg_mgr.set(ConfigManager.ConfigType.TIMERTASK, "", { "timer_tasks": timer_tasks }) return True except Exception as e: @@ -351,7 +357,7 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): item.setData(Qt.UserRole, timer_task) widget = ALTimerTaskItemWidget(self, timer_task) widget.DeleteButton.clicked.connect( - lambda _, uuid = timer_task["task_uuid"]: self.deleteTask(uuid) + lambda _, task = timer_task: self.deleteTask(timer_task) ) if timer_task.get("repeat", False) and hasattr(widget, "HistoryButton"): widget.HistoryButton.clicked.connect( @@ -373,11 +379,42 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): self.timerTasksChanged.emit() - def deleteTask( - self, - task_uuid: str + @staticmethod + def getTimerTaskDetailMessage( + timer_task: dict ): + return ( + f"任务名称:{timer_task["name"]}\n" + f"添加时间:{timer_task["add_time"]}\n" + f"当前状态:{timer_task["status"].value}\n" + f"下次执行时间:{datetime.strftime(timer_task["execute_time"], "%Y-%m-%d %H:%M:%S")}\n" + f"已执行次数:{len(timer_task['history'] if 'history' in timer_task else 0)}" + ) + + + def deleteTask( + self, + timer_task: dict + ): + + if timer_task["repeat"]: # when delete a repeat task + msgbox = QMessageBox(self) + msgbox.setIcon(QMessageBox.Icon.Question) + msgbox.setWindowTitle("警告 - AutoLibrary") + msgbox.setStandardButtons( + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No + ) + msgbox.setText("删除可重复性任务将同时删除所有已执行的记录 !\n是否继续 ?") + msgbox.setDetailedText( + "以下可重复性任务将被删除:\n"\ + "\n" + f"{self.getTimerTaskDetailMessage(timer_task)}" + ) + result = msgbox.exec() + if result != QMessageBox.StandardButton.Yes: + return + task_uuid = timer_task["task_uuid"] self.__timer_tasks = [ x for x in self.__timer_tasks if x["task_uuid"] != task_uuid @@ -397,8 +434,9 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): "是否要清除所有定时任务 ?", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No ) - if result is QMessageBox.StandardButton.No: + if result == QMessageBox.StandardButton.No: return + # READY and RUNNING tasks cannot be cleared in_queue_tasks = [ x for x in self.__timer_tasks if x["status"] == ALTimerTaskStatus.READY @@ -409,9 +447,40 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): QMessageBox.warning( self, "警告 - AutoLibrary", - "存在正在执行或已就绪的队列任务,无法清除所有定时任务 !" + f"存在 {in_queue_count} 个正在执行或已就绪的队列任务,无法清除所有定时任务 !" ) - self.__timer_tasks = in_queue_tasks + return + # repeat tasks ask before clear + repeat_tasks = [ + x for x in self.__timer_tasks + if x.get("repeat", False) + ] + repeat_tasks_count = len(repeat_tasks) + if repeat_tasks_count > 0: + msgbox = QMessageBox(self) + msgbox.setIcon(QMessageBox.Icon.Question) + msgbox.setWindowTitle("警告 - AutoLibrary") + msgbox.setStandardButtons( + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No + ) + msgbox.setText( + f"存在 {repeat_tasks_count} 个可重复性任务,\n" + "删除可重复性任务将同时删除所有已执行的记录 !\n" + "是否继续 ?" + ) + delete_msgs = [ + self.getTimerTaskDetailMessage(x) for x in repeat_tasks + ] + msgbox.setDetailedText( + "以下可重复性任务将被删除:\n"\ + "\n" + f"{"\n\n".join(delete_msgs)}" + ) + result = msgbox.exec() + if result != QMessageBox.StandardButton.Yes: + return + # clear all tasks + self.__timer_tasks.clear() self.timerTasksChanged.emit() @@ -420,7 +489,7 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): task: dict ): - dialog = ALTimerTaskHistoryDialog.ALTimerTaskHistoryDialog(self, task) + dialog = ALTimerTaskHistoryDialog(self, task) if dialog.exec() == QDialog.DialogCode.Accepted: self.timerTasksChanged.emit() @@ -438,7 +507,10 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): if timer_task["status"] is not ALTimerTaskStatus.PENDING: continue if timer_task["execute_time"] <= now + timedelta(seconds = -5): - timer_task["status"] = ALTimerTaskStatus.OUTDATED + if timer_task.get("repeat", False): + self.onRepeatTimerTaskIs(ALTimerTaskStatus.OUTDATED, timer_task) + else: + timer_task["status"] = ALTimerTaskStatus.OUTDATED need_update = True else: timer_task["status"] = ALTimerTaskStatus.READY @@ -493,9 +565,40 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): for task in self.__timer_tasks: if task["task_uuid"] == timer_task["task_uuid"]: task["status"] = ALTimerTaskStatus.RUNNING + break self.timerTasksChanged.emit() + def onRepeatTimerTaskIs( + self, + status: ALTimerTaskStatus, + timer_task: dict + ) -> dict: + + if "history" not in timer_task: + timer_task["history"] = [] + executed_time = datetime.now() + duration = (executed_time - timer_task["execute_time"]).total_seconds() + timer_task["history"].append({ + "execute_time": timer_task["execute_time"].strftime("%Y-%m-%d %H:%M:%S"), + "executed_time": executed_time.strftime("%Y-%m-%d %H:%M:%S"), + "result": status, + "duration": duration if status is ALTimerTaskStatus.EXECUTED else 0, + "uuid": timer_task["task_uuid"] + }) + next_time = TimerUtils.calculateNextRepeatTime( + timer_task["repeat_days"], + timer_task["repeat_hour"], + timer_task["repeat_minute"], + timer_task["repeat_second"] + ) + if next_time: + timer_task["execute_time"] = next_time + timer_task["status"] = ALTimerTaskStatus.PENDING + timer_task["executed"] = False + else: + timer_task["status"] = status + @Slot(dict) def onTimerTaskIsExecuted( self, @@ -505,31 +608,10 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): for task in self.__timer_tasks: if task["task_uuid"] == timer_task["task_uuid"]: if task.get("repeat", False): - if "history" not in task: - task["history"] = [] - executed_time = datetime.now() - duration = (executed_time - task["execute_time"]).total_seconds() - task["history"].append({ - "execute_time": task["execute_time"].strftime("%Y-%m-%d %H:%M:%S"), - "executed_time": executed_time.strftime("%Y-%m-%d %H:%M:%S"), - "result": "成功", - "duration": duration, - "uuid": timer_task["task_uuid"] - }) - next_time = TimerUtils.calculateNextRepeatTime( - task["repeat_days"], - task["repeat_hour"], - task["repeat_minute"], - task["repeat_second"] - ) - if next_time: - task["execute_time"] = next_time - task["status"] = ALTimerTaskStatus.PENDING - task["executed"] = False - else: - task["status"] = ALTimerTaskStatus.EXECUTED + self.onRepeatTimerTaskIs(ALTimerTaskStatus.EXECUTED, task) else: task["status"] = ALTimerTaskStatus.EXECUTED + break self.timerTasksChanged.emit() @Slot(dict) @@ -541,16 +623,8 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): for task in self.__timer_tasks: if task["task_uuid"] == timer_task["task_uuid"]: if task.get("repeat", False): - if "history" not in task: - task["history"] = [] - executed_time = datetime.now() - duration = (executed_time - task["execute_time"]).total_seconds() - task["history"].append({ - "execute_time": task["execute_time"].strftime("%Y-%m-%d %H:%M:%S"), - "executed_time": executed_time.strftime("%Y-%m-%d %H:%M:%S"), - "result": "失败", - "duration": duration, - "uuid": timer_task["task_uuid"] - }) - task["status"] = ALTimerTaskStatus.ERROR + self.onRepeatTimerTaskIs(ALTimerTaskStatus.ERROR, task) + else: + task["status"] = ALTimerTaskStatus.ERROR + break self.timerTasksChanged.emit()