1
1
mirror of https://github.com/KenanZhu/AutoLibrary.git synced 2026-06-18 23:43:02 +08:00

Compare commits

...

5 Commits

Author SHA1 Message Date
KenanZhu e11f696b76 style(*): 添加缺失的版权信息,并同一版权年份为文件创建时间的年份 2026-05-06 01:01:52 +08:00
KenanZhu ffae43d5bd fix(ConfigUtils): 添加未导入的 os 模块 2026-03-24 21:49:52 +08:00
Gogs baa4f23136 refactor(config): 新增 ConfigUtils 工具类并优化配置管理逻辑
- 新增 ConfigUtils 工具类,提供配置路径获取等工具方法
- 将 ConfigManager.getValidateAutomationConfigPaths() 重构为 ConfigUtils.getAutomationConfigPaths()
- 优化 MsgBase 中 LogManager 的导入方式,使用模块导入替代函数导入
- 规范化 TimerUtils.py 中 calculate_next_repeat_time() 的文档字符串格式
2026-03-23 13:31:06 +08:00
KenanZhu 1c88d3db7b chore(requirement): 移除 opencv-python 和 pywin32 冗余依赖 2026-03-22 22:56:43 +08:00
github-actions[bot] 3880f90916 chore(release): merge release/v1.2.1 to main [auto release commit] 2026-03-22 14:17:40 +00:00
31 changed files with 152 additions and 116 deletions
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2025 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2025 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2026 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
+3 -3
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2025 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
@@ -11,7 +11,7 @@ import logging
import queue
import datetime
from managers.log.LogManager import getLogger
import managers.log.LogManager as LogManager
class MsgBase:
@@ -54,7 +54,7 @@ class MsgBase:
self._input_queue = input_queue
self._output_queue = output_queue
try:
self._logger = getLogger(self._class_name)
self._logger = LogManager.getLogger(self._class_name)
except RuntimeError:
self._logger = None
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2026 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2025 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
+3 -2
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2025 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
@@ -24,6 +24,7 @@ import managers.config.ConfigManager as ConfigManager
from utils.JSONReader import JSONReader
from utils.JSONWriter import JSONWriter
from utils.ConfigUtils import ConfigUtils
from gui.resources.ui.Ui_ALConfigWidget import Ui_ALConfigWidget
from gui.ALSeatMapSelectDialog import ALSeatMapSelectDialog
@@ -43,7 +44,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
super().__init__(parent)
self.__cfg_mgr = ConfigManager.instance()
self.__config_paths = ConfigManager.getValidateAutomationConfigPaths()
self.__config_paths = ConfigUtils.getAutomationConfigPaths()
self.__config_data = {"run": {}, "user": {}}
self.setupUi(self)
+4 -6
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2025 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
@@ -19,9 +19,8 @@ from PySide6.QtGui import (
QTextCursor, QCloseEvent, QFont, QIcon, QDesktopServices
)
import managers.config.ConfigManager as ConfigManager
from base.MsgBase import MsgBase
from utils.ConfigUtils import ConfigUtils
from gui.resources.ui.Ui_ALMainWindow import Ui_ALMainWindow
from gui.resources import ALResource
@@ -44,9 +43,8 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow):
MsgBase.__init__(self, queue.Queue(), queue.Queue())
QMainWindow.__init__(self)
self.__cfg_mgr = ConfigManager.instance()
self.__timer_task_queue = queue.Queue()
self.__config_paths = ConfigManager.getValidateAutomationConfigPaths()
self.__config_paths = ConfigUtils.getAutomationConfigPaths()
self.__alTimerTaskManageWidget = None
self.__alConfigWidget = None
self.__auto_lib_thread = None
@@ -300,7 +298,7 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow):
self.__alConfigWidget.configWidgetIsClosed.disconnect(self.onConfigWidgetClosed)
self.__alConfigWidget.deleteLater()
self.__alConfigWidget = None
self.__config_paths = ConfigManager.getValidateAutomationConfigPaths()
self.__config_paths = ConfigUtils.getAutomationConfigPaths()
self.setControlButtons(True, None, None)
self._showLog("配置窗口已关闭,配置文件路径已更新")
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2025 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2025 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2026 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2026 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
+8
View File
@@ -1,4 +1,12 @@
# -*- 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 enum import Enum
from PySide6.QtWidgets import (
+4 -3
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2025 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
@@ -16,7 +16,7 @@ from PySide6.QtCore import Slot, QDateTime
from PySide6.QtWidgets import QLabel, QDialog, QWidget, QSpinBox, QHBoxLayout, QGridLayout, QDateTimeEdit
from gui.resources.ui.Ui_ALTimerTaskAddDialog import Ui_ALTimerTaskAddDialog
import utils.TimerUtils as TimerUtils
from utils.TimerUtils import TimerUtils
class ALTimerTaskStatus(Enum):
@@ -131,6 +131,7 @@ class ALTimerTaskAddDialog(QDialog, Ui_ALTimerTaskAddDialog):
"repeat": self.RepeatCheckBox.isChecked(),
}
if task_data["repeat"]:
task_data["history"] = [] # repeat history
repeat_days = []
if self.MonCheckBox.isChecked():
repeat_days.append(0)
@@ -152,7 +153,7 @@ class ALTimerTaskAddDialog(QDialog, Ui_ALTimerTaskAddDialog):
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["execute_time"] = TimerUtils.getNextTimerRepeatTime(
task_data["repeat_days"],
task_data["repeat_hour"],
task_data["repeat_minute"],
+3 -3
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2025 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
@@ -26,7 +26,7 @@ from PySide6.QtGui import (
)
import managers.config.ConfigManager as ConfigManager
import utils.TimerUtils as TimerUtils
from utils.TimerUtils import TimerUtils
from gui.resources.ui.Ui_ALTimerTaskManageWidget import Ui_ALTimerTaskManageWidget
from gui.ALTimerTaskAddDialog import ALTimerTaskAddDialog, ALTimerTaskStatus
@@ -610,7 +610,7 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget):
"duration": 0,
"uuid": timer_task["uuid"]
})
next_time = TimerUtils.calculateNextRepeatTime(
next_time = TimerUtils.getNextTimerRepeatTime(
timer_task["repeat_days"],
timer_task["repeat_hour"],
timer_task["repeat_minute"],
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2025 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
+2 -44
View File
@@ -176,49 +176,7 @@ class ConfigManager:
# ConfigManager singleton instance.
_config_manager_instance = None
# Utility functions.
#
# Utility function to get validated automation config paths.
def getValidateAutomationConfigPaths(
) -> dict:
"""
Get validated automation config paths from ConfigManager instance.
These function will validate the config paths and return the validated paths in a dict.
Returns:
dict: Validated automation config paths.
"""
config_paths = {"run": "", "user": ""}
auto_config = _config_manager_instance.get(ConfigType.GLOBAL, "automation", {})
for cfg_type in ["run", "user"]:
paths = auto_config.get(f"{cfg_type}_path", {}).get("paths", [])
index = auto_config.get(f"{cfg_type}_path", {}).get("current", 0)
if paths == []:
paths.append(os.path.join(_config_manager_instance.configDir(), f"{cfg_type}.json"))
if index < 0:
index = 0
if index >= len(paths):
index = len(paths) - 1
config_paths[cfg_type] = paths[index]
data = {"current": index, "paths": paths}
auto_config[f"{cfg_type}_path"] = data
_config_manager_instance.set(ConfigType.GLOBAL, "automation", auto_config)
return config_paths
# Utility function to get base config directory.
def getBaseConfigDir(
) -> str:
"""
Get base config directory, on Windows, it is usually at :
'C:\\Users\\<username>\\AppData\\Local\\AutoLibrary\\config'.
Returns:
str: Base config directory.
"""
return _config_manager_instance.configDir()
_config_manager_instance : ConfigManager | None = None
# Singleton instance of ConfigManager.
_instance_lock = threading.Lock()
@@ -240,6 +198,6 @@ def instance(
else:
if config_dir == "":
return _config_manager_instance
if getBaseConfigDir() != config_dir:
if _config_manager_instance.configDir() != config_dir:
raise ValueError("ConfigManager 的实例已初始化,不能使用不同的配置目录。")
return _config_manager_instance
@@ -1,3 +1,12 @@
# -*- 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.
"""
import platform
import browsers
@@ -1,3 +1,12 @@
# -*- 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.
"""
import os
import time
import shutil
+1 -1
View File
@@ -186,7 +186,7 @@ def instance(
raise ValueError("LogManager 的实例已初始化, 不能使用不同的日志目录")
return _log_manager_instance
# export function to get logger
def getLogger(
name: Optional[str] = None
) -> logging.Logger:
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2025 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2025 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2025 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2025 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2025 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2025 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2025 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2025 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
+46
View File
@@ -0,0 +1,46 @@
# -*- 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.
"""
import os
import managers.config.ConfigManager as ConfigManager
class ConfigUtils:
"""
Config utilities class.
"""
@staticmethod
def getAutomationConfigPaths(
) -> dict[str]:
"""
Get validated automation config paths from ConfigManager instance.
These function will validate the config paths and return the validated paths in a dict.
Returns:
dict[str]: Validated automation config paths (include user and run config paths).
"""
cfg_mgr = ConfigManager.instance() # config manager instance
config_paths = {"run": "", "user": ""}
auto_config = cfg_mgr.get(ConfigManager.ConfigType.GLOBAL, "automation", {})
for cfg_type in ["run", "user"]:
paths = auto_config.get(f"{cfg_type}_path", {}).get("paths", [])
index = auto_config.get(f"{cfg_type}_path", {}).get("current", 0)
if paths == []:
paths.append(os.path.join(cfg_mgr.configDir(), f"{cfg_type}.json"))
if index < 0:
index = 0
if index >= len(paths):
index = len(paths) - 1
config_paths[cfg_type] = paths[index]
data = {"current": index, "paths": paths}
auto_config[f"{cfg_type}_path"] = data
cfg_mgr.set(ConfigManager.ConfigType.GLOBAL, "automation", auto_config)
return config_paths
+42 -36
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
Copyright (c) 2026 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
@@ -10,41 +10,47 @@ See the LICENSE file for details.
from datetime import datetime, timedelta
def calculateNextRepeatTime(
repeat_days: list,
hour: int,
minute: int,
second: int
) -> datetime:
class TimerUtils:
"""
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.
Timer utilities class.
"""
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
@staticmethod
def getNextTimerRepeatTime(
repeat_days: list[int],
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[int]): 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