1
1
mirror of https://github.com/KenanZhu/AutoLibrary.git synced 2026-06-18 07:23:03 +08:00
Files
AutoLibrary/src/gui/ALConfigWidget.py
T
KenanZhu 25aab588a8 feat(utils): 添加 ConfigManager 与 JSON 配置读写,替换旧实现
add:
- src/utils/ConfigManager.py
- src/utils/JSONReader.py
- src/utils/JSONWriter.py
remove:
- src/utils/ConfigReader.py
- src/utils/ConfigWriter.py
refactor:
- 更新调用方以使用 ConfigManager / JSONReader / JSONWriter(见 ALConfigWidget.py、ALMainWindow.py、ALTimerTaskManageWidget.py、ALMainWorkers.py 等)
- 统一方法命名(initlize* -> initialize*)、改进错误提示与配置路径管理

BREAKING CHANGE: 删除 ConfigReader/ConfigWriter,外部调用需改为 JSONReader/JSONWriter 或通过 ConfigManager 访问配置
2026-02-26 21:18:18 +08:00

1162 lines
42 KiB
Python

# -*- coding: utf-8 -*-
"""
Copyright (c) 2025 - 2026 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
You may use, modify, and distribute this file under the terms of the MIT License.
See the LICENSE file for details.
"""
import os
import sys
from PySide6.QtCore import (
Qt, Signal, Slot, QTime, QDate, QDir, QFileInfo
)
from PySide6.QtWidgets import (
QDialog, QWidget, QLineEdit, QMessageBox, QFileDialog,
QTreeWidgetItem, QMenu, QInputDialog
)
from PySide6.QtGui import (
QCloseEvent, QAction
)
from utils.JSONReader import JSONReader
from utils.JSONWriter import JSONWriter
from utils.ConfigManager import ConfigType, instance
from utils.ConfigManager import getValidateAutomationConfigPaths
from gui.resources.ui.Ui_ALConfigWidget import Ui_ALConfigWidget
from gui.ALSeatMapSelectDialog import ALSeatMapSelectDialog
from gui.ALSeatMapTable import ALSeatMapTable
from gui.ALUserTreeWidget import ALUserTreeWidget, ALUserTreeItemType
class ALConfigWidget(QWidget, Ui_ALConfigWidget):
configWidgetIsClosed = Signal()
def __init__(
self,
parent = None,
):
super().__init__(parent)
self.__cfg_mgr = instance()
self.__config_paths = getValidateAutomationConfigPaths()
self.__config_data = {"run": {}, "user": {}}
self.setupUi(self)
self.modifyUi()
self.connectSignals()
if not self.initializeConfigs():
self.close()
def modifyUi(
self
):
self.setWindowFlags(Qt.WindowType.Window)
# replace the treewidget with ALUserTreeWidget
self.UserTreeWidget.setParent(None)
self.UserTreeWidget.deleteLater()
self.UserTreeWidget = ALUserTreeWidget()
self.UserListLayout.insertWidget(0, self.UserTreeWidget)
self.UserTreeWidget.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.UserTreeWidget.customContextMenuRequested.connect(self.onUserTreeWidgetContextMenu)
self.initializeFloorRoomMap()
self.initializeUserInfoWidget()
def connectSignals(
self
):
self.ShowPasswordCheckBox.clicked.connect(self.onShowPasswordCheckBoxChecked)
self.FloorComboBox.currentIndexChanged.connect(self.onFloorComboBoxCurrentIndexChanged)
self.SelectSeatsButton.clicked.connect(self.onSelectSeatsButtonClicked)
self.UserTreeWidget.currentItemChanged.connect(self.onUserTreeWidgetCurrentItemChanged)
self.UserTreeWidget.itemChanged.connect(self.onUserTreeWidgetItemChanged)
self.AddUserButton.clicked.connect(self.onAddUserButtonClicked)
self.DelUserButton.clicked.connect(self.onDelUserButtonClicked)
self.BrowseBrowserDriverButton.clicked.connect(self.onBrowseBrowserDriverButtonClicked)
self.BrowseCurrentRunConfigButton.clicked.connect(self.onBrowseCurrentRunConfigButtonClicked)
self.BrowseCurrentUserConfigButton.clicked.connect(self.onBrowseCurrentUserConfigButtonClicked)
self.BrowseExportRunConfigButton.clicked.connect(self.onBrowseExportRunConfigButtonClicked)
self.BrowseExportUserConfigButton.clicked.connect(self.onBrowseExportUserConfigButtonClicked)
self.ExportConfigButton.clicked.connect(self.onExportConfigButtonClicked)
self.NewConfigButton.clicked.connect(self.onNewConfigButtonClicked)
self.LoadConfigButton.clicked.connect(self.onLoadConfigButtonClicked)
self.ConfirmButton.clicked.connect(self.onConfirmButtonClicked)
self.CancelButton.clicked.connect(self.onCancelButtonClicked)
def showEvent(
self,
event
):
result = super().showEvent(event)
screen_rect = self.screen().geometry()
target_pos = self.parent().geometry().center()
target_pos.setX(target_pos.x() - self.width()//2)
target_pos.setY(target_pos.y() - self.height()//2)
if target_pos.x() < 0:
target_pos.setX(0)
if target_pos.x() + self.width() > screen_rect.width():
target_pos.setX(screen_rect.width() - self.width())
if target_pos.y() < 0:
target_pos.setY(0)
if target_pos.y() + self.height() > screen_rect.height():
target_pos.setY(screen_rect.height() - self.height())
self.move(target_pos)
return result
def closeEvent(
self,
event: QCloseEvent
):
self.configWidgetIsClosed.emit()
super().closeEvent(event)
def initializeFloorRoomMap(
self
):
self.__floor_map = {
"2": "二层",
"3": "三层",
"4": "四层",
"5": "五层"
}
self.__room_map = {
"1": "二层内环",
"2": "二层西区",
"3": "三层内环",
"4": "三层外环",
"5": "四层内环",
"6": "四层外环",
"7": "四层期刊",
"8": "五层考研"
}
self.__floor_rmap = {
v: k for k, v in self.__floor_map.items()
}
self.__room_rmap = {
v: k for k, v in self.__room_map.items()
}
self.__floor_room_map = {
"二层": ["二层内环", "二层西区"],
"三层": ["三层内环", "三层外环"],
"四层": ["四层内环", "四层外环", "四层期刊"],
"五层": ["五层考研"]
}
def initializeConfigToWidget(
self,
which: str,
config_data: dict
):
if which == "run":
self.setRunConfigToWidget(config_data)
self.CurrentRunConfigEdit.setText(self.__config_paths["run"])
elif which == "user":
self.initializeUserInfoWidget()
self.setUsersToTreeWidget(config_data)
self.CurrentUserConfigEdit.setText(self.__config_paths["user"])
def initializeConfig(
self,
which: str
) -> bool:
msg = "" # no use for now
is_success = True
if which == "run":
run_config_path = self.__config_paths[which]
if not os.path.exists(run_config_path):
self.__config_data[which] = self.defaultRunConfig()
self.__config_paths[which] = self.__default_config_paths[which]
if self.saveRunConfig(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.loadRunConfig(run_config_path)
if self.__config_data[which] is None:
is_success = False
elif which == "user":
user_config_path = self.__config_paths[which]
if not os.path.exists(user_config_path):
self.__config_data[which] = self.defaultUserConfig()
self.__config_paths[which] = self.__default_config_paths[which]
if self.saveUserConfig(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.loadUserConfig(user_config_path)
if self.__config_data[which] is None:
is_success = False
return is_success
def initializeConfigs(
self
) -> bool:
is_success = True
for which in ["run", "user"]:
if not self.initializeConfig(which):
is_success = False
break
self.initializeConfigToWidget(which, self.__config_data[which])
return is_success
def defaultRunConfig(
self
) -> dict:
return {
"library": {
"host_url": "http://10.1.20.7",
"login_url": "/login"
},
"login": {
"auto_captcha": True,
"max_attempt": 3
},
"web_driver": {
"driver_type": "edge",
"driver_path": "",
"headless": False
},
"mode": {
"run_mode": 1
}
}
def defaultUserConfig(
self
) -> dict:
return {
"groups": [
]
}
def collectRunConfigFromWidget(
self
) -> dict:
run_config = self.defaultRunConfig()
# library config is never changed
run_config["login"]["auto_captcha"] = self.AutoCaptchaCheckBox.isChecked()
run_config["login"]["max_attempt"] = self.LoginAttemptSpinBox.value()
run_config["web_driver"]["driver_type"] = self.BrowserTypeComboBox.currentText()
run_config["web_driver"]["driver_path"] = self.BrowseBrowserDriverEdit.text()
run_config["web_driver"]["headless"] = self.HeadlessCheckBox.isChecked()
run_mode = 0
if self.AutoReserveCheckBox.isChecked():
run_mode |= 0x01
if self.AutoCheckinCheckBox.isChecked():
run_mode |= 0x02
if self.AutoRenewalCheckBox.isChecked():
run_mode |= 0x04
run_config["mode"]["run_mode"] = run_mode
return run_config
def setRunConfigToWidget(
self,
run_config: dict
):
try:
self.HostUrlEdit.setText(run_config["library"]["host_url"])
self.LoginUrlEdit.setText(run_config["library"]["login_url"])
self.AutoCaptchaCheckBox.setChecked(run_config["login"]["auto_captcha"])
self.LoginAttemptSpinBox.setValue(run_config["login"]["max_attempt"])
self.BrowserTypeComboBox.setCurrentText(run_config["web_driver"]["driver_type"])
if run_config["web_driver"]["driver_path"]:
driver_path = os.path.abspath(run_config["web_driver"]["driver_path"])
else:
driver_path = ""
self.BrowseBrowserDriverEdit.setText(QDir.toNativeSeparators(driver_path))
self.HeadlessCheckBox.setChecked(run_config["web_driver"]["headless"])
run_mode = run_config["mode"]["run_mode"]
self.AutoReserveCheckBox.setChecked(run_mode&0x01)
self.AutoCheckinCheckBox.setChecked(run_mode&0x02)
self.AutoRenewalCheckBox.setChecked(run_mode&0x04)
except KeyError as e:
QMessageBox.warning(
self,
"警告 - AutoLibrary",
f"运行配置文件读取键 '{e}' 时发生错误 ! :\n"
f"文件路径: {self.__config_paths['run']}\n"
"文件可能被意外修改或已经损坏\n"
)
except Exception as e:
QMessageBox.warning(
self,
"警告 - AutoLibrary",
f"运行配置文件读取键 '{e}' 时发生未知错误 ! :\n"
f"文件路径: {self.__config_paths['run']}\n"
"文件可能被意外修改或已经损坏\n"
)
def initializeUserInfoWidget(
self
):
self.UsernameEdit.setText("")
self.PasswordEdit.setText("")
self.PasswordEdit.setEchoMode(QLineEdit.EchoMode.Password)
self.ShowPasswordCheckBox.setChecked(False)
self.FloorComboBox.setCurrentIndex(0)
self.onFloorComboBoxCurrentIndexChanged()
self.DateEdit.setDate(QDate.currentDate())
self.DateEdit.setMinimumDate(QDate.currentDate())
self.BeginTimeEdit.setTime(QTime.currentTime())
self.PreferEarlyBeginTimeCheckBox.setChecked(False)
self.MaxBeginTimeDiffSpinBox.setValue(30)
self.EndTimeEdit.setTime(QTime.currentTime().addSecs(120*60))
self.PreferLateEndTimeCheckBox.setChecked(False)
self.MaxEndTimeDiffSpinBox.setValue(30)
self.ExpectDurationSpinBox.setValue(self.BeginTimeEdit.time().secsTo(self.EndTimeEdit.time())/3600)
self.SatisfyDurationCheckBox.setChecked(False)
self.ExpectRenewDurationSpinBox.setValue(1.0)
self.MaxRenewTimeDiffSpinBox.setValue(30)
self.PreferLateRenewTimeCheckBox.setChecked(False)
def collectUserFromWidget(
self
) -> dict:
user = {
"username": self.UsernameEdit.text(),
"password": self.PasswordEdit.text(),
"enabled": True,
"reserve_info": {
"begin_time":{},
"end_time": {},
"renew_time": {}
}
}
user["reserve_info"]["date"] = self.DateEdit.dateTime().toString("yyyy-MM-dd")
user["reserve_info"]["place"] = self.PlaceComboBox.currentText()
user["reserve_info"]["floor"] = self.__floor_rmap[self.FloorComboBox.currentText()]
user["reserve_info"]["room"] = self.__room_rmap[self.RoomComboBox.currentText()]
user["reserve_info"]["seat_id"] = self.SeatIDEdit.text()
user["reserve_info"]["begin_time"]["time"] = self.BeginTimeEdit.time().toString("HH:mm")
user["reserve_info"]["begin_time"]["max_diff"] = self.MaxBeginTimeDiffSpinBox.value()
user["reserve_info"]["begin_time"]["prefer_early"] = self.PreferEarlyBeginTimeCheckBox.isChecked()
user["reserve_info"]["end_time"]["time"] = self.EndTimeEdit.time().toString("HH:mm")
user["reserve_info"]["end_time"]["max_diff"] = self.MaxEndTimeDiffSpinBox.value()
user["reserve_info"]["end_time"]["prefer_early"] = not self.PreferLateEndTimeCheckBox.isChecked()
user["reserve_info"]["expect_duration"] = self.ExpectDurationSpinBox.value()
user["reserve_info"]["satisfy_duration"] = self.SatisfyDurationCheckBox.isChecked()
user["reserve_info"]["renew_time"]["expect_duration"] = self.ExpectRenewDurationSpinBox.value()
user["reserve_info"]["renew_time"]["max_diff"] = self.MaxRenewTimeDiffSpinBox.value()
user["reserve_info"]["renew_time"]["prefer_early"] = not self.PreferLateRenewTimeCheckBox.isChecked()
return user
def collectUsersFromTreeWidget(
self
) -> dict:
user_config = self.defaultUserConfig()
for i in range(self.UserTreeWidget.topLevelItemCount()):
group_item = self.UserTreeWidget.topLevelItem(i)
group_config = {
"name": group_item.text(0),
"enabled": group_item.checkState(1) == Qt.CheckState.Checked,
"users": []
}
for j in range(group_item.childCount()):
user_item = group_item.child(j)
user = user_item.data(0, Qt.UserRole)
if not user:
continue
user["enabled"] = user_item.checkState(1) == Qt.CheckState.Checked
group_config["users"].append(user)
user_config["groups"].append(group_config)
return user_config
def setUserToWidget(
self,
user: dict
) -> None:
try:
self.UsernameEdit.setText(user["username"])
self.PasswordEdit.setText(user["password"])
self.DateEdit.setDate(QDate.fromString(user["reserve_info"]["date"], "yyyy-MM-dd"))
self.PlaceComboBox.setCurrentText(user["reserve_info"]["place"])
self.FloorComboBox.setCurrentText(self.__floor_map[user["reserve_info"]["floor"]])
self.RoomComboBox.setCurrentText(self.__room_map[user["reserve_info"]["room"]])
self.SeatIDEdit.setText(user["reserve_info"]["seat_id"])
self.BeginTimeEdit.setTime(QTime.fromString(user["reserve_info"]["begin_time"]["time"], "H:mm"))
self.MaxBeginTimeDiffSpinBox.setValue(user["reserve_info"]["begin_time"]["max_diff"])
self.PreferEarlyBeginTimeCheckBox.setChecked(user["reserve_info"]["begin_time"]["prefer_early"])
self.EndTimeEdit.setTime(QTime.fromString(user["reserve_info"]["end_time"]["time"], "H:mm"))
self.MaxEndTimeDiffSpinBox.setValue(user["reserve_info"]["end_time"]["max_diff"])
self.PreferLateEndTimeCheckBox.setChecked(not user["reserve_info"]["end_time"]["prefer_early"])
self.ExpectDurationSpinBox.setValue(user["reserve_info"]["expect_duration"])
self.SatisfyDurationCheckBox.setChecked(user["reserve_info"]["satisfy_duration"])
self.ExpectRenewDurationSpinBox.setValue(user["reserve_info"]["renew_time"]["expect_duration"])
self.MaxRenewTimeDiffSpinBox.setValue(user["reserve_info"]["renew_time"]["max_diff"])
self.PreferLateRenewTimeCheckBox.setChecked(not user["reserve_info"]["renew_time"]["prefer_early"])
except KeyError as e:
QMessageBox.warning(
self,
"警告 - AutoLibrary",
f"用户配置文件读取键 '{e}' 时发生错误 ! :\n"
f"文件路径: {self.__config_paths['user']}\n"
"文件可能被意外修改或已经损坏\n"
)
except Exception as e:
QMessageBox.warning(
self,
"警告 - AutoLibrary",
f"用户配置文件读取键 '{e}' 时发生未知错误 ! :\n"
f"文件路径: {self.__config_paths['user']}\n"
"文件可能被意外修改或已经损坏\n"
)
def setUsersToTreeWidget(
self,
users: dict
):
self.UserTreeWidget.clear()
self.UserTreeWidget.itemChanged.disconnect(self.onUserTreeWidgetItemChanged)
try:
if "groups" in users:
for group_config in users["groups"]:
group_item = QTreeWidgetItem(self.UserTreeWidget, ALUserTreeItemType.GROUP.value)
group_item.setText(0, group_config["name"])
group_item.setFlags(group_item.flags() | Qt.ItemIsEditable)
group_item.setCheckState(1, Qt.Checked if group_config.get("enabled", True) else Qt.Unchecked)
for user_config in group_config["users"]:
user_item = QTreeWidgetItem(group_item, ALUserTreeItemType.USER.value)
user_item.setText(0, user_config["username"])
user_item.setText(1, "" if user_config.get("enabled", True) else "跳过")
user_item.setData(0, Qt.UserRole, user_config)
user_item.setCheckState(1, Qt.Checked if user_config.get("enabled", True) else Qt.Unchecked)
user_item.setDisabled(not group_config.get("enabled", True))
group_item.setExpanded(True)
except KeyError as e:
QMessageBox.warning(
self,
"警告 - AutoLibrary",
f"用户配置文件读取键 '{e}' 时发生错误 ! :\n"
f"文件路径: {self.__config_paths['user']}\n"
"文件可能被意外修改或已经损坏\n"
)
except Exception as e:
QMessageBox.warning(
self,
"警告 - AutoLibrary",
f"用户配置文件读取键 '{e}' 时发生未知错误 ! :\n"
f"文件路径: {self.__config_paths['user']}\n"
"文件可能被意外修改或已经损坏\n"
)
finally:
self.UserTreeWidget.itemChanged.connect(self.onUserTreeWidgetItemChanged)
def loadRunConfig(
self,
run_config_path: str
) -> dict:
try:
if not run_config_path or not os.path.exists(run_config_path):
raise Exception("文件路径不存在")
run_config = JSONReader(run_config_path).data()
if run_config and "library" in run_config\
and "web_driver" in run_config\
and "login" in run_config:
return run_config
else:
return None
except Exception as e:
QMessageBox.warning(
self,
"警告 - AutoLibrary",
f"运行配置文件读取发生错误 ! :\n{e}"
)
return None
def saveRunConfig(
self,
run_config_path: str,
run_config_data: dict
) -> bool:
try:
if not run_config_path:
raise Exception("文件路径为空")
if not run_config_data or not isinstance(run_config_data, dict):
raise Exception("运行配置数据为空或类型错误")
JSONWriter(run_config_path, run_config_data)
return True
except Exception as e:
QMessageBox.warning(
self,
"警告 - AutoLibrary",
f"配置文件写入发生错误 ! : \n{e}"
)
return False
def loadUserConfig(
self,
user_config_path: str
) -> dict:
try:
if not user_config_path or not os.path.exists(user_config_path):
raise Exception("文件路径不存在")
user_config = JSONReader(user_config_path).data()
if user_config and "groups" in user_config:
return user_config
# compatibility with old version config format
elif user_config and "users" in user_config:
user_config = {
"groups": [
{
"name": f"兼容分组-{QFileInfo(user_config_path).fileName()}",
"enabled": True,
"users": user_config["users"]
}
]
}
return user_config
else:
return None
except Exception as e:
QMessageBox.warning(
self,
"警告 - AutoLibrary",
f"用户配置文件读取发生错误 ! :\n{e}"
)
return None
def saveUserConfig(
self,
user_config_path: str,
user_config_data: dict
) -> bool:
try:
if not user_config_path:
raise Exception("文件路径为空")
if not user_config_data or not isinstance(user_config_data, dict):
raise Exception("用户配置数据为空或类型错误")
JSONWriter(user_config_path, user_config_data)
return True
except Exception as e:
QMessageBox.warning(
self,
"警告 - AutoLibrary",
f"用户配置文件写入发生错误 ! :\n{e}"
)
return False
def saveConfigs(
self,
run_config_path: str,
user_config_path: str
) -> bool:
if user_config_path:
self.__config_data["user"] = self.collectUsersFromTreeWidget()
if not self.saveUserConfig(
user_config_path,
self.__config_data["user"]
):
return False
if run_config_path:
self.__config_data["run"] = self.collectRunConfigFromWidget()
if not self.saveRunConfig(
run_config_path,
self.__config_data["run"]
):
return False
return True
def loadConfig(
self,
config_path: str
) -> bool:
if not config_path:
config_path = QFileDialog.getOpenFileName(
self,
"从现有配置文件中加载 - AutoLibrary",
f"{QDir.toNativeSeparators(QDir.currentPath())}",
"JSON 文件 (*.json);;所有文件 (*)"
)[0]
if not config_path:
return False
try:
run_config = self.loadRunConfig(config_path)
user_config = self.loadUserConfig(config_path)
if run_config is not None:
self.__config_data["run"].update(run_config)
self.setRunConfigToWidget(self.__config_data["run"])
return True
if user_config is not None:
self.__config_data["user"].update(user_config)
self.setUsersToTreeWidget(self.__config_data["user"])
return True
except:
return False
def addGroup(
self,
group_name: str = ""
) -> QTreeWidgetItem:
self.UserTreeWidget.itemChanged.disconnect(self.onUserTreeWidgetItemChanged)
group_item = QTreeWidgetItem(self.UserTreeWidget, ALUserTreeItemType.GROUP.value)
if not group_name:
group_name = f"新分组-{self.UserTreeWidget.topLevelItemCount()}"
group_item.setText(0, group_name)
group_item.setFlags(group_item.flags() | Qt.ItemIsEditable)
group_item.setCheckState(1, Qt.Checked)
self.UserTreeWidget.setCurrentItem(group_item)
self.UserTreeWidget.itemChanged.connect(self.onUserTreeWidgetItemChanged)
return group_item
def delGroup(
self,
group_item: QTreeWidgetItem = None
):
if group_item is None:
return
if group_item.type() != ALUserTreeItemType.GROUP.value:
return
index = self.UserTreeWidget.indexOfTopLevelItem(group_item)
self.UserTreeWidget.takeTopLevelItem(index)
def addUser(
self,
group_item: QTreeWidgetItem = None
) -> QTreeWidgetItem:
if group_item is None:
current_item = self.UserTreeWidget.currentItem()
if current_item is None:
group_item = self.addGroup()
if group_item.type() == ALUserTreeItemType.USER.value:
group_item = group_item.parent()
if group_item.checkState(1) == Qt.CheckState.Unchecked:
return None
new_user = {
"username": f"新用户-{group_item.childCount()}",
"password": "000000",
"enabled": True,
"reserve_info": {
"date": f"{QDate.currentDate().toString("yyyy-MM-dd")}",
"place": "\u56fe\u4e66\u9986",
"floor": "2",
"room": "1",
"seat_id": "",
"begin_time": {
"time": f"{QTime.currentTime().toString("hh:mm")}",
"max_diff": 30,
"prefer_early": False
},
"end_time": {
"time": f"{QTime.currentTime().addSecs(2*3600).toString("hh:mm")}",
"max_diff": 30,
"prefer_early": True
},
"expect_duration": 2.0,
"satisfy_duration": False,
"renew_time": {
"expect_duration": 1.0,
"max_diff": 30,
"prefer_early": True
}
}
}
self.UserTreeWidget.itemChanged.disconnect(self.onUserTreeWidgetItemChanged)
user_item = QTreeWidgetItem(group_item, ALUserTreeItemType.USER.value)
user_item.setText(0, new_user["username"])
user_item.setText(1, "")
user_item.setData(0, Qt.UserRole, new_user)
user_item.setCheckState(1, Qt.CheckState.Checked)
group_item.setExpanded(True)
self.UserTreeWidget.setCurrentItem(user_item)
self.setUserToWidget(new_user)
self.UserTreeWidget.itemChanged.connect(self.onUserTreeWidgetItemChanged)
return user_item
def delUser(
self,
user_item: QTreeWidgetItem = None
):
if user_item is None:
return
if user_item.type() != ALUserTreeItemType.USER.value:
return
parent_item = user_item.parent()
index = parent_item.indexOfChild(user_item)
parent_item.takeChild(index)
if parent_item.childCount() == 0:
self.UserTreeWidget.setCurrentItem(None)
def renameItem(
self,
item: QTreeWidgetItem,
):
if item is None:
return
old_name = item.text(0)
if item.parent() is None:
item_type = "分组"
else:
item_type = "用户"
new_name, ok = QInputDialog.getText(
self, f"重命名{item_type}项 : '{old_name}'", f"请输入新的{item_type}名:", text=old_name
)
new_name = new_name.strip()
if not ok or not new_name:
return
item.setText(0, new_name)
if item.type() == ALUserTreeItemType.GROUP.value:
item.setText(0, new_name)
else:
user = item.data(0, Qt.UserRole)
user["username"] = new_name
item.setText(0, new_name)
item.setData(0, Qt.UserRole, user)
self.setUserToWidget(user)
@Slot()
def onShowPasswordCheckBoxChecked(
self,
checked: bool
):
if checked:
self.PasswordEdit.setEchoMode(QLineEdit.Normal)
else:
self.PasswordEdit.setEchoMode(QLineEdit.Password)
@Slot()
def onFloorComboBoxCurrentIndexChanged(
self
):
floor = self.FloorComboBox.currentText()
self.RoomComboBox.clear()
self.RoomComboBox.addItems(self.__floor_room_map[floor])
self.RoomComboBox.setCurrentIndex(0)
@Slot()
def onSelectSeatsButtonClicked(
self
):
floor = self.FloorComboBox.currentText()
room = self.RoomComboBox.currentText()
floor_idx = self.__floor_rmap[floor]
room_idx = self.__room_rmap[room]
dialog = ALSeatMapSelectDialog(
self,
floor,
room,
ALSeatMapTable[floor_idx][room_idx]
)
dialog.selectSeats(self.SeatIDEdit.text().split(","))
if dialog.exec() == QDialog.DialogCode.Accepted:
selected_seats = dialog.getSelectedSeats()
if len(selected_seats) == 0:
self.SeatIDEdit.clear()
return
self.SeatIDEdit.setText(",".join(dialog.getSelectedSeats()))
@Slot()
def onUserTreeWidgetCurrentItemChanged(
self,
current: QTreeWidgetItem,
previous: QTreeWidgetItem
):
# dont care about the 'self.__config_data["user"]', we already
# cant effectively update the data of each user, due to the
# possiblity of frequency edit. we just let the QListWidget
# help us.
if previous and previous.type() == ALUserTreeItemType.USER.value:
user = self.collectUserFromWidget()
if user:
self.UsernameEdit.textEdited.disconnect()
user["enabled"] = previous.checkState(1) == Qt.Checked
previous.setText(0, user["username"])
previous.setText(1, "" if user.get("enabled", True) else "跳过")
previous.setData(0, Qt.UserRole, user)
if current is None:
self.initializeUserInfoWidget()
return
if current.type() == ALUserTreeItemType.USER.value:
user = current.data(0, Qt.UserRole)
if user:
self.setUserToWidget(user)
self.UsernameEdit.textEdited.connect(lambda text: current.setText(0, text))
else:
self.initializeUserInfoWidget()
@Slot()
def onUserTreeWidgetItemChanged(
self,
item: QTreeWidgetItem,
column: int
):
if item is None:
return
if column != 1:
return
if item.type() == ALUserTreeItemType.GROUP.value:
is_checked = item.checkState(1) == Qt.CheckState.Checked
for i in range(item.childCount()):
child = item.child(i)
if self.UserTreeWidget.currentItem() == child:
self.UserTreeWidget.setCurrentItem(item)
child.setDisabled(not is_checked)
else:
is_checked = item.checkState(1) == Qt.CheckState.Checked
item.setText(1, "" if is_checked else "跳过")
def showTreeMenu(
self,
menu: QMenu
):
add_group_action = QAction("添加分组", menu)
add_group_action.triggered.connect(self.addGroup)
menu.addAction(add_group_action)
def showGroupMenu(
self,
menu: QMenu,
group_item: QTreeWidgetItem = None
):
add_user_action = QAction("添加用户", menu)
rename_group_action = QAction("重命名分组", menu)
del_group_action = QAction("删除分组", menu)
add_user_action.triggered.connect(lambda: self.addUser(group_item))
rename_group_action.triggered.connect(lambda: self.renameItem(group_item))
del_group_action.triggered.connect(lambda: self.delGroup(group_item))
menu.addAction(add_user_action)
menu.addSeparator()
menu.addAction(rename_group_action)
menu.addAction(del_group_action)
if group_item.checkState(1) == Qt.CheckState.Unchecked:
add_user_action.setEnabled(False)
def showUserMenu(
self,
menu: QMenu,
user_item: QTreeWidgetItem = None
):
rename_user_action = QAction("重命名用户", menu)
del_user_action = QAction("删除用户", menu)
rename_user_action.triggered.connect(lambda: self.renameItem(user_item))
del_user_action.triggered.connect(lambda: self.delUser(user_item))
menu.addAction(rename_user_action)
menu.addAction(del_user_action)
@Slot()
def onUserTreeWidgetContextMenu(
self,
pos
):
current_item = self.UserTreeWidget.itemAt(pos)
menu = QMenu(self.UserTreeWidget)
if current_item is None:
self.showTreeMenu(menu)
elif current_item.type() == ALUserTreeItemType.GROUP.value:
self.showGroupMenu(menu, current_item)
else:
self.showUserMenu(menu, current_item)
menu.exec_(self.UserTreeWidget.mapToGlobal(pos))
@Slot()
def onAddUserButtonClicked(
self
):
current_item = self.UserTreeWidget.currentItem()
self.addUser(current_item)
@Slot()
def onDelUserButtonClicked(
self
):
current_item = self.UserTreeWidget.currentItem()
self.delUser(current_item)
@Slot()
def onBrowseBrowserDriverButtonClicked(
self
):
browser_driver_path = QFileDialog.getOpenFileName(
self,
"选择浏览器驱动 - AutoLibrary",
self.BrowseBrowserDriverEdit.text(),
"可执行文件 (*.exe);;所有文件 (*)"
)[0]
if browser_driver_path:
self.BrowseBrowserDriverEdit.setText(QDir.toNativeSeparators(browser_driver_path))
@Slot()
def onBrowseCurrentRunConfigButtonClicked(
self
):
run_config_path = QFileDialog.getOpenFileName(
self,
"选择其它的运行配置 - AutoLibrary",
self.CurrentRunConfigEdit.text(),
"JSON 文件 (*.json);;所有文件 (*)"
)[0]
if run_config_path:
run_config_path = QDir.toNativeSeparators(run_config_path)
data = self.loadRunConfig(run_config_path)
if data is not None:
self.__config_data["run"].update(data)
self.setRunConfigToWidget(data)
self.__config_paths["run"] = run_config_path
self.CurrentRunConfigEdit.setText(run_config_path)
paths = self.__cfg_mgr.get(ConfigType.GLOBAL, "automation.run_path.paths", [])
if run_config_path not in paths:
paths.append(run_config_path)
index = len(paths) - 1
else:
index = paths.index(run_config_path)
self.__cfg_mgr.set(ConfigType.GLOBAL, "automation.run_path", {"current": index, "paths": paths})
else:
QMessageBox.warning(
self,
"警告 - AutoLibrary",
"运行配置文件读取发生错误 ! :\n"\
"无法从选择的运行配置文件中加载数据 ! :\n"\
"可能选择了错误的配置文件类型"
)
@Slot()
def onBrowseCurrentUserConfigButtonClicked(
self
):
user_config_path = QFileDialog.getOpenFileName(
self,
"选择其它的用户配置 - AutoLibrary",
self.CurrentUserConfigEdit.text(),
"JSON 文件 (*.json);;所有文件 (*)"
)[0]
if user_config_path:
user_config_path = QDir.toNativeSeparators(user_config_path)
data = self.loadUserConfig(user_config_path)
if data is not None:
self.__config_data["user"].update(data)
self.setUsersToTreeWidget(data)
self.__config_paths["user"] = user_config_path
self.CurrentUserConfigEdit.setText(user_config_path)
paths = self.__cfg_mgr.get(ConfigType.GLOBAL, "automation.user_path.paths", [])
if user_config_path not in paths:
paths.append(user_config_path)
index = len(paths) - 1
else:
index = paths.index(user_config_path)
self.__cfg_mgr.set(ConfigType.GLOBAL, "automation.user_path", {"current": index, "paths": paths})
else:
QMessageBox.warning(
self,
"警告 - AutoLibrary",
"用户配置文件读取发生错误 ! :\n"\
"无法从选择的用户配置文件中加载数据 ! :\n"\
"可能选择了错误的配置文件类型"
)
@Slot()
def onBrowseExportRunConfigButtonClicked(
self
):
run_config_path = QFileDialog.getSaveFileName(
self,
"导出运行配置 - AutoLibrary",
self.CurrentRunConfigEdit.text(),
"JSON 文件 (*.json);;所有文件 (*)"
)[0]
if run_config_path:
self.ExportRunConfigEdit.setText(QDir.toNativeSeparators(run_config_path))
@Slot()
def onBrowseExportUserConfigButtonClicked(
self
):
user_config_path = QFileDialog.getSaveFileName(
self,
"导出用户配置 - AutoLibrary",
self.CurrentUserConfigEdit.text(),
"JSON 文件 (*.json);;所有文件 (*)"
)[0]
if user_config_path:
self.ExportUserConfigEdit.setText(QDir.toNativeSeparators(user_config_path))
@Slot()
def onExportConfigButtonClicked(
self
):
msg = ""
run_config_path = self.ExportRunConfigEdit.text()
user_config_path = self.ExportUserConfigEdit.text()
if run_config_path:
if self.saveConfigs(
run_config_path, ""
):
msg += f"运行配置文件已导出到: \n'{run_config_path}'\n"
else:
msg += f"运行配置文件导出失败: \n'{run_config_path}'\n"
if user_config_path:
if self.saveConfigs(
"", user_config_path
):
msg += f"用户配置文件已导出到: \n'{user_config_path}'\n"
else:
msg += f"用户配置文件导出失败: \n'{user_config_path}'\n"
if msg:
QMessageBox.information(
self,
"提示 - AutoLibrary",
msg
)
@Slot()
def onLoadConfigButtonClicked(
self
):
self.loadConfig("")
@Slot()
def onNewConfigButtonClicked(
self
):
file_path = self.CurrentRunConfigEdit.text()
folder_dir = QFileDialog.getExistingDirectory(
self,
"选择新建配置的文件夹 - AutoLibrary",
QDir.toNativeSeparators(QFileInfo(os.path.abspath(file_path)).absoluteDir().path())
)
if not folder_dir:
return
run_config_path = QDir.toNativeSeparators(os.path.join(folder_dir, "run.json"))
user_config_path = QDir.toNativeSeparators(os.path.join(folder_dir, "user.json"))
run_exists = os.path.isfile(run_config_path)
user_exists = os.path.isfile(user_config_path)
if run_exists or user_exists:
exist_files = []
if run_exists:
exist_files.append(f"运行配置文件: \n{run_config_path}")
if user_exists:
exist_files.append(f"用户配置文件: \n{user_config_path}")
reply = QMessageBox.information(
self,
"提示 - AutoLibrary",
f"文件夹中已存在以下文件, 是否覆盖 ?\n{chr(10).join(exist_files)}",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No
)
if reply == QMessageBox.No:
return
self.__config_data["run"] = self.defaultRunConfig()
self.__config_data["user"] = self.defaultUserConfig()
self.__config_paths = {
"run": run_config_path,
"user": user_config_path
}
self.initializeConfigToWidget("run", self.__config_data["run"])
self.initializeConfigToWidget("user", self.__config_data["user"])
@Slot()
def onConfirmButtonClicked(
self
):
current_item = self.UserTreeWidget.currentItem()
if current_item and current_item.type() == ALUserTreeItemType.USER.value:
self.UserTreeWidget.setCurrentItem(None)
if self.saveConfigs(
self.__config_paths["run"],
self.__config_paths["user"]
):
QMessageBox.information(
self,
"提示 - AutoLibrary",
"配置文件保存成功 ! :\n"
f"运行配置文件路径: \n{self.__config_paths['run']}\n"\
f"用户配置文件路径: \n{self.__config_paths['user']}"
)
else:
QMessageBox.warning(
self,
"警告 - AutoLibrary",
"配置文件保存失败 !\n"
)
self.close()
@Slot()
def onCancelButtonClicked(
self
):
self.close()