diff --git a/src/gui/ALConfigWidget.py b/src/gui/ALConfigWidget.py
index 7b16c2f..218c8cc 100644
--- a/src/gui/ALConfigWidget.py
+++ b/src/gui/ALConfigWidget.py
@@ -14,13 +14,18 @@ from PySide6.QtCore import (
Qt, Signal, Slot, QTime, QDate, QDir, QFileInfo
)
from PySide6.QtWidgets import (
- QWidget, QLineEdit, QMessageBox, QFileDialog, QListWidgetItem
+ QWidget, QLineEdit, QMessageBox, QFileDialog,
+ QTreeWidgetItem, QMenu, QInputDialog
+)
+from PySide6.QtGui import (
+ QCloseEvent, QAction
)
-from PySide6.QtGui import QCloseEvent
from gui.Ui_ALConfigWidget import Ui_ALConfigWidget
from gui.ALSeatMapWidget import ALSeatMapWidget
from gui.ALSeatMapTable import seats_maps
+from gui.ALUserTreeWidget import TreeItemType
+from gui.ALUserTreeWidget import ALUserTreeWidget
from utils.ConfigReader import ConfigReader
from utils.ConfigWriter import ConfigWriter
@@ -34,8 +39,8 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
self,
parent = None,
config_paths = {
- "system": "",
- "users": ""
+ "run": "",
+ "user": ""
}
):
@@ -43,7 +48,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
self.setupUi(self)
self.__config_paths = config_paths
- self.__config_data = {"system": {}, "users": {}}
+ self.__config_data = {"run": {}, "user": {}}
self.__seat_map_widget = None
self.modifyUi()
@@ -59,6 +64,13 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
):
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.initlizeFloorRoomMap()
self.initilizeUserInfoWidget()
@@ -70,13 +82,14 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
self.ShowPasswordCheckBox.clicked.connect(self.onShowPasswordCheckBoxChecked)
self.FloorComboBox.currentIndexChanged.connect(self.onFloorComboBoxCurrentIndexChanged)
self.SelectSeatsButton.clicked.connect(self.onSelectSeatsButtonClicked)
- self.UserListWidget.currentItemChanged.connect(self.onUserListWidgetCurrentItemChanged)
+ 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.BrowseCurrentSystemConfigButton.clicked.connect(self.onBrowseCurrentSystemConfigButtonClicked)
+ self.BrowseCurrentRunConfigButton.clicked.connect(self.onBrowseCurrentRunConfigButtonClicked)
self.BrowseCurrentUserConfigButton.clicked.connect(self.onBrowseCurrentUserConfigButtonClicked)
- self.BrowseExportSystemConfigButton.clicked.connect(self.onBrowseExportSystemConfigButtonClicked)
+ self.BrowseExportRunConfigButton.clicked.connect(self.onBrowseExportRunConfigButtonClicked)
self.BrowseExportUserConfigButton.clicked.connect(self.onBrowseExportUserConfigButtonClicked)
self.ExportConfigButton.clicked.connect(self.onExportConfigButtonClicked)
self.NewConfigButton.clicked.connect(self.onNewConfigButtonClicked)
@@ -159,8 +172,8 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
script_path = sys.executable
script_dir = QFileInfo(script_path).absoluteDir()
self.__default_config_paths = {
- "users": QDir.toNativeSeparators(script_dir.absoluteFilePath("users.json")),
- "system": QDir.toNativeSeparators(script_dir.absoluteFilePath("system.json"))
+ "user": QDir.toNativeSeparators(script_dir.absoluteFilePath("user.json")),
+ "run": QDir.toNativeSeparators(script_dir.absoluteFilePath("run.json"))
}
@@ -170,13 +183,13 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
config_data: dict
):
- if which == "system":
- self.setSystemConfigToWidget(config_data)
- self.CurrentSystemConfigEdit.setText(self.__config_paths["system"])
- elif which == "users":
+ if which == "run":
+ self.setRunConfigToWidget(config_data)
+ self.CurrentRunConfigEdit.setText(self.__config_paths["run"])
+ elif which == "user":
self.initilizeUserInfoWidget()
- self.fillUsersList(config_data)
- self.CurrentUserConfigEdit.setText(self.__config_paths["users"])
+ self.fillUserTree(config_data)
+ self.CurrentUserConfigEdit.setText(self.__config_paths["user"])
def initlizeConfig(
@@ -186,30 +199,30 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
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()
+ 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.saveSystemConfig(self.__config_paths[which], self.__config_data[which]):
- msg += f"系统配置文件已初始化, 文件路径: \n{self.__config_paths[which]}\n"
+ 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.loadSystemConfig(system_config_path)
+ self.__config_data[which] = self.loadRunConfig(run_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()
+ 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.saveUsersConfig(self.__config_paths[which], self.__config_data[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.loadUsersConfig(users_config_path)
+ self.__config_data[which] = self.loadUserConfig(user_config_path)
if self.__config_data[which] is None:
is_success = False
if msg:
@@ -226,7 +239,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
) -> bool:
is_success = True
- for which in ["system", "users"]:
+ for which in ["run", "user"]:
if not self.__config_paths[which]:
self.__config_paths[which] = self.__default_config_paths[which]
if not self.initlizeConfig(which):
@@ -236,7 +249,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
return is_success
- def defaultSystemConfig(
+ def defaultRunConfig(
self
) -> dict:
@@ -260,7 +273,27 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
}
- def defaultUsersConfig(
+ def defaultUserConfig(
+ self
+ ) -> dict:
+
+ return {
+ "groups": []
+ }
+
+
+ def defaultGroup(
+ self
+ ) -> dict:
+
+ return {
+ "name": "默认分组",
+ "enabled": True,
+ "users": []
+ }
+
+
+ def defaultUsers(
self
) -> dict:
@@ -269,17 +302,17 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
}
- def collectSystemConfigFromWidget(
+ def collectRunConfigFromWidget(
self
) -> dict:
- system_config = self.defaultSystemConfig()
+ run_config = self.defaultRunConfig()
# library config is never changed
- system_config["login"]["auto_captcha"] = self.AutoCaptchaCheckBox.isChecked()
- system_config["login"]["max_attempt"] = self.LoginAttemptSpinBox.value()
- system_config["web_driver"]["driver_type"] = self.BrowserTypeComboBox.currentText()
- system_config["web_driver"]["driver_path"] = self.BrowseBrowserDriverEdit.text()
- system_config["web_driver"]["headless"] = self.HeadlessCheckBox.isChecked()
+ 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
@@ -287,24 +320,24 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
run_mode |= 0x02
if self.AutoRenewalCheckBox.isChecked():
run_mode |= 0x04
- system_config["mode"]["run_mode"] = run_mode
- return system_config
+ run_config["mode"]["run_mode"] = run_mode
+ return run_config
- def setSystemConfigToWidget(
+ def setRunConfigToWidget(
self,
- system_config: dict
+ run_config: dict
):
- self.HostUrlEdit.setText(system_config["library"]["host_url"])
- self.LoginUrlEdit.setText(system_config["library"]["login_url"])
- self.AutoCaptchaCheckBox.setChecked(system_config["login"]["auto_captcha"])
- self.LoginAttemptSpinBox.setValue(system_config["login"]["max_attempt"])
- self.BrowserTypeComboBox.setCurrentText(system_config["web_driver"]["driver_type"])
- driver_path = os.path.abspath(system_config["web_driver"]["driver_path"])
+ 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"])
+ driver_path = os.path.abspath(run_config["web_driver"]["driver_path"])
self.BrowseBrowserDriverEdit.setText(QDir.toNativeSeparators(driver_path))
- self.HeadlessCheckBox.setChecked(system_config["web_driver"]["headless"])
- run_mode = system_config["mode"]["run_mode"]
+ 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)
@@ -316,7 +349,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
self.UsernameEdit.setText("")
self.PasswordEdit.setText("")
- self.UserListWidget.setSortingEnabled(True)
self.PasswordEdit.setEchoMode(QLineEdit.EchoMode.Password)
self.ShowPasswordCheckBox.setChecked(False)
self.FloorComboBox.setCurrentIndex(0)
@@ -336,199 +368,217 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
self.PreferLateRenewTimeCheckBox.setChecked(False)
- def collectUserConfigFromUserInfoWidget(
+ def collectUserFromUserInfoWidget(
self
) -> dict:
- user_config = {
+ user = {
"username": self.UsernameEdit.text(),
"password": self.PasswordEdit.text(),
+ "enabled": True,
"reserve_info": {
"begin_time":{},
"end_time": {},
"renew_time": {}
}
}
- user_config["reserve_info"]["date"] = self.DateEdit.dateTime().toString("yyyy-MM-dd")
- user_config["reserve_info"]["place"] = self.PlaceComboBox.currentText()
- user_config["reserve_info"]["floor"] = self.__floor_rmap[self.FloorComboBox.currentText()]
- user_config["reserve_info"]["room"] = self.__room_rmap[self.RoomComboBox.currentText()]
- user_config["reserve_info"]["seat_id"] = self.SeatIDEdit.text()
- user_config["reserve_info"]["begin_time"]["time"] = self.BeginTimeEdit.time().toString("HH:mm")
- user_config["reserve_info"]["begin_time"]["max_diff"] = self.MaxBeginTimeDiffSpinBox.value()
- user_config["reserve_info"]["begin_time"]["prefer_early"] = self.PreferEarlyBeginTimeCheckBox.isChecked()
- user_config["reserve_info"]["end_time"]["time"] = self.EndTimeEdit.time().toString("HH:mm")
- user_config["reserve_info"]["end_time"]["max_diff"] = self.MaxEndTimeDiffSpinBox.value()
- user_config["reserve_info"]["end_time"]["prefer_early"] = not self.PreferLateEndTimeCheckBox.isChecked()
- user_config["reserve_info"]["expect_duration"] = self.ExpectDurationSpinBox.value()
- user_config["reserve_info"]["satisfy_duration"] = self.SatisfyDurationCheckBox.isChecked()
- user_config["reserve_info"]["renew_time"]["expect_duration"] = self.ExpectRenewDurationSpinBox.value()
- user_config["reserve_info"]["renew_time"]["max_diff"] = self.MaxRenewTimeDiffSpinBox.value()
- user_config["reserve_info"]["renew_time"]["prefer_early"] = not self.PreferLateRenewTimeCheckBox.isChecked()
- return user_config
+ 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 collectUserConfigFromUserListWidget(
- self,
- index: int
+ def collectUserConfigFromUserTreeWidget(
+ self
) -> dict:
- user_config = self.defaultUsersConfig()
- if index < 0 or index >= self.UserListWidget.count():
- return user_config
- user_item = self.UserListWidget.item(index)
- if user_item:
- user_config = user_item.data(Qt.UserRole)
+ 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 setUserConfigToWidget(
+ def setUserToWidget(
self,
- user_config: dict
+ user: dict
) -> None:
try:
- self.UsernameEdit.setText(user_config["username"])
- self.PasswordEdit.setText(user_config["password"])
- self.DateEdit.setDate(QDate.fromString(user_config["reserve_info"]["date"], "yyyy-MM-dd"))
- self.PlaceComboBox.setCurrentText(user_config["reserve_info"]["place"])
- self.FloorComboBox.setCurrentText(self.__floor_map[user_config["reserve_info"]["floor"]])
- self.RoomComboBox.setCurrentText(self.__room_map[user_config["reserve_info"]["room"]])
- self.SeatIDEdit.setText(user_config["reserve_info"]["seat_id"])
- self.BeginTimeEdit.setTime(QTime.fromString(user_config["reserve_info"]["begin_time"]["time"], "H:mm"))
- self.MaxBeginTimeDiffSpinBox.setValue(user_config["reserve_info"]["begin_time"]["max_diff"])
- self.PreferEarlyBeginTimeCheckBox.setChecked(user_config["reserve_info"]["begin_time"]["prefer_early"])
- self.EndTimeEdit.setTime(QTime.fromString(user_config["reserve_info"]["end_time"]["time"], "H:mm"))
- self.MaxEndTimeDiffSpinBox.setValue(user_config["reserve_info"]["end_time"]["max_diff"])
- self.PreferLateEndTimeCheckBox.setChecked(not user_config["reserve_info"]["end_time"]["prefer_early"])
- self.ExpectDurationSpinBox.setValue(user_config["reserve_info"]["expect_duration"])
- self.SatisfyDurationCheckBox.setChecked(user_config["reserve_info"]["satisfy_duration"])
- self.ExpectRenewDurationSpinBox.setValue(user_config["reserve_info"]["renew_time"]["expect_duration"])
- self.MaxRenewTimeDiffSpinBox.setValue(user_config["reserve_info"]["renew_time"]["max_diff"])
- self.PreferLateRenewTimeCheckBox.setChecked(not user_config["reserve_info"]["renew_time"]["prefer_early"])
+ 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:
QMessageBox.warning(
self,
"警告 - AutoLibrary",
"用户配置文件读取发生错误 !\n"\
- f"用户: {user_config['username']} 配置文件可能已损坏"
+ f"用户: {user['username']} 配置文件可能已损坏"
)
- def loadSystemConfig(
+ def loadRunConfig(
self,
- system_config_path: str
+ run_config_path: str
) -> dict:
try:
- if not system_config_path or not os.path.exists(system_config_path):
+ if not run_config_path or not os.path.exists(run_config_path):
raise Exception("文件路径不存在")
- system_config = ConfigReader(system_config_path).getConfigs()
- if system_config and "library" in system_config\
- and "web_driver" in system_config\
- and "login" in system_config:
- return system_config
+ run_config = ConfigReader(run_config_path).getConfigs()
+ if run_config and "library" in run_config\
+ and "web_driver" in run_config\
+ and "login" in run_config:
+ return run_config
return None
except Exception as e:
QMessageBox.warning(
self,
"警告 - AutoLibrary",
- f"系统配置文件读取发生错误 ! : {e}\n"\
- f"文件路径: {system_config_path}"
+ f"运行配置文件读取发生错误 ! : {e}\n"\
+ f"文件路径: {run_config_path}"
)
return None
- def saveSystemConfig(
+ def saveRunConfig(
self,
- system_config_path: str,
- system_config_data: dict
+ run_config_path: str,
+ run_config_data: dict
) -> bool:
try:
- if not system_config_path:
+ if not run_config_path:
raise Exception("文件路径为空")
- if not system_config_data or not isinstance(system_config_data, dict):
- raise Exception("系统配置数据为空或类型错误")
- ConfigWriter(system_config_path, system_config_data)
+ if not run_config_data or not isinstance(run_config_data, dict):
+ raise Exception("运行配置数据为空或类型错误")
+ ConfigWriter(run_config_path, run_config_data)
return True
except Exception as e:
QMessageBox.warning(
self,
"警告 - AutoLibrary",
f"配置文件写入发生错误 ! : {e}\n"\
- f"文件路径: {system_config_path}"
+ f"文件路径: {run_config_path}"
)
return False
- def loadUsersConfig(
+ def loadUserConfig(
self,
- users_config_path: str
+ user_config_path: str
) -> dict:
try:
- if not users_config_path or not os.path.exists(users_config_path):
+ if not user_config_path or not os.path.exists(user_config_path):
raise Exception("文件路径不存在")
- users_config = ConfigReader(users_config_path).getConfigs()
- if users_config and "users" in users_config:
- return users_config
+ user_config = ConfigReader(user_config_path).getConfigs()
+ if user_config and "groups" in user_config:
+ return user_config
+ # compatibility with old version config format
+ if 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
return None
except Exception as e:
QMessageBox.warning(
self,
"警告 - AutoLibrary",
f"用户配置文件读取发生错误 ! : {e}\n"\
- f"文件路径: {users_config_path}"
+ f"文件路径: {user_config_path}"
)
return None
- def saveUsersConfig(
+ def saveUserConfig(
self,
- users_config_path: str,
- users_config_data: dict
+ user_config_path: str,
+ user_config_data: dict
) -> bool:
try:
- if not users_config_path:
+ if not user_config_path:
raise Exception("文件路径为空")
- if not users_config_data or not isinstance(users_config_data, dict):
+ if not user_config_data or not isinstance(user_config_data, dict):
raise Exception("用户配置数据为空或类型错误")
- ConfigWriter(users_config_path, users_config_data)
+ ConfigWriter(user_config_path, user_config_data)
return True
except Exception as e:
QMessageBox.warning(
self,
"警告 - AutoLibrary",
f"用户配置文件写入发生错误 ! : {e}\n"\
- f"文件路径: \n{users_config_path}"
+ f"文件路径: \n{user_config_path}"
)
return False
def saveConfigs(
self,
- system_config_path: str,
- users_config_path: str
+ run_config_path: str,
+ user_config_path: str
) -> bool:
- if users_config_path:
- self.__config_data["users"] = self.defaultUsersConfig()
- for index in range(self.UserListWidget.count()):
- user_config = self.collectUserConfigFromUserListWidget(index)
- if user_config:
- self.__config_data["users"]["users"].append(user_config)
- if not self.saveUsersConfig(
- users_config_path,
- self.__config_data["users"]
+ if user_config_path:
+ self.__config_data["user"] = self.collectUserConfigFromUserTreeWidget()
+ if not self.saveUserConfig(
+ user_config_path,
+ self.__config_data["user"]
):
return False
- if system_config_path:
- self.__config_data["system"] = self.collectSystemConfigFromWidget()
- if not self.saveSystemConfig(
- system_config_path,
- self.__config_data["system"]
+ 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
@@ -549,40 +599,78 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
if not config_path:
return False
try:
- system_config = self.loadSystemConfig(config_path)
- users_config = self.loadUsersConfig(config_path)
- if system_config is not None:
- self.__config_data["system"].update(system_config)
- self.setSystemConfigToWidget(self.__config_data["system"])
+ 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 users_config is not None:
- self.__config_data["users"].update(users_config)
- self.fillUsersList(self.__config_data["users"])
+ if user_config is not None:
+ self.__config_data["user"].update(user_config)
+ self.fillUserTree(self.__config_data["user"])
return True
except:
return False
- def fillUsersList(
+ def fillUserTree(
self,
- users_config_data: list[dict]
+ user_config_data: dict
):
- self.UserListWidget.clear()
- if "users" in users_config_data:
- for user in users_config_data["users"]:
- user_item = QListWidgetItem(user["username"])
- user_item.setData(Qt.UserRole, user)
- self.UserListWidget.addItem(user_item)
+ self.UserTreeWidget.clear()
+ self.UserTreeWidget.itemChanged.disconnect(self.onUserTreeWidgetItemChanged)
+ try:
+ if "groups" in user_config_data:
+ for group_config in user_config_data["groups"]:
+ group_item = QTreeWidgetItem(self.UserTreeWidget, TreeItemType.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, TreeItemType.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)
+ finally:
+ self.UserTreeWidget.itemChanged.connect(self.onUserTreeWidgetItemChanged)
+
+
+ def addGroup(
+ self,
+ group_name: str = ""
+ ) -> QTreeWidgetItem:
+
+ self.UserTreeWidget.itemChanged.disconnect(self.onUserTreeWidgetItemChanged)
+ group_item = QTreeWidgetItem(self.UserTreeWidget, TreeItemType.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 addUser(
- self
- ):
+ 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() == TreeItemType.USER.value:
+ group_item = group_item.parent()
new_user = {
- "username": f"新用户-{self.UserListWidget.count()}",
+ "username": f"新用户-{group_item.childCount()}",
"password": "000000",
+ "enabled": True,
"reserve_info": {
"date": f"{QDate.currentDate().toString("yyyy-MM-dd")}",
"place": "\u56fe\u4e66\u9986",
@@ -608,25 +696,76 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
}
}
}
- user_item = QListWidgetItem(new_user["username"])
- user_item.setData(Qt.UserRole, new_user)
- self.UserListWidget.addItem(user_item)
- self.UserListWidget.setCurrentItem(user_item)
- self.setUserConfigToWidget(new_user)
+ self.UserTreeWidget.itemChanged.disconnect(self.onUserTreeWidgetItemChanged)
+ user_item = QTreeWidgetItem(group_item, TreeItemType.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
+ self,
+ user_item: QTreeWidgetItem = None
):
- current_item = self.UserListWidget.currentItem()
- if current_item:
- current_index = self.UserListWidget.row(current_item)
- self.UserListWidget.takeItem(current_index)
- if current_index < self.UserListWidget.count():
- self.UserListWidget.setCurrentRow(current_index)
- else:
- self.UserListWidget.setCurrentItem(None)
+ if user_item is None:
+ return
+ if user_item.type() != TreeItemType.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 delGroup(
+ self,
+ group_item: QTreeWidgetItem = None
+ ):
+
+ if group_item is None:
+ return
+ if group_item.type() != TreeItemType.GROUP.value:
+ return
+ index = self.UserTreeWidget.indexOfTopLevelItem(group_item)
+ self.UserTreeWidget.takeTopLevelItem(index)
+
+
+ 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() == TreeItemType.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(
@@ -685,40 +824,129 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
self.__seat_map_widget.selectSeats(self.SeatIDEdit.text().split(","))
@Slot()
- def onUserListWidgetCurrentItemChanged(
+ def onUserTreeWidgetCurrentItemChanged(
self,
- current: QListWidgetItem,
- previous: QListWidgetItem
+ current: QTreeWidgetItem,
+ previous: QTreeWidgetItem
):
- # dont care about the 'self.__users_config_data', we already
+ # 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 not current:
+ if previous and previous.type() == TreeItemType.USER.value:
+ user = self.collectUserFromUserInfoWidget()
+ 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.initilizeUserInfoWidget()
return
- if previous:
- user = self.collectUserConfigFromUserInfoWidget()
+ if current.type() == TreeItemType.USER.value:
+ user = current.data(0, Qt.UserRole)
if user:
- previous.setText(user["username"])
- previous.setData(Qt.UserRole, user)
- user = current.data(Qt.UserRole)
- if user:
- self.setUserConfigToWidget(user)
+ self.setUserToWidget(user)
+ self.UsernameEdit.textEdited.connect(lambda text: current.setText(0, text))
+ else:
+ self.initilizeUserInfoWidget()
+
+ @Slot()
+ def onUserTreeWidgetItemChanged(
+ self,
+ item: QTreeWidgetItem,
+ column: int
+ ):
+
+ if item is None:
+ return
+ if column != 1:
+ return
+ if item.type() == TreeItemType.GROUP.value:
+ is_checked = item.checkState(1) == Qt.CheckState.Checked
+ for i in range(item.childCount()):
+ child = item.child(i)
+ 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() == TreeItemType.GROUP.value:
+ self.showGroupMenu(menu, current_item)
+ else:
+ self.showUserMenu(menu, current_item)
+ menu.exec_(self.UserTreeWidget.mapToGlobal(pos))
@Slot()
def onAddUserButtonClicked(
self
):
- self.addUser()
+ current_item = self.UserTreeWidget.currentItem()
+ self.addUser(current_item)
@Slot()
def onDelUserButtonClicked(
self
):
- self.delUser()
+ current_item = self.UserTreeWidget.currentItem()
+ self.delUser(current_item)
@Slot()
def onBrowseBrowserDriverButtonClicked(
@@ -735,66 +963,66 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
self.BrowseBrowserDriverEdit.setText(QDir.toNativeSeparators(browser_driver_path))
@Slot()
- def onBrowseCurrentSystemConfigButtonClicked(
+ def onBrowseCurrentRunConfigButtonClicked(
self
):
- system_config_path = QFileDialog.getOpenFileName(
+ run_config_path = QFileDialog.getOpenFileName(
self,
- "选择其它的系统配置 - AutoLibrary",
- self.CurrentSystemConfigEdit.text(),
+ "选择其它的运行配置 - AutoLibrary",
+ self.CurrentRunConfigEdit.text(),
"JSON 文件 (*.json);;所有文件 (*)"
)[0]
- if system_config_path:
- system_config_path = QDir.toNativeSeparators(system_config_path)
- if self.loadConfig(system_config_path):
- self.__config_paths["system"] = system_config_path
- self.CurrentSystemConfigEdit.setText(system_config_path)
+ if run_config_path:
+ run_config_path = QDir.toNativeSeparators(run_config_path)
+ if self.loadConfig(run_config_path):
+ self.__config_paths["run"] = run_config_path
+ self.CurrentRunConfigEdit.setText(run_config_path)
@Slot()
def onBrowseCurrentUserConfigButtonClicked(
self
):
- users_config_path = QFileDialog.getOpenFileName(
+ user_config_path = QFileDialog.getOpenFileName(
self,
"选择其它的用户配置 - AutoLibrary",
self.CurrentUserConfigEdit.text(),
"JSON 文件 (*.json);;所有文件 (*)"
)[0]
- if users_config_path:
- users_config_path = QDir.toNativeSeparators(users_config_path)
- if self.loadConfig(users_config_path):
- self.__config_paths["users"] = users_config_path
- self.CurrentUserConfigEdit.setText(users_config_path)
+ if user_config_path:
+ user_config_path = QDir.toNativeSeparators(user_config_path)
+ if self.loadConfig(user_config_path):
+ self.__config_paths["user"] = user_config_path
+ self.CurrentUserConfigEdit.setText(user_config_path)
@Slot()
- def onBrowseExportSystemConfigButtonClicked(
+ def onBrowseExportRunConfigButtonClicked(
self
):
- system_config_path = QFileDialog.getSaveFileName(
+ run_config_path = QFileDialog.getSaveFileName(
self,
- "导出系统配置 - AutoLibrary",
- self.CurrentSystemConfigEdit.text(),
+ "导出运行配置 - AutoLibrary",
+ self.CurrentRunConfigEdit.text(),
"JSON 文件 (*.json);;所有文件 (*)"
)[0]
- if system_config_path:
- self.ExportSystemConfigEdit.setText(QDir.toNativeSeparators(system_config_path))
+ if run_config_path:
+ self.ExportRunConfigEdit.setText(QDir.toNativeSeparators(run_config_path))
@Slot()
def onBrowseExportUserConfigButtonClicked(
self
):
- users_config_path = QFileDialog.getSaveFileName(
+ user_config_path = QFileDialog.getSaveFileName(
self,
"导出用户配置 - AutoLibrary",
self.CurrentUserConfigEdit.text(),
"JSON 文件 (*.json);;所有文件 (*)"
)[0]
- if users_config_path:
- self.ExportUserConfigEdit.setText(QDir.toNativeSeparators(users_config_path))
+ if user_config_path:
+ self.ExportUserConfigEdit.setText(QDir.toNativeSeparators(user_config_path))
@Slot()
def onExportConfigButtonClicked(
@@ -803,22 +1031,22 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
msg = ""
- system_config_path = self.ExportSystemConfigEdit.text()
- users_config_path = self.ExportUserConfigEdit.text()
- if system_config_path:
+ run_config_path = self.ExportRunConfigEdit.text()
+ user_config_path = self.ExportUserConfigEdit.text()
+ if run_config_path:
if self.saveConfigs(
- system_config_path, ""
+ run_config_path, ""
):
- msg += f"系统配置文件已导出到: \n'{system_config_path}'\n"
+ msg += f"运行配置文件已导出到: \n'{run_config_path}'\n"
else:
- msg += f"系统配置文件导出失败: \n'{system_config_path}'\n"
- if users_config_path:
+ msg += f"运行配置文件导出失败: \n'{run_config_path}'\n"
+ if user_config_path:
if self.saveConfigs(
- "", users_config_path
+ "", user_config_path
):
- msg += f"用户配置文件已导出到: \n'{users_config_path}'\n"
+ msg += f"用户配置文件已导出到: \n'{user_config_path}'\n"
else:
- msg += f"用户配置文件导出失败: \n'{users_config_path}'\n"
+ msg += f"用户配置文件导出失败: \n'{user_config_path}'\n"
if msg:
QMessageBox.information(
self,
@@ -838,7 +1066,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
self
):
- file_path = self.CurrentSystemConfigEdit.text()
+ file_path = self.CurrentRunConfigEdit.text()
folder_dir = QFileDialog.getExistingDirectory(
self,
"选择新建配置的文件夹 - AutoLibrary",
@@ -846,16 +1074,16 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
)
if not folder_dir:
return
- system_config_path = QDir.toNativeSeparators(os.path.join(folder_dir, "system.json"))
- users_config_path = QDir.toNativeSeparators(os.path.join(folder_dir, "users.json"))
- system_exists = os.path.isfile(system_config_path)
- users_exists = os.path.isfile(users_config_path)
- if system_exists or users_exists:
+ 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 system_exists:
- exist_files.append(system_config_path)
- if users_exists:
- exist_files.append(users_config_path)
+ if run_exists:
+ exist_files.append(run_config_path)
+ if user_exists:
+ exist_files.append(user_config_path)
reply = QMessageBox.information(
self,
"提示 - AutoLibrary",
@@ -865,34 +1093,33 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
)
if reply == QMessageBox.No:
return
- self.__config_data["system"] = self.defaultSystemConfig()
- self.__config_data["users"] = self.defaultUsersConfig()
+ self.__config_data["run"] = self.defaultRunConfig()
+ self.__config_data["user"] = self.defaultUserConfig()
self.__config_paths = {
- "system": system_config_path,
- "users": users_config_path
+ "run": run_config_path,
+ "user": user_config_path
}
- self.initlizeConfigToWidget("system", self.__config_data["system"])
- self.initlizeConfigToWidget("users", self.__config_data["users"])
+ self.initlizeConfigToWidget("run", self.__config_data["run"])
+ self.initlizeConfigToWidget("user", self.__config_data["user"])
@Slot()
def onConfirmButtonClicked(
self
):
- if self.UserListWidget.currentItem() is not None:
- user_config = self.collectUserConfigFromUserInfoWidget()
- if user_config:
- self.UserListWidget.currentItem().setData(Qt.UserRole, user_config)
+ current_item = self.UserTreeWidget.currentItem()
+ if current_item and current_item.type() == TreeItemType.USER.value:
+ self.UserTreeWidget.setCurrentItem(None)
if self.saveConfigs(
- self.__config_paths["system"],
- self.__config_paths["users"]
+ self.__config_paths["run"],
+ self.__config_paths["user"]
):
QMessageBox.information(
self,
"提示 - AutoLibrary",
"配置文件保存成功 !\n"
- f"系统配置文件路径: \n{self.__config_paths['system']}\n"\
- f"用户配置文件路径: \n{self.__config_paths['users']}"
+ f"运行配置文件路径: \n{self.__config_paths['run']}\n"\
+ f"用户配置文件路径: \n{self.__config_paths['user']}"
)
else:
QMessageBox.warning(
@@ -907,4 +1134,4 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
self
):
- self.close()
+ self.close()
\ No newline at end of file
diff --git a/src/gui/ALConfigWidget.ui b/src/gui/ALConfigWidget.ui
index e507a52..9566ac1 100644
--- a/src/gui/ALConfigWidget.ui
+++ b/src/gui/ALConfigWidget.ui
@@ -93,11 +93,26 @@
用户列表
+
+ 0
+
+
+ 5
+
+
+ 5
+
+
+ 5
+
+
+ 5
+
-
-
+
- 100
+ 230
0
@@ -107,18 +122,58 @@
16777215
-
- false
+
+ QAbstractScrollArea::SizeAdjustPolicy::AdjustIgnored
-
- QListView::ViewMode::ListMode
+
+ true
-
- -1
+
+ true
+
+
+ QAbstractItemView::DragDropMode::DragDrop
+
+
+ Qt::DropAction::MoveAction
+
+
+ true
false
+
+ true
+
+
+ false
+
+
+ false
+
+
+ 2
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+
+ 分组/用户
+
+
+
+
+ 状态
+
+
-
@@ -236,7 +291,7 @@
- 100
+ 80
25
@@ -325,7 +380,7 @@
- 100
+ 80
25
@@ -416,7 +471,7 @@
- 100
+ 80
25
@@ -435,7 +490,7 @@
- 100
+ 80
25
@@ -454,7 +509,7 @@
- 100
+ 80
25
@@ -473,7 +528,7 @@
- 100
+ 80
30
@@ -556,7 +611,7 @@
- 100
+ 80
25
@@ -575,7 +630,7 @@
- 100
+ 80
25
@@ -594,7 +649,7 @@
- 100
+ 80
25
@@ -738,7 +793,7 @@
- H:mm
+ HH:mm
@@ -746,7 +801,7 @@
- 100
+ 80
25
@@ -869,6 +924,9 @@
QAbstractSpinBox::StepType::AdaptiveDecimalStepType
+
+ 0
+
-
@@ -902,7 +960,7 @@
- 100
+ 80
25
@@ -921,7 +979,7 @@
- 100
+ 80
25
@@ -965,7 +1023,7 @@
- H:mm
+ HH:mm
@@ -973,7 +1031,7 @@
- 100
+ 80
25
@@ -1024,7 +1082,7 @@
- 100
+ 80
25
@@ -1047,9 +1105,9 @@
-
+
- 系统设置
+ 运行设置
@@ -1579,7 +1637,7 @@
-
-
+
0
@@ -1620,7 +1678,7 @@
-
-
+
0
@@ -1634,7 +1692,7 @@
- 当前系统配置路径:
+ 当前运行配置路径:
@@ -1658,7 +1716,7 @@
-
-
+
35
@@ -1721,7 +1779,7 @@
-
-
+
0
@@ -1737,7 +1795,7 @@
-
-
+
0
@@ -1751,7 +1809,7 @@
- 系统配置导出路径:
+ 运行配置导出路径:
@@ -1794,7 +1852,7 @@
-
-
+
35
@@ -1836,12 +1894,12 @@
ExportUserConfigEdit
- ExportSystemConfigLabel
+ ExportRunConfigLabel
BrowseExportUserConfigButton
ExportUserConfigLabel
- BrowseExportSystemConfigButton
+ BrowseExportRunConfigButton
ExportConfigButton
- ExportSystemConfigEdit
+ ExportRunConfigEdit
-
diff --git a/src/gui/ALMainWindow.py b/src/gui/ALMainWindow.py
index 05822ac..20ba216 100644
--- a/src/gui/ALMainWindow.py
+++ b/src/gui/ALMainWindow.py
@@ -53,9 +53,9 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
script_path = sys.executable
script_dir = QFileInfo(script_path).absoluteDir()
self.__config_paths = {
- "system": QDir.toNativeSeparators(script_dir.absoluteFilePath("system.json")),
- "users": QDir.toNativeSeparators(script_dir.absoluteFilePath("users.json")),
- "timer_tasks": QDir.toNativeSeparators(script_dir.absoluteFilePath("timer_tasks.json")),
+ "run": QDir.toNativeSeparators(script_dir.absoluteFilePath("run.json")),
+ "user": QDir.toNativeSeparators(script_dir.absoluteFilePath("user.json")),
+ "timer_task": QDir.toNativeSeparators(script_dir.absoluteFilePath("timer_task.json")),
}
self.__alTimerTaskWidget = None
self.__alConfigWidget = None
@@ -81,7 +81,7 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
self.AboutAction.triggered.connect(self.onAboutActionTriggered)
# initialize timer task widget, but not show it
- self.__alTimerTaskWidget = ALTimerTaskWidget(self, self.__config_paths["timer_tasks"])
+ self.__alTimerTaskWidget = ALTimerTaskWidget(self, self.__config_paths["timer_task"])
self.timerTaskIsRunning.connect(self.__alTimerTaskWidget.onTimerTaskIsRunning)
self.timerTaskIsExecuted.connect(self.__alTimerTaskWidget.onTimerTaskIsExecuted)
self.__alTimerTaskWidget.timerTaskIsReady.connect(self.onTimerTaskIsReady)
diff --git a/src/gui/ALMainWorkers.py b/src/gui/ALMainWorkers.py
index 9b1af7f..c591173 100644
--- a/src/gui/ALMainWorkers.py
+++ b/src/gui/ALMainWorkers.py
@@ -63,6 +63,35 @@ class AutoLibWorker(QThread):
return True
+ def loadConfigs(
+ self
+ ) -> bool:
+
+ self.showTraceSignal.emit(
+ f"正在加载配置文件, 运行配置文件路径: {self.__config_paths["run"]}"
+ )
+ self.__run_config = ConfigReader(
+ self.__config_paths["run"]
+ ).getConfigs()
+ self.showTraceSignal.emit(
+ f"正在加载配置文件, 用户配置文件路径: {self.__config_paths["user"]}"
+ )
+ self.__user_config = ConfigReader(
+ self.__config_paths["user"]
+ ).getConfigs()
+ if self.__run_config is None or self.__user_config is None:
+ self.showTraceSignal.emit(
+ "配置文件加载失败, 请检查配置文件是否正确。"
+ )
+ return False
+ if not self.__user_config.get("groups"):
+ self.showTraceSignal.emit(
+ "用户配置文件中无有效任务组, 请检查用户配置文件是否正确"
+ )
+ return False
+ return True
+
+
def run(
self
):
@@ -71,21 +100,39 @@ class AutoLibWorker(QThread):
try:
if not self.checkTimeAvailable():
self.showTraceSignal.emit(
- "当前时间不在图书馆开放时间内。\n"\
+ "当前时间不在图书馆开放时间内\n"\
" 请在 07:30 - 23:30 之间尝试"
)
return
if not self.checkConfigPaths():
return
self.showTraceSignal.emit("AutoLibrary 开始运行")
+ if not self.loadConfigs():
+ return
auto_lib = AutoLib(
self.__input_queue,
self.__output_queue,
+ self.__run_config
)
- auto_lib.run(
- ConfigReader(self.__config_paths["system"]),
- ConfigReader(self.__config_paths["users"]),
- )
+ if auto_lib is None:
+ self.showTraceSignal.emit(
+ "AutoLibrary 初始化失败"
+ )
+ return
+ groups = self.__user_config.get("groups")
+ for group in groups:
+ time.sleep(0.2) # wait for the message queue to be empty
+ if not group["enabled"]:
+ self.showTraceSignal.emit(
+ f"任务组 {group["name"]} 已跳过"
+ )
+ continue
+ self.showTraceSignal.emit(
+ f"正在运行任务组 {group["name"]}"
+ )
+ auto_lib.run(
+ { "users": group.get("users", []) }
+ )
except Exception as e:
self.showTraceSignal.emit(
f"AutoLibrary 运行时发生异常 : {e}"
@@ -93,6 +140,7 @@ class AutoLibWorker(QThread):
finally:
if auto_lib:
auto_lib.close()
+ time.sleep(0.2) # wait for the message queue to be empty
self.showTraceSignal.emit("AutoLibrary 运行结束")
self.finishedSignal.emit()
@@ -109,14 +157,9 @@ class TimerTaskWorker(AutoLibWorker):
config_paths: dict
):
- super().__init__(
- input_queue,
- output_queue,
- config_paths,
- )
+ super().__init__(input_queue, output_queue, config_paths)
self.__timer_task = timer_task
- self.__stopped = False
def run(
self
diff --git a/src/gui/ALUserTreeWidget.py b/src/gui/ALUserTreeWidget.py
new file mode 100644
index 0000000..b07ed64
--- /dev/null
+++ b/src/gui/ALUserTreeWidget.py
@@ -0,0 +1,149 @@
+# -*- coding: utf-8 -*-
+"""
+Copyright (c) 2025 KenanZhu.
+All rights reserved.
+
+This software is provided "as is", without any warranty of any kind.
+You may use, modify, and distribute this file under the terms of the MIT License.
+See the LICENSE file for details.
+"""
+
+from enum import Enum
+
+from PySide6.QtCore import (
+ Qt, QSize, QCoreApplication, QRect, QPoint
+)
+from PySide6.QtWidgets import (
+ QAbstractScrollArea, QAbstractItemView,
+ QTreeWidget, QTreeWidgetItem
+)
+from PySide6.QtGui import (
+ QDragEnterEvent, QDragMoveEvent, QDropEvent
+)
+
+
+class TreeItemType(Enum):
+
+ GROUP = 0
+ USER = 1
+
+
+class ALUserTreeWidget(QTreeWidget):
+
+ def __init__(
+ self,
+ parent = None
+ ):
+
+ super().__init__(parent)
+
+ self.setupUi()
+ self.translateUi()
+
+
+ def setupUi(
+ self
+ ):
+
+ __qtreewidgetitem = QTreeWidgetItem()
+ __qtreewidgetitem.setText(0, u"\u5206\u7ec4/\u7528\u6237");
+ self.setHeaderItem(__qtreewidgetitem)
+ self.setObjectName(u"UserTreeWidget")
+ self.setMinimumSize(QSize(230, 0))
+ self.setMaximumSize(QSize(250, 16777215))
+ self.setSizeAdjustPolicy(QAbstractScrollArea.SizeAdjustPolicy.AdjustIgnored)
+ self.setTabKeyNavigation(True)
+ self.setDragEnabled(True)
+ self.setAcceptDrops(True)
+ self.setDropIndicatorShown(True)
+ self.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)
+ self.setDefaultDropAction(Qt.DropAction.IgnoreAction)
+ self.setAlternatingRowColors(True)
+ self.setSortingEnabled(True)
+ self.setAnimated(True)
+ self.setAllColumnsShowFocus(False)
+ self.setHeaderHidden(False)
+ self.setColumnCount(2)
+ self.setColumnWidth(0, 150)
+ self.setColumnWidth(1, 20)
+ self.header().setCascadingSectionResizes(False)
+ self.header().setHighlightSections(False)
+ self.header().setProperty(u"showSortIndicator", True)
+
+
+ def translateUi(
+ self
+ ):
+
+ ___qtreewidgetitem = self.headerItem()
+ ___qtreewidgetitem.setText(1, QCoreApplication.translate("ALConfigWidget", u"\u72b6\u6001", None));
+
+
+ @staticmethod
+ def isDragPositionValid(
+ target_rect: QRect,
+ drag_pos: QPoint,
+ ) -> bool:
+
+ y_offset = drag_pos.y() - target_rect.top()
+ valid = (y_offset > target_rect.height()*0.2 and
+ y_offset < target_rect.height()*0.8)
+ return valid
+
+
+ def dragEnterEvent(
+ self,
+ event: QDragEnterEvent
+ ):
+
+ super().dragEnterEvent(event)
+
+
+ def dragMoveEvent(
+ self,
+ event: QDragMoveEvent
+ ):
+
+ super().dragMoveEvent(event)
+
+ source_item = self.currentItem()
+ target_item = self.itemAt(event.position().toPoint())
+ if source_item is None:
+ event.ignore()
+ return
+ if source_item.type() == TreeItemType.GROUP.value:
+ if target_item is not None:
+ event.ignore()
+ return
+ elif source_item.type() == TreeItemType.USER.value:
+ if target_item is None:
+ event.ignore()
+ return
+ if target_item.type() != TreeItemType.GROUP.value:
+ event.ignore()
+ return
+ if target_item.checkState(1) == Qt.CheckState.Unchecked:
+ event.ignore()
+ return
+ if not self.isDragPositionValid(
+ self.visualItemRect(target_item),
+ event.position().toPoint()
+ ):
+ event.ignore()
+ return
+ else:
+ event.ignore()
+ return
+ event.acceptProposedAction()
+
+
+ def dropEvent(
+ self,
+ event: QDropEvent
+ ):
+
+ super().dropEvent(event)
+
+ for item_index in range(self.topLevelItemCount()):
+ self.topLevelItem(item_index).setExpanded(True)
+ self.setCurrentItem(None)
diff --git a/src/operators/AutoLib.py b/src/operators/AutoLib.py
index 6df80bc..cb9841f 100644
--- a/src/operators/AutoLib.py
+++ b/src/operators/AutoLib.py
@@ -32,13 +32,20 @@ class AutoLib(MsgBase):
def __init__(
self,
input_queue: queue.Queue,
- output_queue: queue.Queue
+ output_queue: queue.Queue,
+ run_config: dict
):
super().__init__(input_queue, output_queue)
- self.__system_config_reader = None
- self.__users_config_reader = None
+ self.__run_config = run_config
+ self.__user_config = None
self.__driver = None
+ if not self.__initBrowserDriver():
+ return None
+ else:
+ if not self.__initDriverUrl():
+ return None
+ self.__initLibOperators()
def __initBrowserDriver(
@@ -48,7 +55,11 @@ class AutoLib(MsgBase):
self._showTrace("正在初始化浏览器驱动......")
edge_options = webdriver.EdgeOptions()
- if self.__system_config_reader.get("web_driver/headless"):
+ web_driver_config = self.__run_config.get("web_driver", None)
+ if not web_driver_config:
+ self._showTrace("未配置浏览器驱动参数 !")
+ return False
+ if web_driver_config.get("headless"):
edge_options.add_argument("--headless")
edge_options.add_argument("--disable-gpu")
edge_options.add_argument("--no-sandbox")
@@ -76,8 +87,8 @@ class AutoLib(MsgBase):
)
# init browser driver
- self.__driver_path = self.__system_config_reader.get("web_driver/driver_path")
- self.__driver_type = self.__system_config_reader.get("web_driver/driver_type")
+ self.__driver_path = web_driver_config.get("driver_path")
+ self.__driver_type = web_driver_config.get("driver_type")
self.__driver_path = os.path.abspath(self.__driver_path)
try:
service = None
@@ -149,8 +160,11 @@ class AutoLib(MsgBase):
self,
) -> bool:
- url = self.__system_config_reader.get("library/host_url")
- url += self.__system_config_reader.get("library/login_url")
+ lib_config = self.__run_config.get("library", None)
+ if not lib_config:
+ self._showError("未配置图书馆参数 !")
+ return False
+ url = lib_config.get("host_url") + lib_config.get("login_url")
self.__driver.get(url)
if not self.__waitResponseLoad():
return False
@@ -161,24 +175,26 @@ class AutoLib(MsgBase):
self,
username: str,
password: str,
+ login_config: dict,
+ run_mode_config: dict,
reserve_info: dict
) -> int:
- # result : 0 - success, 1 - failed, 2 - passed
+ # result : -1 - terminate, 0 - success, 1 - failed, 2 - passed
result = 2
# login
if not self.__lib_login.login(
username,
password,
- self.__system_config_reader.get("login/max_attempt", 5),
- self.__system_config_reader.get("login/auto_captcha", True),
+ login_config.get("max_attempt", 3),
+ login_config.get("auto_captcha", True),
):
return 1
"""
- Here, we collect the run mode from the config file.
+ Here, we collect the run mode from the run config.
"""
- run_mode = self.__system_config_reader.get("mode/run_mode", 0)
+ run_mode = run_mode_config.get("run_mode", 0)
run_mode = {
"auto_reserve": run_mode&0x1,
"auto_checkin": run_mode&0x2,
@@ -223,43 +239,43 @@ class AutoLib(MsgBase):
):
# if logout is failed, we must make sure the host to be reloaded
# otherwise, the next login may fail
- self.__driver.get(self.__system_config_reader.get("library/host_url"))
- return 1
+ if not self.__initDriverUrl():
+ return -1
return result
def run(
self,
- system_config_reader: ConfigReader,
- users_config_reader: ConfigReader
+ user_config: dict
):
- self.__system_config_reader = system_config_reader
- self.__users_config_reader = users_config_reader
- if not self.__initBrowserDriver():
- return
- else:
- if not self.__initDriverUrl():
- return
- self.__initLibOperators()
+ self.__user_config = user_config
user_counter = {"current": 0, "success": 0, "failed": 0, "passed": 0}
- users = self.__users_config_reader.get("users")
- self._showTrace(
- f"共发现 {len(users)} 个用户, "\
- f"用户配置文件路径: {self.__users_config_reader.configPath()}"
- )
+ users = self.__user_config["users"]
+ self._showTrace(f"共发现 {len(users)} 个用户")
for user in users:
user_counter["current"] += 1
self._showTrace(
- f"正在处理第 {user_counter["current"]}/{len(users)} 个用户: {user['username']}......"
+ f"正在处理第 {user_counter["current"]}/{len(users)} 个用户: {user["username"]}......"
)
+ if not user["enabled"]:
+ self._showTrace(f"用户 {user["username"]} 已跳过")
+ user_counter["passed"] += 1
+ continue
r = self.__run(
username=user["username"],
password=user["password"],
+ login_config=self.__run_config["login"],
+ run_mode_config=self.__run_config["mode"],
reserve_info=user["reserve_info"],
)
- if r == 0:
+ if r == -1:
+ self._showTrace(
+ f"用户 {user["username"]} 处理过程中页面发生异常,无法继续操作, 任务已终止 !"
+ )
+ break
+ elif r == 0:
user_counter["success"] += 1
elif r == 1:
user_counter["failed"] += 1