mirror of
https://github.com/KenanZhu/AutoLibrary.git
synced 2026-06-22 01:13:02 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 883859d1f9 | |||
| f37bcf836b | |||
| b0d1c0e99e | |||
| 5af6120be8 |
@@ -12,15 +12,11 @@ import uuid
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from PySide6.QtCore import (
|
from PySide6.QtCore import Slot, QDateTime
|
||||||
Slot, QDateTime
|
from PySide6.QtWidgets import QLabel, QDialog, QWidget, QSpinBox, QHBoxLayout, QGridLayout, QDateTimeEdit
|
||||||
)
|
|
||||||
from PySide6.QtWidgets import (
|
|
||||||
QLabel, QDialog, QWidget, QSpinBox,
|
|
||||||
QHBoxLayout, QGridLayout, QDateTimeEdit
|
|
||||||
)
|
|
||||||
|
|
||||||
from gui.resources.ui.Ui_ALTimerTaskAddDialog import Ui_ALTimerTaskAddDialog
|
from gui.resources.ui.Ui_ALTimerTaskAddDialog import Ui_ALTimerTaskAddDialog
|
||||||
|
import utils.TimerUtils as TimerUtils
|
||||||
|
|
||||||
|
|
||||||
class ALTimerTaskStatus(Enum):
|
class ALTimerTaskStatus(Enum):
|
||||||
@@ -43,8 +39,8 @@ class ALTimerTaskAddDialog(QDialog, Ui_ALTimerTaskAddDialog):
|
|||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self.connectSignals()
|
|
||||||
self.modifyUi()
|
self.modifyUi()
|
||||||
|
self.connectSignals()
|
||||||
|
|
||||||
|
|
||||||
def modifyUi(
|
def modifyUi(
|
||||||
@@ -97,6 +93,7 @@ class ALTimerTaskAddDialog(QDialog, Ui_ALTimerTaskAddDialog):
|
|||||||
self.CancelButton.clicked.connect(self.reject)
|
self.CancelButton.clicked.connect(self.reject)
|
||||||
self.ConfirmButton.clicked.connect(self.accept)
|
self.ConfirmButton.clicked.connect(self.accept)
|
||||||
self.TimerTypeComboBox.currentIndexChanged.connect(self.onTimerTypeComboBoxIndexChanged)
|
self.TimerTypeComboBox.currentIndexChanged.connect(self.onTimerTypeComboBoxIndexChanged)
|
||||||
|
self.RepeatCheckBox.toggled.connect(self.onRepeatCheckBoxToggled)
|
||||||
|
|
||||||
|
|
||||||
def getTimerTask(
|
def getTimerTask(
|
||||||
@@ -121,7 +118,7 @@ class ALTimerTaskAddDialog(QDialog, Ui_ALTimerTaskAddDialog):
|
|||||||
minutes = self.RelativeMinuteSpinBox.value(),
|
minutes = self.RelativeMinuteSpinBox.value(),
|
||||||
seconds = self.RelativeSecondSpinBox.value()
|
seconds = self.RelativeSecondSpinBox.value()
|
||||||
)
|
)
|
||||||
return {
|
task_data = {
|
||||||
"name": name,
|
"name": name,
|
||||||
"task_uuid": uuid.uuid4().hex.upper() + f"-{added_time.strftime("%Y%m%d%H%M%S")}",
|
"task_uuid": uuid.uuid4().hex.upper() + f"-{added_time.strftime("%Y%m%d%H%M%S")}",
|
||||||
"time_type": self.TimerTypeComboBox.currentText(),
|
"time_type": self.TimerTypeComboBox.currentText(),
|
||||||
@@ -129,9 +126,40 @@ class ALTimerTaskAddDialog(QDialog, Ui_ALTimerTaskAddDialog):
|
|||||||
"silent": silent,
|
"silent": silent,
|
||||||
"add_time": added_time,
|
"add_time": added_time,
|
||||||
"status": ALTimerTaskStatus.PENDING,
|
"status": ALTimerTaskStatus.PENDING,
|
||||||
"executed": False
|
"executed": False,
|
||||||
|
"repeat": self.RepeatCheckBox.isChecked(),
|
||||||
|
"repeat_records": []
|
||||||
}
|
}
|
||||||
|
if task_data["repeat"]:
|
||||||
|
repeat_days = []
|
||||||
|
if self.MonCheckBox.isChecked():
|
||||||
|
repeat_days.append(0)
|
||||||
|
if self.TueCheckBox.isChecked():
|
||||||
|
repeat_days.append(1)
|
||||||
|
if self.WedCheckBox.isChecked():
|
||||||
|
repeat_days.append(2)
|
||||||
|
if self.ThuCheckBox.isChecked():
|
||||||
|
repeat_days.append(3)
|
||||||
|
if self.FriCheckBox.isChecked():
|
||||||
|
repeat_days.append(4)
|
||||||
|
if self.SatCheckBox.isChecked():
|
||||||
|
repeat_days.append(5)
|
||||||
|
if self.SunCheckBox.isChecked():
|
||||||
|
repeat_days.append(6)
|
||||||
|
if not repeat_days:
|
||||||
|
repeat_days = [0, 1, 2, 3, 4, 5, 6]
|
||||||
|
task_data["repeat_days"] = repeat_days
|
||||||
|
task_data["repeat_hour"] = execute_time.hour
|
||||||
|
task_data["repeat_minute"] = execute_time.minute
|
||||||
|
task_data["repeat_second"] = execute_time.second
|
||||||
|
task_data["execute_time"] = TimerUtils.calculateNextRepeatTime(
|
||||||
|
task_data["repeat_days"],
|
||||||
|
task_data["repeat_hour"],
|
||||||
|
task_data["repeat_minute"],
|
||||||
|
task_data["repeat_second"]
|
||||||
|
)
|
||||||
|
|
||||||
|
return task_data
|
||||||
|
|
||||||
@Slot(int)
|
@Slot(int)
|
||||||
def onTimerTypeComboBoxIndexChanged(
|
def onTimerTypeComboBoxIndexChanged(
|
||||||
@@ -141,3 +169,17 @@ class ALTimerTaskAddDialog(QDialog, Ui_ALTimerTaskAddDialog):
|
|||||||
|
|
||||||
self.SpecificTimerWidget.setVisible(index == 0)
|
self.SpecificTimerWidget.setVisible(index == 0)
|
||||||
self.RelativeTimerWidget.setVisible(index == 1)
|
self.RelativeTimerWidget.setVisible(index == 1)
|
||||||
|
|
||||||
|
@Slot(bool)
|
||||||
|
def onRepeatCheckBoxToggled(
|
||||||
|
self,
|
||||||
|
checked: bool
|
||||||
|
):
|
||||||
|
|
||||||
|
self.MonCheckBox.setEnabled(checked)
|
||||||
|
self.TueCheckBox.setEnabled(checked)
|
||||||
|
self.WedCheckBox.setEnabled(checked)
|
||||||
|
self.ThuCheckBox.setEnabled(checked)
|
||||||
|
self.FriCheckBox.setEnabled(checked)
|
||||||
|
self.SatCheckBox.setEnabled(checked)
|
||||||
|
self.SunCheckBox.setEnabled(checked)
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Copyright (c) 2026 KenanZhu.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
This software is provided "as is", without any warranty of any kind.
|
||||||
|
You may use, modify, and distribute this file under the terms of the MIT License.
|
||||||
|
See the LICENSE file for details.
|
||||||
|
"""
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from PySide6.QtCore import Slot, Qt
|
||||||
|
from PySide6.QtWidgets import (
|
||||||
|
QDialog, QTableWidget, QTableWidgetItem,
|
||||||
|
QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QHeaderView
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ALTimerTaskHistoryDialog(QDialog):
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
parent = None,
|
||||||
|
task_data: dict = None
|
||||||
|
):
|
||||||
|
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.__task_data = task_data
|
||||||
|
self.__history = task_data.get("history", [])
|
||||||
|
|
||||||
|
self.modifyUi()
|
||||||
|
|
||||||
|
def modifyUi(
|
||||||
|
self
|
||||||
|
):
|
||||||
|
|
||||||
|
self.setWindowTitle("定时任务执行历史 - AutoLibrary")
|
||||||
|
self.setMinimumSize(600, 400)
|
||||||
|
|
||||||
|
MainLayout = QVBoxLayout(self)
|
||||||
|
|
||||||
|
InfoLayout = QHBoxLayout()
|
||||||
|
TaskNameLabel = QLabel(f"任务: {self.__task_data.get('name', '未命名')}")
|
||||||
|
TaskNameLabel.setStyleSheet("font-weight: bold; font-size: 14px;")
|
||||||
|
InfoLayout.addWidget(TaskNameLabel)
|
||||||
|
InfoLayout.addStretch()
|
||||||
|
|
||||||
|
if self.__task_data.get("repeat", False):
|
||||||
|
repeat_label = QLabel("重复任务")
|
||||||
|
repeat_label.setStyleSheet("color: #2294FF; font-weight: bold;")
|
||||||
|
InfoLayout.addWidget(repeat_label)
|
||||||
|
MainLayout.addLayout(InfoLayout)
|
||||||
|
self.HistoryTableWidget = QTableWidget()
|
||||||
|
self.HistoryTableWidget.setColumnCount(4)
|
||||||
|
self.HistoryTableWidget.setHorizontalHeaderLabels(["执行时间", "结果", "耗时(秒/s)", "uuid"])
|
||||||
|
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.verticalHeader().setVisible(False)
|
||||||
|
self.HistoryTableWidget.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers)
|
||||||
|
self.HistoryTableWidget.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows)
|
||||||
|
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.ClearHistoryButton = QPushButton("清空历史")
|
||||||
|
self.ClearHistoryButton.setFixedSize(80, 25)
|
||||||
|
self.ClearHistoryButton.clicked.connect(self._clearHistory)
|
||||||
|
ButtonLayout.addWidget(self.ClearHistoryButton)
|
||||||
|
ButtonLayout.addWidget(self.CloseButton)
|
||||||
|
MainLayout.addLayout(ButtonLayout)
|
||||||
|
|
||||||
|
|
||||||
|
def _loadHistory(
|
||||||
|
self
|
||||||
|
):
|
||||||
|
|
||||||
|
self.HistoryTableWidget.setRowCount(len(self.__history))
|
||||||
|
for row, record in enumerate(self.__history):
|
||||||
|
self._addHistoryRow(row, record)
|
||||||
|
|
||||||
|
|
||||||
|
def _addHistoryRow(
|
||||||
|
self,
|
||||||
|
row: int,
|
||||||
|
record: dict
|
||||||
|
):
|
||||||
|
|
||||||
|
execute_time_str = record.get("execute_time", "")
|
||||||
|
result = record.get("result", "未知")
|
||||||
|
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)
|
||||||
|
|
||||||
|
@Slot()
|
||||||
|
def _clearHistory(
|
||||||
|
self
|
||||||
|
):
|
||||||
|
|
||||||
|
self.__history.clear()
|
||||||
|
self.HistoryTableWidget.setRowCount(0)
|
||||||
|
self.__task_data["history"] = self.__history
|
||||||
|
|
||||||
|
|
||||||
|
def getHistory(
|
||||||
|
self
|
||||||
|
) -> list:
|
||||||
|
|
||||||
|
return self.__history
|
||||||
@@ -21,11 +21,14 @@ from PySide6.QtWidgets import (
|
|||||||
QDialog, QWidget, QListWidgetItem, QMessageBox,
|
QDialog, QWidget, QListWidgetItem, QMessageBox,
|
||||||
QHBoxLayout, QVBoxLayout, QLabel, QPushButton
|
QHBoxLayout, QVBoxLayout, QLabel, QPushButton
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import gui.ALTimerTaskHistoryDialog as ALTimerTaskHistoryDialog
|
||||||
from PySide6.QtGui import (
|
from PySide6.QtGui import (
|
||||||
QCloseEvent
|
QCloseEvent
|
||||||
)
|
)
|
||||||
|
|
||||||
import utils.ConfigManager as ConfigManager
|
import utils.ConfigManager as ConfigManager
|
||||||
|
import utils.TimerUtils as TimerUtils
|
||||||
|
|
||||||
from gui.resources.ui.Ui_ALTimerTaskManageWidget import Ui_ALTimerTaskManageWidget
|
from gui.resources.ui.Ui_ALTimerTaskManageWidget import Ui_ALTimerTaskManageWidget
|
||||||
from gui.ALTimerTaskAddDialog import ALTimerTaskAddDialog, ALTimerTaskStatus
|
from gui.ALTimerTaskAddDialog import ALTimerTaskAddDialog, ALTimerTaskStatus
|
||||||
@@ -61,9 +64,19 @@ class ALTimerTaskItemWidget(QWidget):
|
|||||||
TaskNameLabel.setFont(TaskNameLabelFont)
|
TaskNameLabel.setFont(TaskNameLabelFont)
|
||||||
TaskNameLabel.setFixedHeight(25)
|
TaskNameLabel.setFixedHeight(25)
|
||||||
self.TaskInfoLayout.addWidget(TaskNameLabel)
|
self.TaskInfoLayout.addWidget(TaskNameLabel)
|
||||||
|
|
||||||
ExecuteTimeStr = self.__timer_task["execute_time"].strftime("%Y-%m-%d %H:%M:%S")
|
ExecuteTimeStr = self.__timer_task["execute_time"].strftime("%Y-%m-%d %H:%M:%S")
|
||||||
ExecuteTimeLabel = QLabel(f"执行时间: {ExecuteTimeStr}")
|
if self.__timer_task.get("repeat", False):
|
||||||
|
repeat_days = self.__timer_task.get("repeat_days", [])
|
||||||
|
if len(repeat_days) == 7:
|
||||||
|
time_str = f"{self.__timer_task.get('repeat_hour', 0):02d}:{self.__timer_task.get('repeat_minute', 0):02d}:{self.__timer_task.get('repeat_second', 0):02d}"
|
||||||
|
ExecuteTimeLabel = QLabel(f"下次执行时间: {ExecuteTimeStr} (每日 {time_str})")
|
||||||
|
else:
|
||||||
|
day_names = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
|
||||||
|
selected_days = [day_names[d] for d in repeat_days]
|
||||||
|
time_str = f"{self.__timer_task.get('repeat_hour', 0):02d}:{self.__timer_task.get('repeat_minute', 0):02d}:{self.__timer_task.get('repeat_second', 0):02d}"
|
||||||
|
ExecuteTimeLabel = QLabel(f"下次执行时间: {ExecuteTimeStr} (每{','.join(selected_days)} {time_str})")
|
||||||
|
else:
|
||||||
|
ExecuteTimeLabel = QLabel(f"执行时间: {ExecuteTimeStr}")
|
||||||
ExecuteTimeLabel.setStyleSheet("color: #969696;")
|
ExecuteTimeLabel.setStyleSheet("color: #969696;")
|
||||||
ExecuteTimeLabel.setFixedHeight(20)
|
ExecuteTimeLabel.setFixedHeight(20)
|
||||||
self.TaskInfoLayout.addWidget(ExecuteTimeLabel)
|
self.TaskInfoLayout.addWidget(ExecuteTimeLabel)
|
||||||
@@ -124,6 +137,10 @@ class ALTimerTaskItemWidget(QWidget):
|
|||||||
if self.__timer_task["status"] == ALTimerTaskStatus.READY\
|
if self.__timer_task["status"] == ALTimerTaskStatus.READY\
|
||||||
or self.__timer_task["status"] == ALTimerTaskStatus.RUNNING:
|
or self.__timer_task["status"] == ALTimerTaskStatus.RUNNING:
|
||||||
self.DeleteButton.setEnabled(False)
|
self.DeleteButton.setEnabled(False)
|
||||||
|
if self.__timer_task.get("repeat", False):
|
||||||
|
self.HistoryButton = QPushButton("历史")
|
||||||
|
self.HistoryButton.setFixedSize(80, 25)
|
||||||
|
self.ItemWidgetLayout.addWidget(self.HistoryButton)
|
||||||
self.setFixedHeight(55)
|
self.setFixedHeight(55)
|
||||||
|
|
||||||
|
|
||||||
@@ -334,6 +351,10 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget):
|
|||||||
widget.DeleteButton.clicked.connect(
|
widget.DeleteButton.clicked.connect(
|
||||||
lambda _, uuid = timer_task["task_uuid"]: self.deleteTask(uuid)
|
lambda _, uuid = timer_task["task_uuid"]: self.deleteTask(uuid)
|
||||||
)
|
)
|
||||||
|
if timer_task.get("repeat", False) and hasattr(widget, "HistoryButton"):
|
||||||
|
widget.HistoryButton.clicked.connect(
|
||||||
|
lambda _, task = timer_task: self.showTaskHistory(task)
|
||||||
|
)
|
||||||
item.setSizeHint(widget.size())
|
item.setSizeHint(widget.size())
|
||||||
self.TimerTasksListWidget.addItem(item)
|
self.TimerTasksListWidget.addItem(item)
|
||||||
self.TimerTasksListWidget.setItemWidget(item, widget)
|
self.TimerTasksListWidget.setItemWidget(item, widget)
|
||||||
@@ -392,6 +413,16 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget):
|
|||||||
self.timerTasksChanged.emit()
|
self.timerTasksChanged.emit()
|
||||||
|
|
||||||
|
|
||||||
|
def showTaskHistory(
|
||||||
|
self,
|
||||||
|
task: dict
|
||||||
|
):
|
||||||
|
|
||||||
|
dialog = ALTimerTaskHistoryDialog.ALTimerTaskHistoryDialog(self, task)
|
||||||
|
if dialog.exec() == QDialog.DialogCode.Accepted:
|
||||||
|
self.timerTasksChanged.emit()
|
||||||
|
|
||||||
|
|
||||||
def checkTasks(
|
def checkTasks(
|
||||||
self
|
self
|
||||||
):
|
):
|
||||||
@@ -471,7 +502,32 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget):
|
|||||||
|
|
||||||
for task in self.__timer_tasks:
|
for task in self.__timer_tasks:
|
||||||
if task["task_uuid"] == timer_task["task_uuid"]:
|
if task["task_uuid"] == timer_task["task_uuid"]:
|
||||||
task["status"] = ALTimerTaskStatus.EXECUTED
|
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
|
||||||
|
else:
|
||||||
|
task["status"] = ALTimerTaskStatus.EXECUTED
|
||||||
self.timerTasksChanged.emit()
|
self.timerTasksChanged.emit()
|
||||||
|
|
||||||
@Slot(dict)
|
@Slot(dict)
|
||||||
@@ -482,5 +538,17 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget):
|
|||||||
|
|
||||||
for task in self.__timer_tasks:
|
for task in self.__timer_tasks:
|
||||||
if task["task_uuid"] == timer_task["task_uuid"]:
|
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
|
task["status"] = ALTimerTaskStatus.ERROR
|
||||||
self.timerTasksChanged.emit()
|
self.timerTasksChanged.emit()
|
||||||
|
|||||||
@@ -6,20 +6,20 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>300</width>
|
<width>350</width>
|
||||||
<height>300</height>
|
<height>400</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>0</width>
|
<width>350</width>
|
||||||
<height>300</height>
|
<height>400</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>500</width>
|
<width>350</width>
|
||||||
<height>300</height>
|
<height>500</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@@ -149,8 +149,20 @@
|
|||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>5</number>
|
<number>5</number>
|
||||||
</property>
|
</property>
|
||||||
<item row="0" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QRadioButton" name="SilentlyRunRadioButton">
|
<widget class="QRadioButton" name="SilentlyRunRadioButton">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>静默运行</string>
|
<string>静默运行</string>
|
||||||
</property>
|
</property>
|
||||||
@@ -168,13 +180,248 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="2" column="0">
|
||||||
<widget class="QRadioButton" name="ShowBeforeRunRadioButton">
|
<widget class="QRadioButton" name="ShowBeforeRunRadioButton">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>运行前提示</string>
|
<string>运行前提示</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QGroupBox" name="RepeatConfigGroupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>重复运行</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="RepeatConfigLayout" stretch="1,1,1">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="RepeatCheckBox">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>启用重复执行</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>重复周期(全选为每日运行):</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QGridLayout" name="RepeatCheckBoxLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="3">
|
||||||
|
<widget class="QCheckBox" name="ThuCheckBox">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>50</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>50</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>周四</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QCheckBox" name="WedCheckBox">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>50</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>50</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>周三</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QCheckBox" name="MonCheckBox">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>50</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>50</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>周一</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QCheckBox" name="TueCheckBox">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>50</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>50</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>周二</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QCheckBox" name="FriCheckBox">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>50</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>50</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>周五</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QCheckBox" name="SatCheckBox">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>50</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>50</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>周六</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QCheckBox" name="SunCheckBox">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>50</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>50</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>周日</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
</property>
|
</property>
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>600</width>
|
<width>800</width>
|
||||||
<height>400</height>
|
<height>400</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
This software is provided "as is", without any warranty of any kind.
|
||||||
|
You may use, modify, and distribute this file under the terms of the MIT License.
|
||||||
|
See the LICENSE file for details.
|
||||||
|
"""
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
|
||||||
|
def calculateNextRepeatTime(
|
||||||
|
repeat_days: list,
|
||||||
|
hour: int,
|
||||||
|
minute: int,
|
||||||
|
second: int
|
||||||
|
) -> datetime:
|
||||||
|
"""
|
||||||
|
Calculate the next repeat time based on repeat days and target time.
|
||||||
|
|
||||||
|
This function calculates the next execution time for a repeatable task.
|
||||||
|
If the current day is in repeat_days and the target time has not passed,
|
||||||
|
it returns today's target time. Otherwise, it finds the next matching day.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
repeat_days (list): List of weekdays to repeat (0=Monday, 6=Sunday).
|
||||||
|
hour (int): Target hour (0-23).
|
||||||
|
minute (int): Target minute (0-59).
|
||||||
|
second (int): Target second (0-59).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
datetime: The next repeat execution time.
|
||||||
|
"""
|
||||||
|
|
||||||
|
current_time = datetime.now()
|
||||||
|
current_weekday = current_time.weekday()
|
||||||
|
target_time = current_time.replace(hour=hour, minute=minute, second=second, microsecond=0)
|
||||||
|
if current_weekday in repeat_days:
|
||||||
|
if target_time > current_time:
|
||||||
|
return target_time
|
||||||
|
repeat_days_sorted = sorted(repeat_days)
|
||||||
|
for day in repeat_days_sorted:
|
||||||
|
if day > current_weekday:
|
||||||
|
days_until = day - current_weekday
|
||||||
|
next_time = target_time + timedelta(days=days_until)
|
||||||
|
return next_time
|
||||||
|
days_until = 7 - current_weekday + repeat_days_sorted[0]
|
||||||
|
next_time = target_time + timedelta(days=days_until)
|
||||||
|
return next_time
|
||||||
Reference in New Issue
Block a user