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

Compare commits

...

4 Commits

Author SHA1 Message Date
KenanZhu 9f17474c1b fix(gui): optimize the config files' status management 2025-11-22 14:27:40 +08:00
KenanZhu 04d66346dc fix(ALConfigWidget): optimize the config window usage
add date calendar popup so that user can select
the date more easily
fix some file dialog title display issue
default max diff time change to 30 minutes
2025-11-22 14:23:35 +08:00
KenanZhu f858295af1 refactor(LibChecker): refactor the code of LibChecker to make it more readable and maintainable 2025-11-22 14:16:38 +08:00
KenanZhu cd6c899388 fix(*): optimize the operators' performance when invoking webdriver
we consume the wait time of webdriver and its
implicit wait time
2025-11-22 14:13:23 +08:00
8 changed files with 293 additions and 187 deletions
+5 -3
View File
@@ -91,7 +91,7 @@ class AutoLib(MsgBase):
self.__driver = webdriver.Firefox(service=service, options=edge_options) self.__driver = webdriver.Firefox(service=service, options=edge_options)
case _: case _:
raise Exception(f"不支持的浏览器驱动类型: {self.__driver_type}") raise Exception(f"不支持的浏览器驱动类型: {self.__driver_type}")
self.__driver.implicitly_wait(10) self.__driver.implicitly_wait(1)
self.__driver.execute_script( self.__driver.execute_script(
"Object.defineProperty(navigator, 'webdriver', {get: () => undefined})" "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
) )
@@ -122,7 +122,7 @@ class AutoLib(MsgBase):
# wait for page load # wait for page load
try: try:
WebDriverWait(self.__driver, 5).until( # title contains "首页" WebDriverWait(self.__driver, 2).until( # title contains "首页"
EC.title_contains("首页") EC.title_contains("首页")
) )
WebDriverWait(self.__driver, 2).until( # username field presence WebDriverWait(self.__driver, 2).until( # username field presence
@@ -147,7 +147,9 @@ class AutoLib(MsgBase):
self, self,
) -> bool: ) -> bool:
self.__driver.get(self.__system_config_reader.get("library/host_url")) url = self.__system_config_reader.get("library/host_url")
url += self.__system_config_reader.get("library/login_url")
self.__driver.get(url)
if not self.__waitResponseLoad(): if not self.__waitResponseLoad():
return False return False
return True return True
+137 -93
View File
@@ -44,9 +44,9 @@ class LibChecker(LibOperator):
seconds: float seconds: float
) -> str: ) -> str:
hours = int(seconds // 3600) hours = int(seconds//3600)
minutes = int(seconds % 3600 // 60) minutes = int(seconds%3600//60)
seconds = int(seconds % 60) seconds = int(seconds%60)
return f"{hours}{minutes}{seconds}" return f"{hours}{minutes}{seconds}"
@@ -55,7 +55,7 @@ class LibChecker(LibOperator):
) -> bool: ) -> bool:
try: try:
WebDriverWait(self.__driver, 5).until( WebDriverWait(self.__driver, 2).until(
EC.element_to_be_clickable((By.XPATH, "//a[@href='/history?type=SEAT']")) EC.element_to_be_clickable((By.XPATH, "//a[@href='/history?type=SEAT']"))
).click() ).click()
WebDriverWait(self.__driver, 2).until( WebDriverWait(self.__driver, 2).until(
@@ -67,6 +67,44 @@ class LibChecker(LibOperator):
return True return True
def __decodeReserveTime(
self,
time_element
) -> dict:
time_str = time_element.text.strip()
today = datetime.now().date()
if "明天" in time_str:
target_date = today + timedelta(days=1)
date = target_date.strftime("%Y-%m-%d")
elif "今天" in time_str:
target_date = today
date = target_date.strftime("%Y-%m-%d")
elif "昨天" in time_str:
target_date = today - timedelta(days=1)
date = target_date.strftime("%Y-%m-%d")
else:
date_match = re.search(r"(\d{4}-\d{1,2}-\d{1,2})", time_str)
if date_match:
date = date_match.group(1)
else:
date = ""
time_match = re.search(r"(\d{1,2}:\d{2}) -- (\d{1,2}:\d{2})", time_str)
if time_match:
begin_time = time_match.group(1)
end_time = time_match.group(2)
else:
begin_time = ""
end_time = ""
return {
"date": date,
"time": {
"begin": begin_time,
"end": end_time
}
}
def __decodeReserveInfo( def __decodeReserveInfo(
self, self,
info_elements info_elements
@@ -108,42 +146,80 @@ class LibChecker(LibOperator):
By.CSS_SELECTOR, "a" By.CSS_SELECTOR, "a"
) )
except: except:
return None return {
# process time element to get the date string "date": "",
time_str = time_element.text.strip() "time": {"begin": "", "end": ""},
today = datetime.now().date() "info": {"location": "", "status": ""}
if "明天" in time_str: }
target_date = today + timedelta(days=1) time = self.__decodeReserveTime(time_element)
date_str = target_date.strftime("%Y-%m-%d")
elif "今天" in time_str:
target_date = today
date_str = target_date.strftime("%Y-%m-%d")
elif "昨天" in time_str:
target_date = today - timedelta(days=1)
date_str = target_date.strftime("%Y-%m-%d")
else:
date_match = re.search(r"(\d{4}-\d{1,2}-\d{1,2})", time_str)
if date_match:
date_str = date_match.group(1)
else:
date_str = ""
time_match = re.search(r"(\d{1,2}:\d{2}) -- (\d{1,2}:\d{2})", time_str)
if time_match:
begin_time = time_match.group(1)
end_time = time_match.group(2)
else:
time_str = ""
info = self.__decodeReserveInfo(info_elements) info = self.__decodeReserveInfo(info_elements)
return { return {
"date": date_str, "date": time["date"],
"time": { "time": time["time"],
"begin": begin_time,
"end": end_time,
},
"info": info "info": info
} }
def __decodeReserveRecords(
self,
reservations
) -> list:
records = []
for reservation in reservations:
record = self.__decodeReserveRecord(reservation)
if record["date"] == "":
record = None
if record["time"] == {"begin": "", "end": ""}:
record = None
records.append(record)
return records
def __loadReserveRecords(
self
) -> list:
try:
# check if there's any reservation on the date
WebDriverWait(self.__driver, 2).until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".myReserveList > dl"))
)
reservations = self.__driver.find_elements(
By.CSS_SELECTOR, ".myReserveList > dl:not(#moreBlock)"
)
return reservations
except:
self._showTrace("加载预约记录失败 !")
return None
def __showMoreReserveRecords(
self
) -> bool:
# load new reservations if still not sure
try:
WebDriverWait(self.__driver, 0.1).until(
EC.element_to_be_clickable((By.ID, "moreBtn"))
)
except:
# the reservation is the last one
return False
try:
more_btn = self.__driver.find_element(By.ID, "moreBtn")
if more_btn.is_displayed() and more_btn.is_enabled():
self.__driver.execute_script("arguments[0].scrollIntoView(true);", more_btn)
self.__driver.execute_script("arguments[0].click();", more_btn)
return True
else:
self._showTrace("用户无法加载更多预约记录")
return False
except:
self._showTrace("加载更多预约记录失败 !")
return False
def __getReserveRecord( def __getReserveRecord(
self, self,
wanted_date: str, wanted_date: str,
@@ -154,7 +230,6 @@ class LibChecker(LibOperator):
self._showTrace("日期未指定, 无法检查当前预约状态") self._showTrace("日期未指定, 无法检查当前预约状态")
return None return None
self._showTrace(f"正在检查用户在 {wanted_date} 是否有预约状态为 {wanted_status} 的预约记录......") self._showTrace(f"正在检查用户在 {wanted_date} 是否有预约状态为 {wanted_status} 的预约记录......")
date_obj = datetime.strptime(wanted_date, "%Y-%m-%d").date()
checked_count = 0 checked_count = 0
max_check_times = 6 # we only check (4*(6-1)=)20 reservations, the last time cant be checked max_check_times = 6 # we only check (4*(6-1)=)20 reservations, the last time cant be checked
@@ -162,59 +237,30 @@ class LibChecker(LibOperator):
if not self.__navigateToReserveRecordPage(): if not self.__navigateToReserveRecordPage():
return None return None
for _ in range(max_check_times): for _ in range(max_check_times):
try: reservations = self.__loadReserveRecords()
# check if there's any reservation on the date if reservations is None:
WebDriverWait(self.__driver, 2).until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".myReserveList > dl"))
)
reservations = self.__driver.find_elements(
By.CSS_SELECTOR, ".myReserveList > dl:not(#moreBlock)"
)
except:
self._showTrace("加载预约记录失败 !")
return None return None
for i in range(checked_count, len(reservations)): # the last one is load button records = self.__decodeReserveRecords(reservations[checked_count:])
reservation = reservations[i] for record in records:
record = self.__decodeReserveRecord(reservation) checked_count += 1
if record is None: if record is None:
continue continue
record_date = record["date"] # record date is later than the given date, check the next one
record_time = record["time"] if datetime.strptime(record["date"], "%Y-%m-%d").date() >\
status = record["info"]["status"] datetime.strptime(wanted_date, "%Y-%m-%d").date():
location = record["info"]["location"]
if record_date == "" or record_time == {"begin": "", "end": ""}:
continue continue
is_wanted = (status == wanted_status) # record date is earlier than the given date, so there is no wanted record
# reservation is later than the given date, check the next one if datetime.strptime(record["date"], "%Y-%m-%d").date() <\
if datetime.strptime(record_date, "%Y-%m-%d").date() > date_obj: datetime.strptime(wanted_date, "%Y-%m-%d").date():
continue
# reservation is earlier than the given date, can reserve
if datetime.strptime(record_date, "%Y-%m-%d").date() < date_obj:
return None return None
# query the wanted status if record["info"]["status"] == wanted_status:
if is_wanted:
self._showTrace( self._showTrace(
f"寻找到用户第 {i + 1} 条状态为 {wanted_status} 的预约记录, " f"寻找到用户第 {checked_count} 条状态为 {wanted_status} 的预约记录, "
f"详细信息: {record_date} {record_time['begin']} - {record_time['end']} {location}" f"详细信息: {record["date"]} "
f"{record["time"]["begin"]} - {record["time"]["end"]} {record["info"]["location"]}"
) )
return { return record
"index": i, if not self.__showMoreReserveRecords():
"date": record_date,
"time": record_time,
"status": wanted_status
}
checked_count = len(reservations)
# load new reservations if still not sure
try:
more_btn = self.__driver.find_element(By.ID, "moreBtn")
if more_btn.is_displayed() and more_btn.is_enabled():
self.__driver.execute_script("arguments[0].scrollIntoView(true);", more_btn)
self.__driver.execute_script("arguments[0].click();", more_btn)
else:
self._showTrace("用户无法加载更多预约记录")
break
except:
self._showTrace("加载更多预约记录失败 !")
break break
return None return None
@@ -252,21 +298,21 @@ class LibChecker(LibOperator):
if time_diff_seconds < -30*60: if time_diff_seconds < -30*60:
self._showTrace( self._showTrace(
f"用户在 {date} 的预约开始时间为 {begin_time}, " f"用户在 {date} 的预约开始时间为 {begin_time}, "
f"距离当前时间还有 {self.__formatDiffTime(abs(time_diff_seconds))}, 无法签到" f"当前距离预约开始时间还有 {self.__formatDiffTime(abs(time_diff_seconds))}, 无法签到"
) )
return False return False
# before in 30 minutes, can checkin # before in 30 minutes, can checkin
elif -30*60 <= time_diff_seconds < 0: elif -30*60 <= time_diff_seconds < 0:
self._showTrace( self._showTrace(
f"用户在 {date} 的预约开始时间为 {begin_time}, " f"用户在 {date} 的预约开始时间为 {begin_time}, "
f"距离当前时间还有 {self.__formatDiffTime(abs(time_diff_seconds))}, 可以签到" f"当前距离预约开始时间还有 {self.__formatDiffTime(abs(time_diff_seconds))}, 可以签到"
) )
return True return True
# past less than 30 minutes, can checkin # past less than 30 minutes, can checkin
elif 0 <= time_diff_seconds < 30*60: elif 0 <= time_diff_seconds < 30*60 - 5: # spare 5 seconds for the checkin process
self._showTrace( self._showTrace(
f"用户在 {date} 的预约开始时间为 {begin_time}, " f"用户在 {date} 的预约开始时间为 {begin_time}, "
f"当前时间已经 {self.__formatDiffTime(abs(time_diff_seconds))}, 可以签到" f"当前距离预约开始时间已经过去 {self.__formatDiffTime(abs(time_diff_seconds))}, 可以签到"
) )
return True return True
self._showTrace(f"用户在 {date} 没有有效预约记录, 无法签到") self._showTrace(f"用户在 {date} 没有有效预约记录, 无法签到")
@@ -286,17 +332,15 @@ class LibChecker(LibOperator):
time_diff = end_time - datetime.now() time_diff = end_time - datetime.now()
time_diff_seconds = time_diff.total_seconds() time_diff_seconds = time_diff.total_seconds()
# a using record is definitely after the begin time # a using record is definitely after the begin time
trace_msg = (
f"用户在 {date} 的预约结束时间为 {end_time}, "
f"当前距离预约结束时间还有 {self.__formatDiffTime(abs(time_diff_seconds))}"
)
if abs(time_diff_seconds) < 120*60: if abs(time_diff_seconds) < 120*60:
self._showTrace( self._showTrace(f"{trace_msg}, 可以续约")
f"用户在 {date} 的预约结束时间为 {end_time}, "
f"距离当前时间还有 {self.__formatDiffTime(abs(time_diff_seconds))}, 可以续约"
)
return True return True
else: else:
self._showTrace( self._showTrace(f"{trace_msg}, 无法续约")
f"用户在 {date} 的预约结束时间为 {end_time}, "
f"距离当前时间还有 {self.__formatDiffTime(abs(time_diff_seconds))}, 无法续约"
)
return False return False
self._showTrace(f"用户在 {date} 没有有效预约记录, 无法续约") self._showTrace(f"用户在 {date} 没有有效预约记录, 无法续约")
return False return False
+1 -1
View File
@@ -38,7 +38,7 @@ class LibCheckin(LibOperator):
) -> bool: ) -> bool:
try: try:
WebDriverWait(self.__driver, 5).until( WebDriverWait(self.__driver, 2).until(
EC.presence_of_element_located((By.CLASS_NAME, "ui_dialog")) EC.presence_of_element_located((By.CLASS_NAME, "ui_dialog"))
) )
WebDriverWait(self.__driver, 2).until( WebDriverWait(self.__driver, 2).until(
+3 -3
View File
@@ -41,13 +41,13 @@ class LibLogin(LibOperator):
# wait to verify login success # wait to verify login success
try: try:
WebDriverWait(self.__driver, 5).until( # title contains "自选座位 :: 座位预约系统" WebDriverWait(self.__driver, 2).until( # title contains "自选座位 :: 座位预约系统"
EC.title_contains("自选座位 :: 座位预约系统") EC.title_contains("自选座位 :: 座位预约系统")
) )
WebDriverWait(self.__driver, 3).until( # search button presence WebDriverWait(self.__driver, 2).until( # search button presence
EC.presence_of_element_located((By.ID, "search")) EC.presence_of_element_located((By.ID, "search"))
) )
WebDriverWait(self.__driver, 3).until( # select content presence WebDriverWait(self.__driver, 2).until( # select content presence
EC.presence_of_element_located((By.CLASS_NAME, "selectContent")) EC.presence_of_element_located((By.CLASS_NAME, "selectContent"))
) )
return True return True
+26 -7
View File
@@ -55,13 +55,13 @@ class LibReserve(LibOperator):
) -> bool: ) -> bool:
try: try:
WebDriverWait(self.__driver, 5).until( WebDriverWait(self.__driver, 2).until(
EC.presence_of_element_located((By.CLASS_NAME, "layoutSeat")) EC.presence_of_element_located((By.CLASS_NAME, "layoutSeat"))
) )
title_elements = [] title_elements = []
# reserve failed without title elements, so we need to try # reserve failed without title elements, so we need to try
try: try:
WebDriverWait(self.__driver, 1).until( WebDriverWait(self.__driver, 2).until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".layoutSeat dt")) EC.presence_of_element_located((By.CSS_SELECTOR, ".layoutSeat dt"))
) )
title_elements = self.__driver.find_elements( title_elements = self.__driver.find_elements(
@@ -309,12 +309,12 @@ class LibReserve(LibOperator):
try: try:
# click the trigger element # click the trigger element
WebDriverWait(self.__driver, 5).until( WebDriverWait(self.__driver, 2).until(
EC.element_to_be_clickable(trigger_locator) EC.element_to_be_clickable(trigger_locator)
).click() ).click()
if option_locator: if option_locator:
# select the option element if specified # select the option element if specified
WebDriverWait(self.__driver, 5).until( WebDriverWait(self.__driver, 2).until(
EC.element_to_be_clickable(option_locator) EC.element_to_be_clickable(option_locator)
).click() ).click()
self._showTrace(success_msg) self._showTrace(success_msg)
@@ -386,9 +386,16 @@ class LibReserve(LibOperator):
try: try:
# wait fot seat layout element to load # wait fot seat layout element to load
WebDriverWait(self.__driver, 5).until( WebDriverWait(self.__driver, 2).until(
EC.presence_of_element_located((By.ID, "seatLayout")) EC.presence_of_element_located((By.ID, "seatLayout"))
) )
WebDriverWait(self.__driver, 2).until(
EC.presence_of_all_elements_located((By.CSS_SELECTOR, "li[id^='seat_']"))
)
except:
self._showTrace(f"座位加载失败 !")
return False
try:
all_seats = self.__driver.find_elements( all_seats = self.__driver.find_elements(
By.CSS_SELECTOR, "li[id^='seat_']" By.CSS_SELECTOR, "li[id^='seat_']"
) )
@@ -397,7 +404,7 @@ class LibReserve(LibOperator):
if not seat_id_upper == seat.text.lstrip('0'): if not seat_id_upper == seat.text.lstrip('0'):
continue continue
seat_link = seat.find_element(By.TAG_NAME, "a") seat_link = seat.find_element(By.TAG_NAME, "a")
WebDriverWait(self.__driver, 5).until( WebDriverWait(self.__driver, 2).until(
EC.element_to_be_clickable(seat_link) EC.element_to_be_clickable(seat_link)
) )
seat_link.click() seat_link.click()
@@ -419,6 +426,15 @@ class LibReserve(LibOperator):
prefer_earlier: bool = True prefer_earlier: bool = True
) -> int: ) -> int:
try:
WebDriverWait(self.__driver, 2).until(
EC.presence_of_all_elements_located(
(By.CSS_SELECTOR, f"#{time_id} ul li a")
)
)
except:
self._showTrace(f"{time_type} 选择失败 ! : 当前未查询到可用时间")
return -1
try: try:
all_time_opts = self.__driver.find_elements( all_time_opts = self.__driver.find_elements(
By.CSS_SELECTOR, By.CSS_SELECTOR,
@@ -429,6 +445,9 @@ class LibReserve(LibOperator):
best_actual_diff = None best_actual_diff = None
best_time_opt = None best_time_opt = None
if not all_time_opts:
self._showTrace(f"{time_type} 选择失败 ! : 当前未查询到可用时间")
return -1
for time_opt in all_time_opts: for time_opt in all_time_opts:
time_attr = time_opt.get_attribute("time") time_attr = time_opt.get_attribute("time")
if time_attr == "now": if time_attr == "now":
@@ -544,7 +563,7 @@ class LibReserve(LibOperator):
return False return False
# map page # map page
try: try:
WebDriverWait(self.__driver, 5).until( WebDriverWait(self.__driver, 2).until(
EC.element_to_be_clickable((By.XPATH, "//a[@href='/map']")) EC.element_to_be_clickable((By.XPATH, "//a[@href='/map']"))
).click() ).click()
WebDriverWait(self.__driver, 2).until( WebDriverWait(self.__driver, 2).until(
+101 -73
View File
@@ -32,27 +32,24 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
self, self,
parent = None, parent = None,
config_paths = { config_paths = {
"system": "system": "",
f"{QDir.toNativeSeparators(QFileInfo(sys.executable).absoluteDir().absoluteFilePath("system.json"))}", "users": ""
"users":
f"{QDir.toNativeSeparators(QFileInfo(sys.executable).absoluteDir().absoluteFilePath("users.json"))}",
} }
): ):
super().__init__(parent) super().__init__(parent)
self.setupUi(self) self.setupUi(self)
self.connectSignals()
self.modifyUi()
self.__config_paths = config_paths self.__config_paths = config_paths
self.__system_config_data = self.loadSystemConfig(self.__config_paths["system"]) self.__config_data = {"system": {}, "users": {}}
self.__users_config_data = self.loadUsersConfig(self.__config_paths["users"]) self.__seat_map_widget = None
if not self.__system_config_data:
self.initlizeDefaultConfig("system") self.modifyUi()
if not self.__users_config_data: self.connectSignals()
self.initlizeDefaultConfig("users") self.initlizeFloorRoomMap()
self.initlizeConfigToWidget("system", self.__system_config_data) self.initlizeDefaultConfigPaths()
self.initlizeConfigToWidget("users", self.__users_config_data) if not self.initlizeConfigs():
self.close()
def modifyUi( def modifyUi(
@@ -129,42 +126,16 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
def initlizeDefaultConfigPaths( def initlizeDefaultConfigPaths(
self self
) -> dict: ):
script_path = sys.executable script_path = sys.executable
script_dir = QFileInfo(script_path).absoluteDir() script_dir = QFileInfo(script_path).absoluteDir()
return { self.__default_config_paths = {
"users": QDir.toNativeSeparators(script_dir.absoluteFilePath("users.json")), "users": QDir.toNativeSeparators(script_dir.absoluteFilePath("users.json")),
"system": QDir.toNativeSeparators(script_dir.absoluteFilePath("system.json")) "system": QDir.toNativeSeparators(script_dir.absoluteFilePath("system.json"))
} }
def initlizeDefaultConfig(
self,
which: str
):
default_config_paths = self.initlizeDefaultConfigPaths()
if which == "system":
self.__system_config_data = self.defaultSystemConfig()
self.__config_paths["system"] = default_config_paths["system"]
self.saveSystemConfig(self.__config_paths["system"], self.__system_config_data)
elif which == "users":
self.__users_config_data = self.defaultUsersConfig()
self.__config_paths["users"] = default_config_paths["users"]
self.saveUsersConfig(self.__config_paths["users"], self.__users_config_data)
if which == "system":
file_type = "系统配置文件"
elif which == "users":
file_type = "用户配置文件"
QMessageBox.information(
self,
"提示 - AutoLibrary",
f"{file_type}已初始化, \n"\
f" 文件路径: {self.__config_paths[which]}"
)
def initlizeConfigToWidget( def initlizeConfigToWidget(
self, self,
which: str, which: str,
@@ -180,6 +151,63 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
self.CurrentUserConfigEdit.setText(self.__config_paths["users"]) self.CurrentUserConfigEdit.setText(self.__config_paths["users"])
def initlizeConfig(
self,
which: str
) -> bool:
msg = ""
is_success = True
if which == "system":
system_config_path = self.__config_paths[which]
if not os.path.exists(system_config_path):
self.__config_data[which] = self.defaultSystemConfig()
self.__config_paths[which] = self.__default_config_paths[which]
if self.saveSystemConfig(self.__config_paths[which], self.__config_data[which]):
msg += f"系统配置文件已初始化, 文件路径: \n{self.__config_paths[which]}\n"
else:
is_success = False
else:
self.__config_data[which] = self.loadSystemConfig(system_config_path)
if self.__config_data[which] is None:
is_success = False
elif which == "users":
users_config_path = self.__config_paths[which]
if not os.path.exists(users_config_path):
self.__config_data[which] = self.defaultUsersConfig()
self.__config_paths[which] = self.__default_config_paths[which]
if self.saveUsersConfig(self.__config_paths[which], self.__config_data[which]):
msg += f"用户配置文件已初始化, 文件路径: \n{self.__config_paths[which]}\n"
else:
is_success = False
else:
self.__config_data[which] = self.loadUsersConfig(users_config_path)
if self.__config_data[which] is None:
is_success = False
if msg:
QMessageBox.information(
self,
"提示 - AutoLibrary",
f"配置文件初始化完成: \n{msg}"
)
return is_success
def initlizeConfigs(
self
) -> bool:
is_success = True
for which in ["system", "users"]:
if not self.__config_paths[which]:
self.__config_paths[which] = self.__default_config_paths[which]
if not self.initlizeConfig(which):
is_success = False
break
self.initlizeConfigToWidget(which, self.__config_data[which])
return is_success
def defaultSystemConfig( def defaultSystemConfig(
self self
) -> dict: ) -> dict:
@@ -261,21 +289,18 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
self.UsernameEdit.setText("") self.UsernameEdit.setText("")
self.PasswordEdit.setText("") self.PasswordEdit.setText("")
self.UserListWidget.setSortingEnabled(True) self.UserListWidget.setSortingEnabled(True)
self.PasswordEdit.setEchoMode(QLineEdit.Password) self.PasswordEdit.setEchoMode(QLineEdit.EchoMode.Password)
self.ShowPasswordCheckBox.setChecked(False) self.ShowPasswordCheckBox.setChecked(False)
self.FloorComboBox.setCurrentIndex(1) # use for the '__init__' to effect the signal
self.FloorComboBox.setCurrentIndex(0) self.FloorComboBox.setCurrentIndex(0)
self.onFloorComboBoxCurrentIndexChanged()
self.DateEdit.setDate(QDate.currentDate()) self.DateEdit.setDate(QDate.currentDate())
self.DateEdit.setMinimumDate(QDate.currentDate()) self.DateEdit.setMinimumDate(QDate.currentDate())
self.DateEdit.setMaximumDate(QDate.currentDate())
if QTime.currentTime() > QTime(18, 0, 0) and QTime.currentTime() < QTime(23, 0, 0):
self.DateEdit.setMaximumDate(QDate.currentDate().addDays(1))
self.BeginTimeEdit.setTime(QTime.currentTime()) self.BeginTimeEdit.setTime(QTime.currentTime())
self.PreferEarlyBeginTimeCheckBox.setChecked(False) self.PreferEarlyBeginTimeCheckBox.setChecked(False)
self.MaxBeginTimeDiffSpinBox.setValue(10) self.MaxBeginTimeDiffSpinBox.setValue(30)
self.EndTimeEdit.setTime(QTime.currentTime().addSecs(120*60)) self.EndTimeEdit.setTime(QTime.currentTime().addSecs(120*60))
self.PreferLateEndTimeCheckBox.setChecked(False) self.PreferLateEndTimeCheckBox.setChecked(False)
self.MaxEndTimeDiffSpinBox.setValue(10) self.MaxEndTimeDiffSpinBox.setValue(30)
self.ExpectDurationSpinBox.setValue(self.BeginTimeEdit.time().secsTo(self.EndTimeEdit.time())/3600) self.ExpectDurationSpinBox.setValue(self.BeginTimeEdit.time().secsTo(self.EndTimeEdit.time())/3600)
self.SatisfyDurationCheckBox.setChecked(False) self.SatisfyDurationCheckBox.setChecked(False)
@@ -451,21 +476,21 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
) -> bool: ) -> bool:
if users_config_path: if users_config_path:
self.__users_config_data = self.defaultUsersConfig() self.__config_data["users"] = self.defaultUsersConfig()
for index in range(self.UserListWidget.count()): for index in range(self.UserListWidget.count()):
user_config = self.collectUserConfigFromUserListWidget(index) user_config = self.collectUserConfigFromUserListWidget(index)
if user_config: if user_config:
self.__users_config_data["users"].append(user_config) self.__config_data["users"]["users"].append(user_config)
if not self.saveUsersConfig( if not self.saveUsersConfig(
users_config_path, users_config_path,
self.__users_config_data self.__config_data["users"]
): ):
return False return False
if system_config_path: if system_config_path:
self.__system_config_data = self.collectSystemConfigFromWidget() self.__config_data["system"] = self.collectSystemConfigFromWidget()
if not self.saveSystemConfig( if not self.saveSystemConfig(
system_config_path, system_config_path,
self.__system_config_data self.__config_data["system"]
): ):
return False return False
return True return True
@@ -489,12 +514,12 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
system_config = self.loadSystemConfig(config_path) system_config = self.loadSystemConfig(config_path)
users_config = self.loadUsersConfig(config_path) users_config = self.loadUsersConfig(config_path)
if system_config is not None: if system_config is not None:
self.__system_config_data.update(system_config) self.__config_data["system"].update(system_config)
self.setSystemConfigToWidget(self.__system_config_data) self.setSystemConfigToWidget(self.__config_data["system"])
return True return True
if users_config is not None: if users_config is not None:
self.__users_config_data.update(users_config) self.__config_data["users"].update(users_config)
self.fillUsersList(self.__users_config_data) self.fillUsersList(self.__config_data["users"])
return True return True
except: except:
return False return False
@@ -528,12 +553,12 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
"seat_id": "", "seat_id": "",
"begin_time": { "begin_time": {
"time": f"{QTime.currentTime().toString("hh:mm")}", "time": f"{QTime.currentTime().toString("hh:mm")}",
"max_diff": 0, "max_diff": 30,
"prefer_early": False "prefer_early": False
}, },
"end_time": { "end_time": {
"time": f"{QTime.currentTime().addSecs(2*3600).toString("hh:mm")}", "time": f"{QTime.currentTime().addSecs(2*3600).toString("hh:mm")}",
"max_diff": 0, "max_diff": 30,
"prefer_early": True "prefer_early": True
}, },
"expect_duration": 2.0, "expect_duration": 2.0,
@@ -621,7 +646,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
browser_driver_path = QFileDialog.getOpenFileName( browser_driver_path = QFileDialog.getOpenFileName(
self, self,
"选择浏览器驱动 - AutoLibrary", "选择浏览器驱动 - AutoLibrary",
self.CurrentSystemConfigEdit.text(), self.BrowseBrowserDriverEdit.text(),
"可执行文件 (*.exe);;所有文件 (*)" "可执行文件 (*.exe);;所有文件 (*)"
)[0] )[0]
if browser_driver_path: if browser_driver_path:
@@ -700,19 +725,22 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
users_config_path = self.ExportUserConfigEdit.text() users_config_path = self.ExportUserConfigEdit.text()
if system_config_path: if system_config_path:
if self.saveConfigs( if self.saveConfigs(
system_config_path, system_config_path, ""
users_config_path=""
): ):
msg += f"系统配置文件已导出到: \n'{system_config_path}'\n" msg += f"系统配置文件已导出到: \n'{system_config_path}'\n"
else:
msg += f"系统配置文件导出失败: \n'{system_config_path}'\n"
if users_config_path: if users_config_path:
if self.saveConfigs( if self.saveConfigs(
"", users_config_path "", users_config_path
): ):
msg += f"用户配置文件已导出到: \n'{users_config_path}'\n" msg += f"用户配置文件已导出到: \n'{users_config_path}'\n"
else:
msg += f"用户配置文件导出失败: \n'{users_config_path}'\n"
if msg: if msg:
QMessageBox.information( QMessageBox.information(
self, self,
"信息 - AutoLibrary", "提示 - AutoLibrary",
msg msg
) )
@@ -748,21 +776,21 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
exist_files.append(users_config_path) exist_files.append(users_config_path)
reply = QMessageBox.information( reply = QMessageBox.information(
self, self,
"信息 - AutoLibrary", "提示 - AutoLibrary",
f"文件夹中已存在以下文件, 是否覆盖 ?\n{chr(10).join(exist_files)}", f"文件夹中已存在以下文件, 是否覆盖 ?\n{chr(10).join(exist_files)}",
QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes | QMessageBox.No,
QMessageBox.No QMessageBox.No
) )
if reply == QMessageBox.No: if reply == QMessageBox.No:
return return
self.__system_config_data = self.defaultSystemConfig() self.__config_data["system"] = self.defaultSystemConfig()
self.__users_config_data = self.defaultUsersConfig() self.__config_data["users"] = self.defaultUsersConfig()
self.__config_paths = { self.__config_paths = {
"system": system_config_path, "system": system_config_path,
"users": users_config_path "users": users_config_path
} }
self.initlizeConfigToWidget("system", self.__system_config_data) self.initlizeConfigToWidget("system", self.__config_data["system"])
self.initlizeConfigToWidget("users", self.__users_config_data) self.initlizeConfigToWidget("users", self.__config_data["users"])
@Slot() @Slot()
def onConfirmButtonClicked( def onConfirmButtonClicked(
@@ -770,16 +798,16 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
): ):
if self.UserListWidget.currentItem() is not None: if self.UserListWidget.currentItem() is not None:
user = self.collectUserConfigFromUserInfoWidget() user_config = self.collectUserConfigFromUserInfoWidget()
if user: if user_config:
self.UserListWidget.currentItem().setData(Qt.UserRole, user) self.UserListWidget.currentItem().setData(Qt.UserRole, user_config)
if self.saveConfigs( if self.saveConfigs(
self.__config_paths["system"], self.__config_paths["system"],
self.__config_paths["users"] self.__config_paths["users"]
): ):
QMessageBox.information( QMessageBox.information(
self, self,
"信息 - AutoLibrary", "提示 - AutoLibrary",
"配置文件保存成功 !\n" "配置文件保存成功 !\n"
f"系统配置文件路径: \n{self.__config_paths['system']}\n"\ f"系统配置文件路径: \n{self.__config_paths['system']}\n"\
f"用户配置文件路径: \n{self.__config_paths['users']}" f"用户配置文件路径: \n{self.__config_paths['users']}"
+17 -2
View File
@@ -448,11 +448,14 @@
</size> </size>
</property> </property>
<property name="toolTip"> <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> <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="maximum"> <property name="maximum">
<double>8.000000000000000</double> <double>8.000000000000000</double>
</property> </property>
<property name="stepType">
<enum>QAbstractSpinBox::StepType::AdaptiveDecimalStepType</enum>
</property>
</widget> </widget>
</item> </item>
<item row="11" column="4"> <item row="11" column="4">
@@ -620,6 +623,9 @@
<height>25</height> <height>25</height>
</size> </size>
</property> </property>
<property name="calendarPopup">
<bool>true</bool>
</property>
<property name="date"> <property name="date">
<date> <date>
<year>2025</year> <year>2025</year>
@@ -752,6 +758,9 @@
<property name="maximum"> <property name="maximum">
<number>120</number> <number>120</number>
</property> </property>
<property name="stepType">
<enum>QAbstractSpinBox::StepType::AdaptiveDecimalStepType</enum>
</property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="2" column="1">
@@ -842,6 +851,9 @@
<property name="maximum"> <property name="maximum">
<number>120</number> <number>120</number>
</property> </property>
<property name="stepType">
<enum>QAbstractSpinBox::StepType::AdaptiveDecimalStepType</enum>
</property>
</widget> </widget>
</item> </item>
<item row="3" column="4"> <item row="3" column="4">
@@ -911,6 +923,9 @@
<height>25</height> <height>25</height>
</size> </size>
</property> </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="text"> <property name="text">
<string/> <string/>
</property> </property>
@@ -1164,7 +1179,7 @@
</size> </size>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;详情请参阅根目录中的 manual.html&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;详情请参阅 &lt;a href=&quot;https://www.autolibrary.cv/docs/manual_lists.html&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#69fcff;&quot;&gt;用户手册&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="whatsThis"> <property name="whatsThis">
<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>
+3 -5
View File
@@ -13,7 +13,7 @@ import time
import queue import queue
from PySide6.QtCore import ( from PySide6.QtCore import (
Qt, Signal, Slot, QDir, QFileInfo, QTimer, QThread Qt, Signal, Slot, QTimer, QThread
) )
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QMainWindow, QMenu QMainWindow, QMenu
@@ -130,10 +130,8 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
self.__input_queue = queue.Queue() self.__input_queue = queue.Queue()
self.__output_queue = queue.Queue() self.__output_queue = queue.Queue()
self.__config_paths = { self.__config_paths = {
"system": "system": "",
f"{QDir.toNativeSeparators(QFileInfo(sys.executable).absoluteDir().absoluteFilePath("system.json"))}", "users": "",
"users":
f"{QDir.toNativeSeparators(QFileInfo(sys.executable).absoluteDir().absoluteFilePath("users.json"))}",
} }
self.__alConfigWidget = None self.__alConfigWidget = None
self.__auto_lib_thread = None self.__auto_lib_thread = None