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

Compare commits

...

9 Commits

Author SHA1 Message Date
KenanZhu 829a8440ad chore(gui): fix some widget length to match the design 2025-11-11 09:14:04 +08:00
KenanZhu 389ac885d3 fix(ALMainWindow): optimize the resource usage of gui
This commit fixes memory management issues in the
ALMainWindow class where config window and
task threads were not properly deleted after use,
leading to continuously increasing memory usage.

The fix ensures that all GUI components are
deleted after close and background threads are
correctly terminated.
2025-11-11 09:05:55 +08:00
KenanZhu 68b61b5c8c feat(AutoLib): new feature 'Auto Check-in' 2025-11-11 09:04:11 +08:00
KenanZhu fd5abb5f1e chore(LibReserve, LibCheckin): *
We use a more clear and structured output message
of reservation.

Complete the LibCheckin for the upcoming new
feature : 'Auto Check-in'
2025-11-11 09:00:20 +08:00
KenanZhu 1f16181aeb fix(LibLogin): more clear error message 2025-11-09 19:52:21 +08:00
KenanZhu f0c25903a3 refactor(LibReserve): optimize the pre-check of reserve
Extract the different pre-checks of reserve to
their separate methods

More clear role of 'satisfy_duration' flag
2025-11-09 19:40:08 +08:00
KenanZhu b24e4f473f fix(LibChecker): chore(LibChecker): introduce a new method to generate more readable output
: add renew checker method for upcoming feature: 'Auto Renew'
2025-11-08 18:34:38 +08:00
KenanZhu 8bb65be0b9 fix(LibChecker): remove the debug print 2025-11-08 18:32:33 +08:00
KenanZhu 631785122b fix(gui.ALConfigWidget): fix the width of web driver path line edit 2025-11-08 18:31:07 +08:00
7 changed files with 609 additions and 411 deletions
+23 -1
View File
@@ -21,6 +21,7 @@ from LibChecker import LibChecker
from LibLogin import LibLogin
from LibLogout import LibLogout
from LibReserve import LibReserve
from LibCheckin import LibCheckin
from ConfigReader import ConfigReader
@@ -112,6 +113,7 @@ class AutoLib(MsgBase):
self.__lib_login = LibLogin(self._input_queue, self._output_queue, self.__driver)
self.__lib_logout = LibLogout(self._input_queue, self._output_queue, self.__driver)
self.__lib_reserve = LibReserve(self._input_queue, self._output_queue, self.__driver)
self.__lib_checkin = LibCheckin(self._input_queue, self._output_queue, self.__driver)
def __waitResponseLoad(
@@ -159,7 +161,7 @@ class AutoLib(MsgBase):
) -> int:
# result : 0 - success, 1 - failed, 2 - passed
result = 1
result = 2
# login
if not self.__lib_login.login(
@@ -188,6 +190,26 @@ class AutoLib(MsgBase):
self._showTrace(f"用户 {username} 预约失败 !")
result = 1
else:
self._showTrace(f"用户 {username} 无法预约,已跳过")
result = 2
# checkin
if run_mode["auto_checkin"] and result == 2:
if self.__lib_checker.canCheckin(reserve_info.get("date")):
if self.__lib_checkin.checkin(username):
self._showTrace(f"用户 {username} 签到成功 !")
result = 0
else:
self._showTrace(f"用户 {username} 签到失败 !")
result = 1
else:
self._showTrace(f"用户 {username} 无法签到,已跳过")
result = 2
# renewal
if run_mode["auto_renewal"] and result == 2:
if self.__lib_checker.canRenew(reserve_info.get("date")):
pass
else:
self._showTrace(f"用户 {username} 无法续约,已跳过")
result = 2
# logout
if not self.__lib_logout.logout(
+43 -5
View File
@@ -39,6 +39,16 @@ class LibChecker(LibOperator):
pass
@staticmethod
def __formatDiffTime(
seconds: float
) -> str:
hours = int(seconds // 3600)
minutes = int(seconds % 3600 // 60)
seconds = int(seconds % 60)
return f"{hours}{minutes}{seconds}"
def __navigateToReserveRecordPage(
self
@@ -166,7 +176,6 @@ class LibChecker(LibOperator):
for i in range(checked_count, len(reservations)): # the last one is load button
reservation = reservations[i]
record = self.__decodeReserveRecord(reservation)
print(record)
if record is None:
continue
record_date = record["date"]
@@ -243,22 +252,51 @@ class LibChecker(LibOperator):
if time_diff_seconds < -30*60:
self._showTrace(
f"用户在 {date} 的预约开始时间为 {begin_time}, "
f"距离当前时间还有 {abs(time_diff_seconds)/60:.2f} 分钟, 无法签到"
f"距离当前时间还有 {self.__formatDiffTime(abs(time_diff_seconds))}, 无法签到"
)
return False
# before in 30 minutes, can checkin
elif -30*60 <= time_diff_seconds < 0:
self._showTrace(
f"用户在 {date} 的预约开始时间为 {begin_time}, "
f"距离当前时间还有 {abs(time_diff_seconds)/60:.2f} 分钟, 可以签到"
f"距离当前时间还有 {self.__formatDiffTime(abs(time_diff_seconds))}, 可以签到"
)
return True
# past less than 30 minutes, can checkin
elif 0 <= time_diff_seconds < 30*60:
self._showTrace(
f"用户在 {date} 的预约开始时间为 {begin_time}, "
f"当前时间已经 {abs(time_diff_seconds)/60:.2f} 分钟, 可以签到"
f"当前时间已经 {self.__formatDiffTime(abs(time_diff_seconds))}, 可以签到"
)
return True
self._showTrace(f"用户在 {date} 没有有效预约记录, 无法签到")
self._showTrace(f"用户在 {date} 没有有效预约记录, 无法签到")
return False
def canRenew(
self,
date: str
) -> bool:
# have a using record in the given date
record = self.__getReserveRecord(date, "使用中")
if record is not None:
end_time = record["time"]["end"]
end_time = datetime.strptime(f"{date} {end_time}", "%Y-%m-%d %H:%M")
time_diff = end_time - datetime.now()
time_diff_seconds = time_diff.total_seconds()
# a using record is definitely after the begin time
if abs(time_diff_seconds) < 120*60:
self._showTrace(
f"用户在 {date} 的预约结束时间为 {end_time}, "
f"距离当前时间还有 {self.__formatDiffTime(abs(time_diff_seconds))}, 可以续约"
)
return True
else:
self._showTrace(
f"用户在 {date} 的预约结束时间为 {end_time}, "
f"距离当前时间还有 {self.__formatDiffTime(abs(time_diff_seconds))}, 无法续约"
)
return False
self._showTrace(f"用户在 {date} 没有有效预约记录, 无法续约")
return False
+69 -2
View File
@@ -34,7 +34,74 @@ class LibCheckin(LibOperator):
def _waitResponseLoad(
self,
self
) -> bool:
pass
try:
WebDriverWait(self.__driver, 5).until(
EC.presence_of_element_located((By.CLASS_NAME, "ui_dialog"))
)
WebDriverWait(self.__driver, 2).until(
EC.presence_of_element_located((By.CLASS_NAME, "resultMessage"))
)
WebDriverWait(self.__driver, 2).until(
EC.element_to_be_clickable((By.CLASS_NAME, "btnOK"))
)
result_message_element = self.__driver.find_element(
By.CLASS_NAME, "resultMessage"
)
ok_btn = self.__driver.find_element(By.CLASS_NAME, "btnOK")
except:
self._showTrace("签到时发生未知错误 !")
return False
print(result_message_element)
result_message = result_message_element.text
if "签到成功" in result_message:
try:
detail_elements = self.__driver.find_elements(
By.CSS_SELECTOR, ".resultMessage dd"
)
except:
pass
if detail_elements:
details = [element.text for element in detail_elements if element.text.strip()]
if len(details) >= 5:
self._showTrace(f"\n"\
f" 签到成功 !\n"\
f" {details[1]}\n"\
f" {details[2]}\n"\
f" {details[3]}\n"\
f" {details[4]}")
else:
self._showTrace(
" 签到成功 !\n"\
" 未获取到签到详情 !")
ok_btn.click()
return True
else:
failure_reason = result_message.replace("签到失败", "").strip()
self._showTrace(f"签到失败: {failure_reason}")
ok_btn.click()
return False
def checkin(
self,
username: str
) -> bool:
if self.__driver is None:
self._showTrace("未提供有效 WebDriver 实例 !")
return False
try:
checkin_btn = WebDriverWait(self.__driver, 2).until(
EC.element_to_be_clickable((By.ID, "btnCheckIn"))
)
except:
self._showTrace(f"用户 {username} 签到界面加载失败 !")
return False
if "disabled" in checkin_btn.get_attribute("class"):
self._showTrace("签到按钮不可用, 可能不在场馆内, 请连接图书馆网络后重试")
return False
checkin_btn.click()
return self._waitResponseLoad()
+2 -2
View File
@@ -51,8 +51,8 @@ class LibLogin(LibOperator):
EC.presence_of_element_located((By.CLASS_NAME, "selectContent"))
)
return True
except Exception as e:
self._showTrace(f"登录页面加载失败 ! : {e}")
except:
self._showTrace(f"登录页面加载失败 ! : 用户账号或者密码错误/验证码错误, 具体以页面提示为准")
return False
+134 -95
View File
@@ -83,16 +83,12 @@ class LibReserve(LibOperator):
raise
if "预定好了" in title or "预约成功" in title or "操作成功" in title:
if len(contents) >= 6:
date_val = contents[1].split(" : ")[1].strip() if " : " in contents[1] else contents[1].strip()
time_val = contents[2].split(" : ")[1].strip() if " : " in contents[2] else contents[2].strip()
seat_val = contents[3].split(" : ")[1].strip() if " : " in contents[3] else contents[3].strip()
checkin_val = contents[5].strip()
self._showTrace(f"\n"\
f" 预约成功 !\n"\
f" 预约日期: {date_val}, \n"\
f" 预约时间: {time_val}, \n"\
f" 预约座位: {seat_val}, \n"\
f" 签到时间: {checkin_val}")
f" {contents[1]}\n"\
f" {contents[2]}\n"\
f" {contents[3]}\n"\
f" 签到时间 {contents[5]}")
else:
self._showTrace(f"\n"\
f" 预约成功 !\n"\
@@ -119,26 +115,26 @@ class LibReserve(LibOperator):
return f"{hour:02d}:{minute:02d}"
def __checkReserveInfo(
def __containRequiredInfo(
self,
reserve_info: dict
) -> bool:
try:
# check the required information
# reserve_info["place"]
if reserve_info.get("floor") is None:
# must contain the required infomation
if reserve_info.get("floor") is None: # if existence ?
raise ValueError("未指定楼层")
if reserve_info["floor"] not in self.__floor_map:
raise ValueError(f"楼层 '{reserve_info['floor']}' 不存在")
if reserve_info["floor"] not in self.__floor_map: # if in the mao ?
raise ValueError(f"楼层 '{reserve_info['floor']}' 不存在")
if reserve_info.get("room") is None:
raise ValueError("未指定房间")
if reserve_info["room"] not in self.__room_map:
raise ValueError(f"房间 '{reserve_info['room']}' 不存在")
raise ValueError(f"房间 '{reserve_info['room']}' 不存在")
if reserve_info.get("seat_id") is None:
raise ValueError("未指定座位")
if reserve_info["seat_id"] == "":
raise ValueError("未指定座位号")
return True
except ValueError as e:
self._showTrace(
f"预约信息错误 ! : {e}, "\
@@ -146,10 +142,14 @@ class LibReserve(LibOperator):
)
return False
# check and try to fix the time errors
cur_time_str = time.strftime("%Y-%m-%d %H:%M", time.localtime())
cur_date, curr_time = cur_time_str.split()
if not reserve_info.get("date"):
def __isValidDate(
self,
reserve_info: dict
) -> bool:
cur_date = time.strftime("%Y-%m-%d", time.localtime())
if reserve_info.get("date") is None:
reserve_info["date"] = cur_date
self._showTrace(f"预约日期未指定, 自动设置为当前日期: {cur_date}")
else:
@@ -159,94 +159,133 @@ class LibReserve(LibOperator):
f"{reserve_info['date']} 早于当前日期 {cur_date}, 自动设置为当前日期"
)
reserve_info["date"] = cur_date
# check the begin time
begin_time = reserve_info.get("begin_time")
if not begin_time:
reserve_info["begin_time"] = {
"time": curr_time,
"max_diff": 30,
"prefer_early": True
}
self._showTrace(f"开始时间未指定, 自动设置为当前时间: {curr_time}, 最大时间差为 30 分钟, 优先选择更早预约时间")
else:
begin_time = reserve_info["begin_time"]
if "time" not in begin_time:
begin_time["time"] = curr_time
self._showTrace(f"开始时间未指定, 自动设置为当前时间: {curr_time}")
if "max_diff" not in begin_time:
begin_time["max_diff"] = 30
self._showTrace(f"最大时间差未指定, 自动设置为 30 分钟")
if "prefer_early" not in begin_time:
begin_time["prefer_early"] = True
self._showTrace(f"是否优先选择更早预约时间未指定, 自动设置为 True")
expect_duration = reserve_info.get("expect_duration")
if not expect_duration:
return True
def __isValidBeginTime(
self,
reserve_info: dict
) -> bool:
cur_time = time.strftime("%H:%M", time.localtime())
if reserve_info.get("begin_time") is None:
reserve_info["begin_time"] = {}
if "time" not in reserve_info["begin_time"]:
reserve_info["begin_time"]["time"] = cur_time
self._showTrace(f"开始时间未指定, 自动设置为当前时间: {cur_time}")
if "max_diff" not in reserve_info["begin_time"]:
reserve_info["begin_time"]["max_diff"] = 30
self._showTrace(f"开始时间最大时间差未指定, 自动设置为 30 分钟")
if "prefer_early" not in reserve_info["begin_time"]:
reserve_info["begin_time"]["prefer_early"] = True
self._showTrace(f"是否优先选择更早开始时间未指定, 自动设置为 True")
return True
def __isValidExpectDuration(
self,
reserve_info: dict
) -> bool:
if reserve_info.get("expect_duration") is None:
reserve_info["expect_duration"] = 4
expect_duration = 4
self._showTrace("预约持续时间未指定, 使用默认时长为 4 小时")
if not reserve_info.get("satisfy_duration"):
if reserve_info.get("satisfy_duration") is None:
reserve_info["satisfy_duration"] = True
self._showTrace("预约满足时长要求未指定, 默认满足")
# check the end time
if not reserve_info.get("end_time"):
begin_mins = self.__timeToMins(reserve_info["begin_time"]["time"])
end_mins = begin_mins + reserve_info["expect_duration"] * 60
end_time_str = self.__minsToTime(end_mins)
return True
def __isValidEndTime(
self,
reserve_info: dict
) -> bool:
if reserve_info.get("end_time") is None:
reserve_info["end_time"] = {}
if "time" not in reserve_info["end_time"]:
end_mins = self.__timeToMins(reserve_info["begin_time"]["time"])
end_mins = end_mins + int(reserve_info["expect_duration"]*60)
reserve_info["end_time"] = {
"time": end_time_str,
"time": self.__minsToTime(end_mins),
"max_diff": 30,
"prefer_early": False
}
self._showTrace(f"结束时间未指定, 自动设置为开始时间加上期望时长: {end_time_str}, 最大时间差为 30 分钟, 优先选择较晚预约时间")
self._showTrace(
f"结束时间未指定, 自动设置为开始时间加上期望时长: {reserve_info['end_time']['time']}"
)
if "max_diff" not in reserve_info["end_time"]:
reserve_info["end_time"]["max_diff"] = 30
self._showTrace(f"结束时间最大时间差未指定, 自动设置为 30 分钟")
if "prefer_early" not in reserve_info["end_time"]:
reserve_info["end_time"]["prefer_early"] = False
self._showTrace(f"是否优先选择较晚结束时间未指定, 自动设置为 True")
return True
def __finalCheck(
self,
reserve_info: dict
):
begin_time, end_time = reserve_info["begin_time"], reserve_info["end_time"]
begin_mins = self.__timeToMins(begin_time["time"])
end_mins = self.__timeToMins(end_time["time"])
# if end time is earlier than begin_time, exchange them
if end_mins < begin_mins:
self._showTrace(
f"结束时间 {end_time['time']} 早于开始时间 {begin_time['time']}, 自动交换"
)
reserve_info["end_time"] = begin_time
reserve_info["begin_time"] = end_time
begin_time, end_time = reserve_info["begin_time"], reserve_info["end_time"]
begin_mins = self.__timeToMins(begin_time["time"])
end_mins = self.__timeToMins(end_time["time"])
# ensure the end time is not later than 23:30
if end_mins > self.__timeToMins("23:30"):
self._showTrace(
f"结束时间 {end_time['time']} 晚于 23:30, 自动设置为 23:30"
)
reserve_info["end_time"]["time"] = "23:30"
end_mins = self.__timeToMins("23:30")
# ensure the duration is not longer than 8 hours
if reserve_info["satisfy_duration"]:
if reserve_info["expect_duration"] > 8:
self._showTrace(
f"该用户设置了优先满足时长要求, 但是预约期望持续时间 "
f"{reserve_info['expect_duration']} 小时 "
f"超出最大时长 8 小时, 自动设置为 8 小时"
)
reserve_info["expect_duration"] = 8
else:
end_time = reserve_info["end_time"]
if "time" not in end_time:
begin_mins = self.__timeToMins(reserve_info["begin_time"]["time"])
end_mins = begin_mins + reserve_info["expect_duration"] * 60
end_time["time"] = self.__minsToTime(end_mins)
self._showTrace(f"结束时间未指定, 自动设置为开始时间加上期望时长: {end_time['time']}")
if "max_diff" not in end_time:
end_time["max_diff"] = 30
self._showTrace(f"最大时间差未指定, 自动设置为 30 分钟")
if "prefer_early" not in end_time:
end_time["prefer_early"] = False
self._showTrace(f"是否优先选择较早预约时间未指定, 自动设置为 False")
# check the reserve time boundary and fix the errors
#
# get time string for message show
begin_time_str = reserve_info["begin_time"]["time"]
end_time_str = reserve_info["end_time"]["time"]
if end_mins - begin_mins > 8*60:
self._showTrace(
f"该用户未设置优先满足时长要求, 但是检查到预约持续时间 "
f"{int((end_mins - begin_mins)/60)} 小时 "
f"超出最大时长 8 小时, 自动设置为 8 小时"
)
reserve_info["expect_duration"] = 8
reserve_info["end_time"]["time"] = self.__minsToTime(begin_mins + 8*60)
return True
# minute time for check and fix them
begin_mins = self.__timeToMins(begin_time_str)
end_mins = self.__timeToMins(end_time_str)
# ensure begin time is not later than end time
if begin_mins > end_mins:
reserve_info["begin_time"]["time"], reserve_info["end_time"]["time"] = end_time_str, begin_time_str
reserve_info["begin_time"]["prefer_early"], reserve_info["end_time"]["prefer_early"] = \
reserve_info["end_time"]["prefer_early"], reserve_info["begin_time"]["prefer_early"]
self._showTrace("预约开始时间晚于预约结束时间, 自动调换开始时间和结束时间")
def __checkReserveInfo(
self,
reserve_info: dict
) -> bool:
# update the begin_mins and end_mins after swap
begin_time_str, end_time_str = end_time_str, begin_time_str
begin_mins, end_mins = end_mins, begin_mins
# ensure end time is not later than 22:30
max_end_mins = self.__timeToMins("22:30")
if end_mins > max_end_mins:
reserve_info["end_time"]["time"] = "22:30"
end_time_str = "22:30"
end_mins = max_end_mins
self._showTrace("预约结束时间超过 22:30, 自动设置为 22:30")
# ensure expect duration is shorter than 8 hours
max_duration_mins = 8 * 60
duration_mins = end_mins - begin_mins
if duration_mins > max_duration_mins:
new_end_mins = begin_mins + max_duration_mins
reserve_info["end_time"]["time"] = self.__minsToTime(new_end_mins)
self._showTrace("预约持续时间超过8小时, 自动设置为 8 小时")
if not self.__containRequiredInfo(reserve_info):
return False
if not self.__isValidDate(reserve_info):
return False
if not self.__isValidBeginTime(reserve_info):
return False
if not self.__isValidExpectDuration(reserve_info):
return False
if not self.__isValidEndTime(reserve_info):
return False
if not self.__finalCheck(reserve_info):
return False
self._showTrace(
f"预约信息检查完成, 准备预约 "
f"{reserve_info['date']} "
+331 -305
View File
@@ -232,8 +232,8 @@
<property name="spacing">
<number>5</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="UsernameKabel">
<item row="2" column="0">
<widget class="QLabel" name="PasswordLabel">
<property name="minimumSize">
<size>
<width>100</width>
@@ -247,7 +247,7 @@
</size>
</property>
<property name="text">
<string>用户名</string>
<string>密码</string>
</property>
</widget>
</item>
@@ -299,25 +299,6 @@
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="PasswordLabel">
<property name="minimumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>密码:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="UsernameEdit">
<property name="minimumSize">
@@ -340,6 +321,25 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="UsernameKabel">
<property name="minimumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>用户名:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@@ -376,8 +376,65 @@
<property name="spacing">
<number>5</number>
</property>
<item row="4" column="4">
<widget class="QLineEdit" name="SeatIDEdit">
<item row="0" column="1">
<widget class="QLabel" name="DateLabel">
<property name="minimumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>日期:</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="MaxBeginTimeDiffLabel">
<property name="minimumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>最大时长偏差:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="PlaceLabel">
<property name="minimumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>地点:</string>
</property>
</widget>
</item>
<item row="12" column="4">
<widget class="QDoubleSpinBox" name="ExpectDurationSpinBox">
<property name="minimumSize">
<size>
<width>0</width>
@@ -390,6 +447,37 @@
<height>25</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;期望的预约时长,脚本会尽量满足&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<double>8.000000000000000</double>
</property>
</widget>
</item>
<item row="11" column="4">
<widget class="QCheckBox" name="PreferLateEndTimeCheckBox">
<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="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;勾选此项,如果有多个与目标&lt;span style=&quot; font-weight:700; font-style:italic;&quot;&gt;结束时间&lt;/span&gt;相近的可选时间,选择时间最晚项&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>优先选择最晚</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="9" column="1">
@@ -411,11 +499,27 @@
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QComboBox" name="PlaceComboBox">
<property name="enabled">
<bool>false</bool>
<item row="5" column="1">
<widget class="QLabel" name="BeginTimeLabel">
<property name="minimumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>开始时间:</string>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QComboBox" name="FloorComboBox">
<property name="minimumSize">
<size>
<width>0</width>
@@ -430,13 +534,28 @@
</property>
<item>
<property name="text">
<string>图书馆</string>
<string>二层</string>
</property>
</item>
<item>
<property name="text">
<string>三层</string>
</property>
</item>
<item>
<property name="text">
<string>四层</string>
</property>
</item>
<item>
<property name="text">
<string>五层</string>
</property>
</item>
</widget>
</item>
<item row="12" column="1">
<widget class="QLabel" name="ExpectDurationLabel">
<item row="10" column="1">
<widget class="QLabel" name="MaxEndTimeDiffLabel">
<property name="minimumSize">
<size>
<width>100</width>
@@ -450,7 +569,7 @@
</size>
</property>
<property name="text">
<string>期望时长</string>
<string>最大时长偏差</string>
</property>
</widget>
</item>
@@ -487,8 +606,31 @@
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="DateLabel">
<item row="0" column="4">
<widget class="QDateEdit" name="DateEdit">
<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="date">
<date>
<year>2025</year>
<month>11</month>
<day>1</day>
</date>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QLabel" name="ExpectDurationLabel">
<property name="minimumSize">
<size>
<width>100</width>
@@ -502,7 +644,26 @@
</size>
</property>
<property name="text">
<string>期:</string>
<string>期望时长</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="RoomLabel">
<property name="minimumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>区域:</string>
</property>
</widget>
</item>
@@ -546,11 +707,11 @@
</property>
</widget>
</item>
<item row="8" column="4">
<widget class="QCheckBox" name="PreferEarlyBeginTimeCheckBox">
<item row="13" column="4">
<widget class="QCheckBox" name="SatisfyDurationCheckBox">
<property name="minimumSize">
<size>
<width>100</width>
<width>0</width>
<height>25</height>
</size>
</property>
@@ -561,128 +722,16 @@
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;勾选此项,如果有多个与目标&lt;span style=&quot; font-weight:700; font-style:italic;&quot;&gt;开始时间&lt;/span&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;span style=&quot; font-weight:700; font-style:italic;&quot;&gt;预约失败&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>优先选择最早</string>
<string>优先满足时长要求</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QLabel" name="MaxEndTimeDiffLabel">
<property name="minimumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>最大时长偏差:</string>
</property>
</widget>
</item>
<item row="10" column="4">
<widget class="QSpinBox" name="MaxEndTimeDiffSpinBox">
<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="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;期望的&lt;span style=&quot; font-weight:700; font-style:italic;&quot;&gt;结束时间&lt;/span&gt;不可用时,按照该偏差范围寻找最近可选时间&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>120</number>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="RoomLabel">
<property name="minimumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>区域:</string>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QComboBox" name="FloorComboBox">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>25</height>
</size>
</property>
<item>
<property name="text">
<string>二层</string>
</property>
</item>
<item>
<property name="text">
<string>三层</string>
</property>
</item>
<item>
<property name="text">
<string>四层</string>
</property>
</item>
<item>
<property name="text">
<string>五层</string>
</property>
</item>
</widget>
</item>
<item row="3" column="4">
<widget class="QComboBox" name="RoomComboBox">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>25</height>
</size>
</property>
</widget>
</item>
<item row="7" column="4">
<widget class="QSpinBox" name="MaxBeginTimeDiffSpinBox">
<property name="minimumSize">
@@ -705,50 +754,6 @@
</property>
</widget>
</item>
<item row="11" column="4">
<widget class="QCheckBox" name="PreferLateEndTimeCheckBox">
<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="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;勾选此项,如果有多个与目标&lt;span style=&quot; font-weight:700; font-style:italic;&quot;&gt;结束时间&lt;/span&gt;相近的可选时间,选择时间最晚项&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>优先选择最晚</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="BeginTimeLabel">
<property name="minimumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>开始时间:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="FloorLabel">
<property name="minimumSize">
@@ -768,6 +773,93 @@
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QComboBox" name="PlaceComboBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>25</height>
</size>
</property>
<item>
<property name="text">
<string>图书馆</string>
</property>
</item>
</widget>
</item>
<item row="8" column="4">
<widget class="QCheckBox" name="PreferEarlyBeginTimeCheckBox">
<property name="minimumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>25</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;勾选此项,如果有多个与目标&lt;span style=&quot; font-weight:700; font-style:italic;&quot;&gt;开始时间&lt;/span&gt;相近的可选时间,选择时间最早项&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>优先选择最早</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="10" column="4">
<widget class="QSpinBox" name="MaxEndTimeDiffSpinBox">
<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="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;期望的&lt;span style=&quot; font-weight:700; font-style:italic;&quot;&gt;结束时间&lt;/span&gt;不可用时,按照该偏差范围寻找最近可选时间&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>120</number>
</property>
</widget>
</item>
<item row="3" column="4">
<widget class="QComboBox" name="RoomComboBox">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>25</height>
</size>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="SeatIDLabel">
<property name="minimumSize">
@@ -787,113 +879,47 @@
</property>
</widget>
</item>
<item row="12" column="4">
<widget class="QDoubleSpinBox" name="ExpectDurationSpinBox">
<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="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;期望的预约时长,脚本会尽量满足&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<double>8.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QDateEdit" name="DateEdit">
<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="date">
<date>
<year>2025</year>
<month>11</month>
<day>1</day>
</date>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="MaxBeginTimeDiffLabel">
<property name="minimumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>最大时长偏差:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="PlaceLabel">
<property name="minimumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>地点:</string>
</property>
</widget>
</item>
<item row="13" column="4">
<widget class="QCheckBox" name="SatisfyDurationCheckBox">
<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="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;勾选此项,会优先满足预约时长限制,当座位紧张时可能会导致&lt;span style=&quot; font-weight:700; font-style:italic;&quot;&gt;预约失败&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>优先满足时长要求</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
<item row="4" column="4">
<layout class="QHBoxLayout" name="SeatIDLayout">
<item>
<widget class="QLineEdit" name="SeatIDEdit">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>130</width>
<height>25</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="SelectSeatsButton">
<property name="minimumSize">
<size>
<width>25</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>25</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="edit-find"/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
@@ -1127,13 +1153,13 @@
<widget class="QLineEdit" name="BrowseBrowserDriverEdit">
<property name="minimumSize">
<size>
<width>265</width>
<width>250</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>260</width>
<width>300</width>
<height>25</height>
</size>
</property>
+7 -1
View File
@@ -242,6 +242,8 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
config_paths: dict
):
self.__alConfigWidget.configWidgetCloseSingal.disconnect(self.onConfigWidgetClosed)
self.__alConfigWidget.deleteLater()
self.__alConfigWidget = None
self.ConfigButton.setEnabled(True)
self.StartButton.setEnabled(True)
@@ -288,8 +290,12 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
):
if self.__auto_lib_thread and self.__auto_lib_thread.isRunning():
self.__auto_lib_thread.stop()
self.showTrace("正在停止操作......")
self.__auto_lib_thread.stop()
self.__auto_lib_thread.wait()
self.showTrace("操作已停止")
self.__auto_lib_thread.finishedSignal.disconnect(self.onStopButtonClicked)
self.__auto_lib_thread.deleteLater()
self.setControlButtons(True, True, False)
@Slot()