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

Compare commits

...

19 Commits

Author SHA1 Message Date
github-actions[bot] f3d68c40cb chore(release): v1.0.4 [auto release commit] 2026-01-17 18:18:22 +00:00
KenanZhu 0ceff677e4 docs(readme): 更新自述文档内容 2026-01-18 02:14:01 +08:00
KenanZhu 6f6b415bff refactor(ALMainWindow, ALMainWorkers): 重构 Qt 信号函数的命名 2026-01-18 02:08:12 +08:00
KenanZhu 735f31830d refactor(gui.*): 统一界面控件颜色风格 2026-01-18 02:08:12 +08:00
KenanZhu 7be5afeae1 style(gui.ALSeatFrame): 一些格式问题 2026-01-18 02:08:12 +08:00
KenanZhu 3d6978c9c2 optimize(gui.*): 优化界面组件的布局和样式 2026-01-18 02:08:12 +08:00
github-actions[bot] db7a868598 chore(release): v1.0.3 [auto release commit] 2026-01-17 17:52:03 +00:00
KenanZhu f1e0334ce3 docs(MsgBase, LibOperator): 添加并完善类文档注释 2026-01-16 23:41:25 +08:00
KenanZhu b9411261ea style(ALMainWorkers): 一些格式更改 2026-01-16 23:25:42 +08:00
KenanZhu fa737711d4 optimize(ConfigReader, ConfigWriter): 优化配置文件读写类逻辑,完善异常处理,添加注释文档 2026-01-16 23:23:03 +08:00
KenanZhu 79e2128fca style(operators.*): 显式指定浏览器驱动类型为 WebDriver 2026-01-16 23:21:36 +08:00
KenanZhu 128c8e7a83 style(*): 移除未使用的 import 语句 2026-01-16 22:37:26 +08:00
KenanZhu 6474f6e3bb style(*): 格式化一些界面类的构造函数 2026-01-16 22:33:01 +08:00
KenanZhu ba60a5d884 style(comment): 修改一些注释格式 2026-01-15 17:08:54 +08:00
KenanZhu 4d8f8130dc chore(operators.__init__): 添加 LibChecker 类的简介 2026-01-13 22:40:45 +08:00
KenanZhu eba99cab9f fix(ALSeatMapWidget): 修复座位图选择的确定取消逻辑 2026-01-13 22:01:16 +08:00
KenanZhu aa7a806ff7 fix(gui): 修复一些界面问题 2026-01-12 14:22:20 +08:00
KenanZhu bb180f8c8e fix(ALConfigWidget, LibReserve): 修改二楼楼层区域名称
将 二层外环 改为 二层西区
2026-01-09 14:06:36 +08:00
KenanZhu 107ed41b58 chore(*): 更新 license 和版权信息为 2025 - 2026 年 2026-01-09 14:00:25 +08:00
31 changed files with 310 additions and 200 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright 2025 KenanZhu Copyright 2025 - 2026 KenanZhu
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+9 -8
View File
@@ -4,10 +4,11 @@
![AutoLibrary Logo](./src/gui/icons/AutoLibrary_128x128.ico) ![AutoLibrary Logo](./src/gui/icons/AutoLibrary_128x128.ico)
[![GitHub stars](https://img.shields.io/github/stars/KenanZhu/AutoLibrary.svg?style=social&label=Star)](https://github.com/KenanZhu/AutoLibrary)
![License](https://img.shields.io/github/license/KenanZhu/AutoLibrary) ![License](https://img.shields.io/github/license/KenanZhu/AutoLibrary)
![Issue](https://img.shields.io/github/issues/KenanZhu/AutoLibrary) [![Build](https://img.shields.io/github/actions/workflow/status/KenanZhu/AutoLibrary/release.yml)](https://github.com/KenanZhu/AutoLibrary/actions/workflows/release.yml)
![Release](https://img.shields.io/github/v/release/KenanZhu/AutoLibrary) [![Release](https://img.shields.io/github/v/release/KenanZhu/AutoLibrary)](https://github.com/KenanZhu/AutoLibrary/releases)
![Download](https://img.shields.io/github/downloads/KenanZhu/AutoLibrary/total) ![Downloads](https://img.shields.io/github/downloads/KenanZhu/AutoLibrary/total)
了解更多请访问 [_AutoLibrary 网站_](http://autolibrary.cv) 了解更多请访问 [_AutoLibrary 网站_](http://autolibrary.cv)
@@ -23,9 +24,9 @@
*1,2,3 的具体操作方法和注意事项请访问我们的 [帮助手册](https://autolibrary.cv/docs/manual_lists.html)* *1,2,3 的具体操作方法和注意事项请访问我们的 [帮助手册](https://autolibrary.cv/docs/manual_lists.html)*
### 特点 ### 注意事项
#### 关于预约等操作的注意事项 #### 关于预约等操作
工具会自动处理登录过程的验证码识别过程,正常情况下单次识别准确率在 90% 以上,如遇验证码识别错误,大概率是校园网网络环境不佳导致的。 工具会自动处理登录过程的验证码识别过程,正常情况下单次识别准确率在 90% 以上,如遇验证码识别错误,大概率是校园网网络环境不佳导致的。
@@ -34,11 +35,11 @@
> [!NOTE] > [!NOTE]
> 工具仅作为正常的预约,签到和续约的图书馆辅助工具,请勿干扰图书馆的正常运行(如故意预约多个座位,或同时预约大量的用户等,对此影响图书馆正常运行本工具概不负责,请在善用工具方便自己的情况下尽量不用影响其他同学的使用)。 > 工具仅作为正常的预约,签到和续约的图书馆辅助工具,请勿干扰图书馆的正常运行(如故意预约多个座位,或同时预约大量的用户等,对此影响图书馆正常运行本工具概不负责,请在善用工具方便自己的情况下尽量不用影响其他同学的使用)。
#### 关于批量操作的注意事项 #### 关于批量操作
批量操作时,建议将需要操作的用户分成多个组,每个组的用户数量不要超过 4 人(即一整张桌子的数量),否则会影响操作效率,大量用户同时预约会一定程度上增加图书馆服务器的压力,影响正常使用。根据需要在用户管理界面中可以勾选本次操作是否跳过该用户,以提高运行效率。 批量操作时,建议将需要操作的用户分成多个组,每个组的用户数量不要超过 4 人(即一整张桌子的数量),否则会影响操作效率,大量用户同时预约会一定程度上增加图书馆服务器的压力,影响正常使用。根据需要在用户管理界面中可以勾选本次操作是否跳过该用户,以提高运行效率。
#### 关于定时任务的注意事项 #### 关于定时任务
定时任务会在指定的时间自动运行,运行时会根据当前预约信息进行操作。一般情况下不建议设置两个运行开始时间比较接近的定时任务,否则后一个任务会等待前一个任务完成后才会运行,按照队列的顺序执行。 定时任务会在指定的时间自动运行,运行时会根据当前预约信息进行操作。一般情况下不建议设置两个运行开始时间比较接近的定时任务,否则后一个任务会等待前一个任务完成后才会运行,按照队列的顺序执行。
@@ -97,7 +98,7 @@ def classification(self, img: bytes):
#### 后续会有哪些功能? #### 后续会有哪些功能?
当前 v1.0.0 版本的功能对于正常使用已经足够,不过后续会着重考虑完善 2-4 人预约时的使用体验,暂时有以下构想: 当前版本的功能对于正常使用已经足够,不过后续会着重考虑完善 2-4 人预约时的使用体验,暂时有以下构想:
1. 2-4 人一起预约时,往往会偏向于预约并排或对面的整个空座位,这时候工具会按照一定策略查询搜索符合条件的座位,并预约并排或对面的整个座位,而不是各自独立预约。 1. 2-4 人一起预约时,往往会偏向于预约并排或对面的整个空座位,这时候工具会按照一定策略查询搜索符合条件的座位,并预约并排或对面的整个座位,而不是各自独立预约。
2. 预约时会考虑到组内用户的预约时间是否冲突,若冲突则会提示用户是否继续预约,若用户选择继续预约,则会按需要调整预约时间,再进行预约。 2. 预约时会考虑到组内用户的预约时间是否冲突,若冲突则会提示用户是否继续预约,若用户选择继续预约,则会按需要调整预约时间,再进行预约。
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. This software is provided "as is", without any warranty of any kind.
+8 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. This software is provided "as is", without any warranty of any kind.
@@ -13,6 +13,13 @@ from base.MsgBase import MsgBase
class LibOperator(MsgBase): class LibOperator(MsgBase):
"""
Base abstract class for library operation.
This class provides the foundation for library-related operations, inheriting
message handling and tracing abilities from MsgBase. It serves as an abstract
base class that must be subclassed to implement specific library functionality.
"""
def __init__( def __init__(
self, self,
+17 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. This software is provided "as is", without any warranty of any kind.
@@ -12,6 +12,22 @@ import queue
class MsgBase: class MsgBase:
"""
Base class for message and trace abilities (thread-safe).
This class provides the foundation for message handling and tracing
abilities based on the provided input and output queues. It enables
thread-safe communication between components using queue-based messaging.
Args:
input_queue (queue.Queue): The input queue for receiving messages.
output_queue (queue.Queue): The output queue for sending messages.
Usage:
This class must be initialized with input and output queues. The queue
provider (the caller of this class or its subclasses) must explicitly
implement queue polling to retrieve and process messages.
"""
def __init__( def __init__(
self, self,
+2 -2
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. This software is provided "as is", without any warranty of any kind.
@@ -60,7 +60,7 @@ class ALAboutDialog(QDialog, Ui_ALAboutDialog):
def generateAboutText( def generateAboutText(
self self
): ) -> str:
os_info = self.getOSInfo() os_info = self.getOSInfo()
about_text = f""" about_text = f"""
+1 -1
View File
@@ -104,7 +104,7 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QFrame" name="frame"> <widget class="QFrame" name="AboutInfoSpaceFrame">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>56</width> <width>56</width>
+3 -10
View File
@@ -1,31 +1,24 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. 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. You may use, modify, and distribute this file under the terms of the MIT License.
See the LICENSE file for details. See the LICENSE file for details.
""" """
import os
import sys
import time
import uuid import uuid
import queue
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 (
Qt, Signal, Slot, QDateTime Slot, QDateTime
) )
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QLabel, QDialog, QWidget, QSpinBox, QVBoxLayout, QLabel, QDialog, QWidget, QSpinBox,
QHBoxLayout, QGridLayout, QDateTimeEdit QHBoxLayout, QGridLayout, QDateTimeEdit
) )
from PySide6.QtGui import (
QCloseEvent
)
from gui.Ui_ALAddTimerTaskDialog import Ui_ALAddTimerTaskDialog from gui.Ui_ALAddTimerTaskDialog import Ui_ALAddTimerTaskDialog
+8 -8
View File
@@ -86,16 +86,16 @@
<number>5</number> <number>5</number>
</property> </property>
<property name="leftMargin"> <property name="leftMargin">
<number>5</number> <number>3</number>
</property> </property>
<property name="topMargin"> <property name="topMargin">
<number>5</number> <number>3</number>
</property> </property>
<property name="rightMargin"> <property name="rightMargin">
<number>5</number> <number>3</number>
</property> </property>
<property name="bottomMargin"> <property name="bottomMargin">
<number>5</number> <number>3</number>
</property> </property>
<item> <item>
<layout class="QHBoxLayout" name="TimerTypeSelectLayout"> <layout class="QHBoxLayout" name="TimerTypeSelectLayout">
@@ -135,16 +135,16 @@
</property> </property>
<layout class="QGridLayout" name="TaskConfigLayout"> <layout class="QGridLayout" name="TaskConfigLayout">
<property name="leftMargin"> <property name="leftMargin">
<number>5</number> <number>3</number>
</property> </property>
<property name="topMargin"> <property name="topMargin">
<number>5</number> <number>3</number>
</property> </property>
<property name="rightMargin"> <property name="rightMargin">
<number>5</number> <number>3</number>
</property> </property>
<property name="bottomMargin"> <property name="bottomMargin">
<number>5</number> <number>3</number>
</property> </property>
<property name="spacing"> <property name="spacing">
<number>5</number> <number>5</number>
+9 -9
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. This software is provided "as is", without any warranty of any kind.
@@ -33,7 +33,7 @@ from utils.ConfigWriter import ConfigWriter
class ALConfigWidget(QWidget, Ui_ALConfigWidget): class ALConfigWidget(QWidget, Ui_ALConfigWidget):
configWidgetCloseSingal = Signal(dict) configWidgetIsClosed = Signal(dict)
def __init__( def __init__(
self, self,
@@ -45,12 +45,11 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
): ):
super().__init__(parent) super().__init__(parent)
self.setupUi(self)
self.__config_paths = config_paths self.__config_paths = config_paths
self.__config_data = {"run": {}, "user": {}} self.__config_data = {"run": {}, "user": {}}
self.__seat_map_widget = None self.__seat_map_widget = None
self.setupUi(self)
self.modifyUi() self.modifyUi()
self.connectSignals() self.connectSignals()
self.initlizeFloorRoomMap() self.initlizeFloorRoomMap()
@@ -127,7 +126,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
event: QCloseEvent event: QCloseEvent
): ):
self.configWidgetCloseSingal.emit(self.__config_paths) self.configWidgetIsClosed.emit(self.__config_paths)
super().closeEvent(event) super().closeEvent(event)
@@ -143,12 +142,12 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
} }
self.__room_map = { self.__room_map = {
"1": "二层内环", "1": "二层内环",
"2": "二层外环", "2": "二层西区",
"3": "三层内环", "3": "三层内环",
"4": "三层外环", "4": "三层外环",
"5": "四层内环", "5": "四层内环",
"6": "四层外环", "6": "四层外环",
"7": "四层期刊", "7": "四层期刊",
"8": "五层考研" "8": "五层考研"
} }
self.__floor_rmap = { self.__floor_rmap = {
@@ -158,9 +157,9 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
v: k for k, v in self.__room_map.items() v: k for k, v in self.__room_map.items()
} }
self.__floor_room_map = { self.__floor_room_map = {
"二层": ["二层内环", "二层外环"], "二层": ["二层内环", "二层西区"],
"三层": ["三层内环", "三层外环"], "三层": ["三层内环", "三层外环"],
"四层": ["四层内环", "四层外环", "四层期刊"], "四层": ["四层内环", "四层外环", "四层期刊"],
"五层": ["五层考研"] "五层": ["五层考研"]
} }
@@ -803,6 +802,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
self.__seat_map_widget.deleteLater() self.__seat_map_widget.deleteLater()
self.__seat_map_widget = None self.__seat_map_widget = None
if len(selected_seats) == 0: if len(selected_seats) == 0:
self.SeatIDEdit.clear() # no selected seat, we clear the edit
return return
self.SeatIDEdit.setText(",".join(selected_seats)) self.SeatIDEdit.setText(",".join(selected_seats))
+61 -15
View File
@@ -94,19 +94,19 @@
</property> </property>
<layout class="QVBoxLayout" name="UserListLayout"> <layout class="QVBoxLayout" name="UserListLayout">
<property name="spacing"> <property name="spacing">
<number>0</number> <number>5</number>
</property> </property>
<property name="leftMargin"> <property name="leftMargin">
<number>5</number> <number>3</number>
</property> </property>
<property name="topMargin"> <property name="topMargin">
<number>5</number> <number>3</number>
</property> </property>
<property name="rightMargin"> <property name="rightMargin">
<number>5</number> <number>3</number>
</property> </property>
<property name="bottomMargin"> <property name="bottomMargin">
<number>5</number> <number>3</number>
</property> </property>
<item> <item>
<widget class="QTreeWidget" name="UserTreeWidget"> <widget class="QTreeWidget" name="UserTreeWidget">
@@ -178,6 +178,9 @@
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="UserListControlLayout"> <layout class="QHBoxLayout" name="UserListControlLayout">
<property name="spacing">
<number>5</number>
</property>
<item> <item>
<widget class="QPushButton" name="DelUserButton"> <widget class="QPushButton" name="DelUserButton">
<property name="minimumSize"> <property name="minimumSize">
@@ -309,7 +312,7 @@
<item row="2" column="1"> <item row="2" column="1">
<layout class="QHBoxLayout" name="PasswordLayout"> <layout class="QHBoxLayout" name="PasswordLayout">
<property name="spacing"> <property name="spacing">
<number>0</number> <number>5</number>
</property> </property>
<item> <item>
<widget class="QLineEdit" name="PasswordEdit"> <widget class="QLineEdit" name="PasswordEdit">
@@ -546,7 +549,7 @@
<item row="4" column="4"> <item row="4" column="4">
<layout class="QHBoxLayout" name="SeatIDLayout"> <layout class="QHBoxLayout" name="SeatIDLayout">
<property name="spacing"> <property name="spacing">
<number>0</number> <number>5</number>
</property> </property>
<item> <item>
<widget class="QLineEdit" name="SeatIDEdit"> <widget class="QLineEdit" name="SeatIDEdit">
@@ -703,7 +706,7 @@
<item row="10" column="4"> <item row="10" column="4">
<layout class="QHBoxLayout" name="EndTimeDiffLayout"> <layout class="QHBoxLayout" name="EndTimeDiffLayout">
<property name="spacing"> <property name="spacing">
<number>0</number> <number>5</number>
</property> </property>
<item> <item>
<widget class="QSpinBox" name="MaxEndTimeDiffSpinBox"> <widget class="QSpinBox" name="MaxEndTimeDiffSpinBox">
@@ -899,13 +902,13 @@
<item row="7" column="4"> <item row="7" column="4">
<layout class="QHBoxLayout" name="BeginTimeDiffLayout"> <layout class="QHBoxLayout" name="BeginTimeDiffLayout">
<property name="spacing"> <property name="spacing">
<number>0</number> <number>5</number>
</property> </property>
<item> <item>
<widget class="QSpinBox" name="MaxBeginTimeDiffSpinBox"> <widget class="QSpinBox" name="MaxBeginTimeDiffSpinBox">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>65</width> <width>55</width>
<height>25</height> <height>25</height>
</size> </size>
</property> </property>
@@ -1049,10 +1052,23 @@
<item row="15" column="4"> <item row="15" column="4">
<layout class="QHBoxLayout" name="RenewTimeDiffLayout"> <layout class="QHBoxLayout" name="RenewTimeDiffLayout">
<property name="spacing"> <property name="spacing">
<number>0</number> <number>5</number>
</property> </property>
<item> <item>
<widget class="QSpinBox" name="MaxRenewTimeDiffSpinBox"/> <widget class="QSpinBox" name="MaxRenewTimeDiffSpinBox">
<property name="minimumSize">
<size>
<width>55</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>25</height>
</size>
</property>
</widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="PreferLateRenewTimeCheckBox"> <widget class="QCheckBox" name="PreferLateRenewTimeCheckBox">
@@ -1079,7 +1095,7 @@
</layout> </layout>
</item> </item>
<item row="15" column="1"> <item row="15" column="1">
<widget class="QLabel" name="label"> <widget class="QLabel" name="MaxRenewTimeDiffLabel">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>80</width> <width>80</width>
@@ -1140,10 +1156,10 @@
<property name="rightMargin"> <property name="rightMargin">
<number>3</number> <number>3</number>
</property> </property>
<property name="horizontalSpacing"> <property name="bottomMargin">
<number>3</number> <number>3</number>
</property> </property>
<property name="verticalSpacing"> <property name="spacing">
<number>5</number> <number>5</number>
</property> </property>
<item row="0" column="0" colspan="2"> <item row="0" column="0" colspan="2">
@@ -1636,6 +1652,21 @@
<string>当前配置</string> <string>当前配置</string>
</property> </property>
<layout class="QGridLayout" name="CurrentConfigLayout"> <layout class="QGridLayout" name="CurrentConfigLayout">
<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>
<property name="spacing">
<number>5</number>
</property>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLineEdit" name="CurrentRunConfigEdit"> <widget class="QLineEdit" name="CurrentRunConfigEdit">
<property name="minimumSize"> <property name="minimumSize">
@@ -1762,6 +1793,21 @@
<string>导出路径</string> <string>导出路径</string>
</property> </property>
<layout class="QGridLayout" name="ExportConfigLayout"> <layout class="QGridLayout" name="ExportConfigLayout">
<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>
<property name="spacing">
<number>5</number>
</property>
<item row="3" column="0"> <item row="3" column="0">
<widget class="QLineEdit" name="ExportUserConfigEdit"> <widget class="QLineEdit" name="ExportUserConfigEdit">
<property name="minimumSize"> <property name="minimumSize">
+10 -15
View File
@@ -1,13 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. 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. You may use, modify, and distribute this file under the terms of the MIT License.
See the LICENSE file for details. See the LICENSE file for details.
""" """
import os
import sys import sys
import time import time
import queue import queue
@@ -30,9 +29,6 @@ from gui.ALMainWorkers import TimerTaskWorker, AutoLibWorker
from gui import AutoLibraryResource from gui import AutoLibraryResource
from utils.ConfigReader import ConfigReader
from utils.ConfigWriter import ConfigWriter
class ALMainWindow(QMainWindow, Ui_ALMainWindow): class ALMainWindow(QMainWindow, Ui_ALMainWindow):
@@ -46,8 +42,6 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
super().__init__() super().__init__()
self.__class_name = self.__class__.__name__ self.__class_name = self.__class__.__name__
self.setupUi(self)
self.__input_queue = queue.Queue() self.__input_queue = queue.Queue()
self.__output_queue = queue.Queue() self.__output_queue = queue.Queue()
self.__timer_task_queue = queue.Queue() self.__timer_task_queue = queue.Queue()
@@ -64,6 +58,7 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
self.__current_timer_task_thread = None self.__current_timer_task_thread = None
self.__is_running_timer_task = False self.__is_running_timer_task = False
self.setupUi(self)
self.modifyUi() self.modifyUi()
self.setupTray() self.setupTray()
self.connectSignals() self.connectSignals()
@@ -246,7 +241,7 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
self.__output_queue, self.__output_queue,
self.__config_paths self.__config_paths
) )
self.__current_timer_task_thread.finishedSignal_TimerWorker.connect(self.onTimerTaskFinished) self.__current_timer_task_thread.TimerTaskWorkerIsFinished.connect(self.onTimerTaskFinished)
self.__current_timer_task_thread.start() self.__current_timer_task_thread.start()
except queue.Empty: except queue.Empty:
self.__is_running_timer_task = False self.__is_running_timer_task = False
@@ -311,7 +306,7 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
): ):
if self.__alConfigWidget: if self.__alConfigWidget:
self.__alConfigWidget.configWidgetCloseSingal.disconnect(self.onConfigWidgetClosed) self.__alConfigWidget.configWidgetIsClosed.disconnect(self.onConfigWidgetClosed)
self.__alConfigWidget.deleteLater() self.__alConfigWidget.deleteLater()
self.__alConfigWidget = None self.__alConfigWidget = None
self.setControlButtons(True, None, None) self.setControlButtons(True, None, None)
@@ -333,7 +328,7 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
): ):
self.__current_timer_task_thread.wait(1000) self.__current_timer_task_thread.wait(1000)
self.__current_timer_task_thread.finishedSignal_TimerWorker.disconnect(self.onTimerTaskFinished) self.__current_timer_task_thread.TimerTaskWorkerIsFinished.disconnect(self.onTimerTaskFinished)
self.__current_timer_task_thread.deleteLater() self.__current_timer_task_thread.deleteLater()
self.__current_timer_task_thread = None self.__current_timer_task_thread = None
self.setControlButtons(None, False, True) self.setControlButtons(None, False, True)
@@ -374,7 +369,7 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
self, self,
self.__config_paths self.__config_paths
) )
self.__alConfigWidget.configWidgetCloseSingal.connect(self.onConfigWidgetClosed) self.__alConfigWidget.configWidgetIsClosed.connect(self.onConfigWidgetClosed)
self.__alConfigWidget.show() self.__alConfigWidget.show()
self.__alConfigWidget.raise_() self.__alConfigWidget.raise_()
self.__alConfigWidget.activateWindow() self.__alConfigWidget.activateWindow()
@@ -392,8 +387,8 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
self.__output_queue, self.__output_queue,
self.__config_paths self.__config_paths
) )
self.__auto_lib_thread.finishedSignal.connect(self.onStopButtonClicked) self.__auto_lib_thread.AutoLibWorkerIsFinished.connect(self.onStopButtonClicked)
self.__auto_lib_thread.finishedWithErrorSignal.connect(self.onStopButtonClicked) self.__auto_lib_thread.AutoLibWorkerFinishedWithError.connect(self.onStopButtonClicked)
self.__auto_lib_thread.start() self.__auto_lib_thread.start()
@Slot() @Slot()
@@ -405,8 +400,8 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
self.showTrace("正在停止操作......") self.showTrace("正在停止操作......")
self.__auto_lib_thread.wait(2000) self.__auto_lib_thread.wait(2000)
self.showTrace("操作已停止") self.showTrace("操作已停止")
self.__auto_lib_thread.finishedSignal.disconnect(self.onStopButtonClicked) self.__auto_lib_thread.AutoLibWorkerIsFinished.disconnect(self.onStopButtonClicked)
self.__auto_lib_thread.finishedWithErrorSignal.disconnect(self.onStopButtonClicked) self.__auto_lib_thread.AutoLibWorkerFinishedWithError.disconnect(self.onStopButtonClicked)
self.__auto_lib_thread.deleteLater() self.__auto_lib_thread.deleteLater()
self.__auto_lib_thread = None self.__auto_lib_thread = None
self.setControlButtons(None, False, True) self.setControlButtons(None, False, True)
+5 -7
View File
@@ -34,13 +34,13 @@
<number>5</number> <number>5</number>
</property> </property>
<property name="leftMargin"> <property name="leftMargin">
<number>3</number> <number>5</number>
</property> </property>
<property name="topMargin"> <property name="topMargin">
<number>0</number> <number>0</number>
</property> </property>
<property name="rightMargin"> <property name="rightMargin">
<number>3</number> <number>5</number>
</property> </property>
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
@@ -156,11 +156,9 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">background-color: rgb(10, 170, 10); <string notr="true">background-color: #0AAA0A;
font: 12pt &quot;Microsoft YaHei UI&quot;; color: #FFFFFF;
color: rgb(255, 255, 255); font: 700 9pt;</string>
font: 9pt &quot;Segoe UI&quot;;
font: 700 9pt &quot;Microsoft YaHei UI&quot;;</string>
</property> </property>
<property name="text"> <property name="text">
<string>启动脚本</string> <string>启动脚本</string>
+15 -19
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. This software is provided "as is", without any warranty of any kind.
@@ -22,8 +22,8 @@ from utils.ConfigReader import ConfigReader
class AutoLibWorker(QThread, MsgBase): class AutoLibWorker(QThread, MsgBase):
finishedSignal = Signal() AutoLibWorkerIsFinished = Signal()
finishedWithErrorSignal = Signal() AutoLibWorkerFinishedWithError = Signal()
def __init__( def __init__(
self, self,
@@ -32,7 +32,7 @@ class AutoLibWorker(QThread, MsgBase):
config_paths: dict config_paths: dict
): ):
super().__init__(input_queue = input_queue, output_queue = output_queue) super().__init__(input_queue=input_queue, output_queue=output_queue)
self.__config_paths = config_paths self.__config_paths = config_paths
@@ -69,15 +69,11 @@ class AutoLibWorker(QThread, MsgBase):
self._showTrace( self._showTrace(
f"正在加载配置文件, 运行配置文件路径: {self.__config_paths["run"]}" f"正在加载配置文件, 运行配置文件路径: {self.__config_paths["run"]}"
) )
self.__run_config = ConfigReader( self.__run_config = ConfigReader(self.__config_paths["run"]).getConfigs()
self.__config_paths["run"]
).getConfigs()
self._showTrace( self._showTrace(
f"正在加载配置文件, 用户配置文件路径: {self.__config_paths["user"]}" f"正在加载配置文件, 用户配置文件路径: {self.__config_paths["user"]}"
) )
self.__user_config = ConfigReader( self.__user_config = ConfigReader(self.__config_paths["user"]).getConfigs()
self.__config_paths["user"]
).getConfigs()
if self.__run_config is None or self.__user_config is None: if self.__run_config is None or self.__user_config is None:
self._showTrace("配置文件加载失败, 请检查配置文件是否正确") self._showTrace("配置文件加载失败, 请检查配置文件是否正确")
self._showTrace("配置文件加载失败, 请检查配置文件是否正确") self._showTrace("配置文件加载失败, 请检查配置文件是否正确")
@@ -120,17 +116,17 @@ class AutoLibWorker(QThread, MsgBase):
) )
except Exception as e: except Exception as e:
self._showTrace(f"AutoLibrary 运行时发生异常 : {e}") self._showTrace(f"AutoLibrary 运行时发生异常 : {e}")
self.finishedWithErrorSignal.emit() self.AutoLibWorkerFinishedWithError.emit()
return return
if auto_lib: if auto_lib:
auto_lib.close() auto_lib.close()
self._showTrace("AutoLibrary 运行结束") self._showTrace("AutoLibrary 运行结束")
self.finishedSignal.emit() self.AutoLibWorkerIsFinished.emit()
class TimerTaskWorker(AutoLibWorker): class TimerTaskWorker(AutoLibWorker):
finishedSignal_TimerWorker = Signal(bool, dict) TimerTaskWorkerIsFinished = Signal(bool, dict)
def __init__( def __init__(
self, self,
@@ -143,8 +139,8 @@ class TimerTaskWorker(AutoLibWorker):
super().__init__(input_queue, output_queue, config_paths) super().__init__(input_queue, output_queue, config_paths)
self.__timer_task = timer_task self.__timer_task = timer_task
self.finishedSignal.connect(self.onTimerTaskIsFinished) self.AutoLibWorkerIsFinished.connect(self.onTimerTaskIsFinished)
self.finishedWithErrorSignal.connect(self.onTimerTaskIsError) self.AutoLibWorkerFinishedWithError.connect(self.onTimerTaskIsError)
def run( def run(
self self
@@ -153,18 +149,18 @@ class TimerTaskWorker(AutoLibWorker):
self._showTrace(f"定时任务 {self.__timer_task['name']} 开始运行") self._showTrace(f"定时任务 {self.__timer_task['name']} 开始运行")
super().run() super().run()
@Slot(dict) @Slot()
def onTimerTaskIsError( def onTimerTaskIsError(
self self
): ):
self._showTrace(f"定时任务 {self.__timer_task['name']} 运行时发生异常") self._showTrace(f"定时任务 {self.__timer_task['name']} 运行时发生异常")
self.finishedSignal_TimerWorker.emit(True, self.__timer_task) self.TimerTaskWorkerIsFinished.emit(True, self.__timer_task)
@Slot(dict) @Slot()
def onTimerTaskIsFinished( def onTimerTaskIsFinished(
self self
): ):
self._showTrace(f"定时任务 {self.__timer_task['name']} 运行结束") self._showTrace(f"定时任务 {self.__timer_task['name']} 运行结束")
self.finishedSignal_TimerWorker.emit(False, self.__timer_task) self.TimerTaskWorkerIsFinished.emit(False, self.__timer_task)
+15 -13
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. This software is provided "as is", without any warranty of any kind.
@@ -22,12 +22,13 @@ class ALSeatFrame(QFrame):
def __init__( def __init__(
self, self,
seat_number, seat_number,
parent=None parent = None
): ):
super().__init__(parent) super().__init__(parent)
self.__seat_number = seat_number self.__seat_number = seat_number
self.__is_selected = False self.__is_selected = False
self.setupUi() self.setupUi()
def setupUi( def setupUi(
@@ -39,18 +40,19 @@ class ALSeatFrame(QFrame):
self.setLineWidth(2) self.setLineWidth(2)
self.setStyleSheet(""" self.setStyleSheet("""
QFrame { QFrame {
background-color: #4196EB; background-color: #2294FF;
border: 2px solid #4196EB; border: 2px solid #2294FF;
border-radius: 5px; border-radius: 5px;
} }
QLabel { QLabel {
color: #F0F0F0; color: #FFFFFF;
font-weight: bold; font-weight: bold;
} }
""") """)
self.label = QLabel(self.__seat_number, self) self.setCursor(Qt.CursorShape.PointingHandCursor)
self.label.setAlignment(Qt.AlignCenter) self.Label = QLabel(self.__seat_number, self)
self.label.setGeometry(0, 0, 60, 40) self.Label.setAlignment(Qt.AlignCenter)
self.Label.setGeometry(0, 0, 60, 40)
def mousePressEvent( def mousePressEvent(
self, self,
@@ -76,24 +78,24 @@ class ALSeatFrame(QFrame):
self.setStyleSheet(""" self.setStyleSheet("""
QFrame { QFrame {
background-color: #4CAF50; background-color: #4CAF50;
border: 2px solid #388E3C; border: 2px solid #4CAF50;
border-radius: 5px; border-radius: 5px;
color: white; color: white;
} }
QLabel { QLabel {
color: #F0F0F0; color: #FFFFFF;
font-weight: bold; font-weight: bold;
} }
""") """)
else: else:
self.setStyleSheet(""" self.setStyleSheet("""
QFrame { QFrame {
background-color: #4196EB; background-color: #2294FF;
border: 2px solid #4196EB; border: 2px solid #2294FF;
border-radius: 5px; border-radius: 5px;
} }
QLabel { QLabel {
color: #F0F0F0; color: #FFFFFF;
font-weight: bold; font-weight: bold;
} }
""") """)
+13 -3
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. This software is provided "as is", without any warranty of any kind.
@@ -34,12 +34,13 @@ class ALSeatMapWidget(QWidget):
): ):
super().__init__(parent) super().__init__(parent)
self.__floor = floor self.__floor = floor
self.__room = room self.__room = room
self.__seats_data = seats_data self.__seats_data = seats_data
self.__selected_seats = [] self.__selected_seats = []
self.__seat_frames = {} self.__seat_frames = {}
self.__confirmed = False
self.setupUi() self.setupUi()
self.connectSignals() self.connectSignals()
@@ -66,6 +67,8 @@ class ALSeatMapWidget(QWidget):
self.setWindowTitle(f"选择楼层座位 - AutoLibrary") self.setWindowTitle(f"选择楼层座位 - AutoLibrary")
self.SeatMapWidgetMainLayout = QVBoxLayout(self) self.SeatMapWidgetMainLayout = QVBoxLayout(self)
self.SeatMapWidgetMainLayout.setContentsMargins(5, 5, 5, 5)
self.SeatMapWidgetMainLayout.setSpacing(5)
self.TitleLabel = QLabel(f"楼层座位分布图: {self.__floor}-{self.__room}") self.TitleLabel = QLabel(f"楼层座位分布图: {self.__floor}-{self.__room}")
self.TitleLabel.setAlignment(Qt.AlignmentFlag.AlignCenter) self.TitleLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.TitleLabel.setStyleSheet("font-size: 16px; font-weight: bold; margin: 10px;") self.TitleLabel.setStyleSheet("font-size: 16px; font-weight: bold; margin: 10px;")
@@ -98,9 +101,13 @@ class ALSeatMapWidget(QWidget):
self.ConfirmButton = QPushButton("确认") self.ConfirmButton = QPushButton("确认")
self.ConfirmButton.setFixedSize(80, 25) self.ConfirmButton.setFixedSize(80, 25)
self.ConfirmButton.setAutoDefault(True)
self.ConfirmButton.setDefault(True)
self.CancelButton = QPushButton("取消") self.CancelButton = QPushButton("取消")
self.CancelButton.setFixedSize(80, 25) self.CancelButton.setFixedSize(80, 25)
self.SeatMapWidgetControlLayout = QHBoxLayout() self.SeatMapWidgetControlLayout = QHBoxLayout()
self.SeatMapWidgetControlLayout.setContentsMargins(0, 0, 0, 0)
self.SeatMapWidgetControlLayout.setSpacing(5)
self.SeatMapWidgetControlLayout.setAlignment(Qt.AlignmentFlag.AlignRight) self.SeatMapWidgetControlLayout.setAlignment(Qt.AlignmentFlag.AlignRight)
self.SeatMapWidgetControlLayout.addWidget(self.CancelButton) self.SeatMapWidgetControlLayout.addWidget(self.CancelButton)
self.SeatMapWidgetControlLayout.addWidget(self.ConfirmButton) self.SeatMapWidgetControlLayout.addWidget(self.ConfirmButton)
@@ -144,6 +151,8 @@ class ALSeatMapWidget(QWidget):
event: QCloseEvent event: QCloseEvent
): ):
if not self.__confirmed:
self.clearSelections()
self.seatMapWidgetClosed.emit(self.__selected_seats) self.seatMapWidgetClosed.emit(self.__selected_seats)
super().closeEvent(event) super().closeEvent(event)
@@ -265,6 +274,7 @@ class ALSeatMapWidget(QWidget):
self self
): ):
self.__confirmed = True
self.close() self.close()
@Slot() @Slot()
@@ -272,5 +282,5 @@ class ALSeatMapWidget(QWidget):
self self
): ):
self.clearSelections() self.__confirmed = False
self.close() self.close()
+8 -11
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. This software is provided "as is", without any warranty of any kind.
@@ -8,10 +8,7 @@ You may use, modify, and distribute this file under the terms of the MIT License
See the LICENSE file for details. See the LICENSE file for details.
""" """
import os import os
import sys
import time
import copy import copy
import queue
from enum import Enum from enum import Enum
from datetime import datetime, timedelta from datetime import datetime, timedelta
@@ -24,7 +21,7 @@ from PySide6.QtWidgets import (
QHBoxLayout, QVBoxLayout, QLabel, QPushButton QHBoxLayout, QVBoxLayout, QLabel, QPushButton
) )
from PySide6.QtGui import ( from PySide6.QtGui import (
QCloseEvent, QScreen QCloseEvent
) )
from gui.Ui_ALTimerTaskWidget import Ui_ALTimerTaskWidget from gui.Ui_ALTimerTaskWidget import Ui_ALTimerTaskWidget
@@ -50,8 +47,8 @@ class TimerTaskItemWidget(QWidget):
): ):
super().__init__(parent) super().__init__(parent)
self.__timer_task = timer_task self.__timer_task = timer_task
self.modifyUi() self.modifyUi()
@@ -74,7 +71,7 @@ class TimerTaskItemWidget(QWidget):
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}") ExecuteTimeLabel = QLabel(f"执行时间: {ExecuteTimeStr}")
ExecuteTimeLabel.setStyleSheet("color: gray;") ExecuteTimeLabel.setStyleSheet("color: #969696;")
ExecuteTimeLabel.setFixedHeight(20) ExecuteTimeLabel.setFixedHeight(20)
self.TaskInfoLayout.addWidget(ExecuteTimeLabel) self.TaskInfoLayout.addWidget(ExecuteTimeLabel)
@@ -96,15 +93,15 @@ class TimerTaskItemWidget(QWidget):
TaskStatusColor = "#4CAF50" TaskStatusColor = "#4CAF50"
case TimerTaskStatus.ERROR: case TimerTaskStatus.ERROR:
TaskStatusText = "执行失败" TaskStatusText = "执行失败"
TaskStatusColor = "#FF5722" TaskStatusColor = "#DC0000"
case TimerTaskStatus.OUTDATED: case TimerTaskStatus.OUTDATED:
TaskStatusText = "已过期" TaskStatusText = "已过期"
TaskStatusColor = "#FF5722" TaskStatusColor = "#DC0000"
TaskStatusLabel = QLabel(TaskStatusText) TaskStatusLabel = QLabel(TaskStatusText)
TaskStatusLabel.setStyleSheet(f""" TaskStatusLabel.setStyleSheet(f"""
QLabel {{ QLabel {{
background-color: {TaskStatusColor}; background-color: {TaskStatusColor};
color: white; color: #FFFFFF;
border-radius: 5px; border-radius: 5px;
font-weight: bold; font-weight: bold;
}} }}
@@ -119,7 +116,7 @@ class TimerTaskItemWidget(QWidget):
TaskModeLabel.setStyleSheet(f""" TaskModeLabel.setStyleSheet(f"""
QLabel {{ QLabel {{
background-color: {TaskModeColor}; background-color: {TaskModeColor};
color: white; color: #FFFFFF;
border-radius: 5px; border-radius: 5px;
font-weight: bold; font-weight: bold;
}} }}
+1 -1
View File
@@ -153,7 +153,7 @@
</property> </property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QLabel { <string notr="true">QLabel {
color: #FF5722 color: #DC0000
}</string> }</string>
</property> </property>
<property name="text"> <property name="text">
+1 -2
View File
@@ -1,13 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. 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. You may use, modify, and distribute this file under the terms of the MIT License.
See the LICENSE file for details. See the LICENSE file for details.
""" """
from enum import Enum from enum import Enum
from PySide6.QtCore import ( from PySide6.QtCore import (
+3 -3
View File
@@ -5,11 +5,11 @@
workflow process. Do not edit manually. workflow process. Do not edit manually.
This file is auto-generated during the workflow process. This file is auto-generated during the workflow process.
Last updated: 2026-01-05 04:04:54 UTC Last updated: 2026-01-17 18:18:12 UTC
""" """
AL_VERSION = "1.0.2" AL_VERSION = "1.0.4"
AL_TAG = "v1.0.2" AL_TAG = "v1.0.4"
AL_COMMIT_SHA = "local" AL_COMMIT_SHA = "local"
AL_COMMIT_DATE = "null" # time zone : UTC AL_COMMIT_DATE = "null" # time zone : UTC
AL_BUILD_DATE = "null" # time zone : UTC AL_BUILD_DATE = "null" # time zone : UTC
+2 -6
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. This software is provided "as is", without any warranty of any kind.
@@ -26,8 +26,6 @@ from operators.LibReserve import LibReserve
from operators.LibCheckin import LibCheckin from operators.LibCheckin import LibCheckin
from operators.LibRenew import LibRenew from operators.LibRenew import LibRenew
from utils.ConfigReader import ConfigReader
class AutoLib(MsgBase): class AutoLib(MsgBase):
@@ -214,9 +212,7 @@ class AutoLib(MsgBase):
login_config.get("auto_captcha", True), login_config.get("auto_captcha", True),
): ):
return 1 return 1
""" # Here, we collect the run mode from the run config.
Here, we collect the run mode from the run config.
"""
run_mode = run_mode_config.get("run_mode", 0) run_mode = run_mode_config.get("run_mode", 0)
run_mode = { run_mode = {
"auto_reserve": run_mode&0x1, "auto_reserve": run_mode&0x1,
+9 -8
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. This software is provided "as is", without any warranty of any kind.
@@ -13,6 +13,7 @@ import queue
from datetime import datetime, timedelta from datetime import datetime, timedelta
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.webdriver import WebDriver
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support import expected_conditions as EC
@@ -25,7 +26,7 @@ class LibChecker(LibOperator):
self, self,
input_queue: queue.Queue, input_queue: queue.Queue,
output_queue: queue.Queue, output_queue: queue.Queue,
driver: any driver: WebDriver
): ):
super().__init__(input_queue, output_queue) super().__init__(input_queue, output_queue)
@@ -336,15 +337,15 @@ class LibChecker(LibOperator):
def postRenewCheck( def postRenewCheck(
self, self,
record: dict record: dict
): ) -> bool:
""" """
Check if the renew operation is successful Check if the renew operation is successful
Args: Args:
record (dict): The expected record after renewal record (dict): The expected record after renewal
Returns: Returns:
bool: True if the renew operation is successful, False otherwise bool: True if the renew operation is successful, False otherwise
""" """
# because the special circumstance that the renew operation # because the special circumstance that the renew operation
# do not show the success message or anything else, # do not show the success message or anything else,
+3 -4
View File
@@ -1,18 +1,17 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. 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. You may use, modify, and distribute this file under the terms of the MIT License.
See the LICENSE file for details. See the LICENSE file for details.
""" """
import re
import time import time
import queue import queue
from datetime import datetime, timedelta
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.webdriver import WebDriver
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support import expected_conditions as EC
@@ -25,7 +24,7 @@ class LibCheckin(LibOperator):
self, self,
input_queue: queue.Queue, input_queue: queue.Queue,
output_queue: queue.Queue, output_queue: queue.Queue,
driver: any driver: WebDriver
): ):
super().__init__(input_queue, output_queue) super().__init__(input_queue, output_queue)
+3 -2
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. This software is provided "as is", without any warranty of any kind.
@@ -13,6 +13,7 @@ import queue
from datetime import datetime, timedelta from datetime import datetime, timedelta
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.webdriver import WebDriver
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support import expected_conditions as EC
@@ -25,7 +26,7 @@ class LibCheckout(LibOperator):
self, self,
input_queue: queue.Queue, input_queue: queue.Queue,
output_queue: queue.Queue, output_queue: queue.Queue,
driver: any driver: WebDriver
): ):
super().__init__(input_queue, output_queue) super().__init__(input_queue, output_queue)
+3 -3
View File
@@ -1,19 +1,19 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. 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. You may use, modify, and distribute this file under the terms of the MIT License.
See the LICENSE file for details. See the LICENSE file for details.
""" """
import time
import queue import queue
import base64 import base64
import ddddocr import ddddocr
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.webdriver import WebDriver
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support import expected_conditions as EC
@@ -26,7 +26,7 @@ class LibLogin(LibOperator):
self, self,
input_queue: queue.Queue, input_queue: queue.Queue,
output_queue: queue.Queue, output_queue: queue.Queue,
driver: any driver: WebDriver
): ):
super().__init__(input_queue, output_queue) super().__init__(input_queue, output_queue)
+3 -4
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. This software is provided "as is", without any warranty of any kind.
@@ -10,8 +10,7 @@ See the LICENSE file for details.
import queue import queue
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.chrome.webdriver import WebDriver
from selenium.webdriver.support import expected_conditions as EC
from base.LibOperator import LibOperator from base.LibOperator import LibOperator
@@ -22,7 +21,7 @@ class LibLogout(LibOperator):
self, self,
input_queue: queue.Queue, input_queue: queue.Queue,
output_queue: queue.Queue, output_queue: queue.Queue,
driver: any driver: WebDriver
): ):
super().__init__(input_queue, output_queue) super().__init__(input_queue, output_queue)
+3 -5
View File
@@ -1,18 +1,16 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. 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. You may use, modify, and distribute this file under the terms of the MIT License.
See the LICENSE file for details. See the LICENSE file for details.
""" """
import os
import time
import queue import queue
from datetime import datetime, timedelta
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.webdriver import WebDriver
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support import expected_conditions as EC
@@ -25,7 +23,7 @@ class LibRenew(LibOperator):
self, self,
input_queue: queue.Queue, input_queue: queue.Queue,
output_queue: queue.Queue, output_queue: queue.Queue,
driver: any driver: WebDriver
): ):
super().__init__(input_queue, output_queue) super().__init__(input_queue, output_queue)
+6 -6
View File
@@ -1,18 +1,18 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. 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. You may use, modify, and distribute this file under the terms of the MIT License.
See the LICENSE file for details. See the LICENSE file for details.
""" """
import re
import time import time
import queue import queue
from datetime import datetime, timedelta from datetime import datetime
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.webdriver import WebDriver
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support import expected_conditions as EC
@@ -25,7 +25,7 @@ class LibReserve(LibOperator):
self, self,
input_queue: queue.Queue, input_queue: queue.Queue,
output_queue: queue.Queue, output_queue: queue.Queue,
driver: any driver: WebDriver
): ):
super().__init__(input_queue, output_queue) super().__init__(input_queue, output_queue)
@@ -40,12 +40,12 @@ class LibReserve(LibOperator):
} }
self.__room_map = { self.__room_map = {
"1": "二层内环", "1": "二层内环",
"2": "二层外环", "2": "二层西区",
"3": "三层内环", "3": "三层内环",
"4": "三层外环", "4": "三层外环",
"5": "四层内环", "5": "四层内环",
"6": "四层外环", "6": "四层外环",
"7": "四层期刊", "7": "四层期刊",
"8": "五层考研" "8": "五层考研"
} }
+1
View File
@@ -8,5 +8,6 @@
- LibReserve: Library operator for reserving seat. - LibReserve: Library operator for reserving seat.
- LibCheckin: Library operator for checking in seat. - LibCheckin: Library operator for checking in seat.
- LibCheckout: Library operator for checking out seat. - LibCheckout: Library operator for checking out seat.
- LibChecker: Library operator for checking record status.
- LibRenew: Library operator for renewing seat. - LibRenew: Library operator for renewing seat.
""" """
+45 -19
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. This software is provided "as is", without any warranty of any kind.
@@ -8,63 +8,89 @@ You may use, modify, and distribute this file under the terms of the MIT License
See the LICENSE file for details. See the LICENSE file for details.
""" """
import json import json
import copy
from typing import Any
class ConfigReader: class ConfigReader:
"""
Config reader class.
This class is used to read config file in JSON format.
Args:
config_path (str): The path of config file.
Examples:
>>> print(open("config.json", "r", encoding="utf-8").read())
{
"key1": {
"key2": "value1"
}
}
>>> config_reader = ConfigReader("config.json")
>>> config_reader.get("key1/key2")
"value1"
"""
def __init__( def __init__(
self, self,
config_path: str config_path: str
): ):
self._config_path = config_path self.__config_path = config_path
self._config_data = {} self.__config_data = None
if not self.__readConfig(): self.__readConfig()
return None
def __readConfig( def __readConfig(
self self
) -> bool: ):
try: try:
with open(self._config_path, 'r', encoding='utf-8') as file: with open(self.__config_path, 'r', encoding='utf-8') as file:
self._config_data = json.load(file) self.__config_data = json.load(file)
return True except FileNotFoundError as e:
raise Exception(f"Config file not found: {self.__config_path}") from e
except PermissionError as e:
raise Exception(f"Without enough permission to read config file: {self.__config_path}") from e
except json.JSONDecodeError as e:
raise Exception(f"JSON decode error in config file: {self.__config_path}") from e
except Exception as e: except Exception as e:
print(f"Error reading config file: {e}") raise Exception(f"Unknown error occurred while reading config file: {e}") from e
return False
def getConfigs( def getConfigs(
self self
) -> dict: ) -> dict:
return self._config_data.copy() return self.__config_data.copy()
def getConfig( def getConfig(
self, self,
key: str key: str
) -> dict: ) -> Any:
return self._config_data.get(key, {}) config = self.__config_data.get(key, {})
return copy.deepcopy(config)
def get( def get(
self, self,
key: str, key: str,
default: any = None default: Any = None
) -> any: ) -> Any:
keys = key.split('/') keys = key.split('/')
current = self._config_data current = self.__config_data
for k in keys: for k in keys:
if isinstance(current, dict) and k in current: if isinstance(current, dict) and k in current:
current = current[k] current = current[k]
else: else:
return default return default
return current return copy.deepcopy(current)
def hasConfig( def hasConfig(
@@ -86,4 +112,4 @@ class ConfigReader:
self self
) -> str: ) -> str:
return self._config_path return self.__config_path
+41 -12
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Copyright (c) 2025 KenanZhu. Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved. All rights reserved.
This software is provided "as is", without any warranty of any kind. This software is provided "as is", without any warranty of any kind.
@@ -9,8 +9,35 @@ See the LICENSE file for details.
""" """
import json import json
from typing import Any
class ConfigWriter: class ConfigWriter:
"""
Config writer class.
This class is used to write config file in JSON format.
Args:
config_path (str): The path of config file.
config_data (dict): The config data to be written.
Examples:
>>> config_data = {
... "key1": {
... "key2": "value1"
... }
... }
>>> config_writer = ConfigWriter("config.json", config_data)
>>> config_writer.set("key1/key2", "value1")
True
>>> print(open("config.json", "r", encoding="utf-8").read())
{
"key1": {
"key2": "value1"
}
}
"""
def __init__( def __init__(
self, self,
@@ -19,23 +46,25 @@ class ConfigWriter:
): ):
self.__config_path = config_path self.__config_path = config_path
self.__config_data = config_data if config_data is not None else {} self.__config_data = config_data.copy() if config_data is not None else {}
if config_data is None: self.__writeConfig()
return None
if not self.__writeConfig():
return None
def __writeConfig( def __writeConfig(
self self
) -> bool: ):
try: try:
with open(self.__config_path, "w") as f: with open(self.__config_path, "w", encoding="utf-8") as f:
json.dump(self.__config_data, f, indent=4, sort_keys=False) json.dump(self.__config_data, f, indent=4, sort_keys=False)
return True except PermissionError as e:
except: raise Exception(f"Without enough permission to write config file: {self.__config_path}") from e
return False except IOError as e:
raise Exception(f"IO error occurred while writing config file: {self.__config_path}") from e
except TypeError as e:
raise Exception(f"Config data contains invalid type that can not be JSON serialized: {e}") from e
except Exception as e:
raise Exception(f"Unknown error occurred while writing config file: {e}") from e
def setConfigs( def setConfigs(
@@ -60,7 +89,7 @@ class ConfigWriter:
def set( def set(
self, self,
key: str, key: str,
value: dict value: Any
) -> bool: ) -> bool:
keys = key.replace("\\", "/").split("/") keys = key.replace("\\", "/").split("/")