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

feat(ALConfigWidget): 大更新 - 用户树状列表和其它

1. 在这个 commit 中,我们思考了许久,最终决定将现有的
用户管理列表转为树状列表,以解决用户数量增多时,用户的
选择性管理,分组等问题。
2. 同时因为该更改需要重构很多内容,我们也在该 commit
中决定将所有‘系统配置’更换为‘运行配置’,同时文件名称和
内容变量也相应变为‘run’和‘user’。
3. 重构 AutoLib 和 ALMainWorkers 中的配置相关代码,
以适应新的用户树状列表。

当前迭代更新至 v1.0.0-beta.4, 同时,在该版本的 rc
阶段前,我们计划不再发布 beta 阶段相关的 release
This commit is contained in:
2025-12-13 00:07:33 +08:00
parent 7dcd72939b
commit 55ae4d0d96
6 changed files with 832 additions and 339 deletions
+482 -255
View File
File diff suppressed because it is too large Load Diff
+95 -37
View File
@@ -93,11 +93,26 @@
<string>用户列表</string>
</property>
<layout class="QVBoxLayout" name="UserListLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QListWidget" name="UserListWidget">
<widget class="QTreeWidget" name="UserTreeWidget">
<property name="minimumSize">
<size>
<width>100</width>
<width>230</width>
<height>0</height>
</size>
</property>
@@ -107,18 +122,58 @@
<height>16777215</height>
</size>
</property>
<property name="isWrapping" stdset="0">
<bool>false</bool>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::SizeAdjustPolicy::AdjustIgnored</enum>
</property>
<property name="viewMode">
<enum>QListView::ViewMode::ListMode</enum>
<property name="tabKeyNavigation">
<bool>true</bool>
</property>
<property name="currentRow">
<number>-1</number>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDropMode::DragDrop</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::DropAction::MoveAction</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="sortingEnabled">
<bool>false</bool>
</property>
<property name="animated">
<bool>true</bool>
</property>
<property name="allColumnsShowFocus">
<bool>false</bool>
</property>
<property name="headerHidden">
<bool>false</bool>
</property>
<property name="columnCount">
<number>2</number>
</property>
<attribute name="headerCascadingSectionResizes">
<bool>false</bool>
</attribute>
<attribute name="headerHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="headerShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string notr="true">分组/用户</string>
</property>
</column>
<column>
<property name="text">
<string>状态</string>
</property>
</column>
</widget>
</item>
<item>
@@ -236,7 +291,7 @@
<widget class="QLabel" name="PasswordLabel">
<property name="minimumSize">
<size>
<width>100</width>
<width>80</width>
<height>25</height>
</size>
</property>
@@ -325,7 +380,7 @@
<widget class="QLabel" name="UsernameKabel">
<property name="minimumSize">
<size>
<width>100</width>
<width>80</width>
<height>25</height>
</size>
</property>
@@ -416,7 +471,7 @@
<widget class="QLabel" name="RoomLabel">
<property name="minimumSize">
<size>
<width>100</width>
<width>80</width>
<height>25</height>
</size>
</property>
@@ -435,7 +490,7 @@
<widget class="QLabel" name="FloorLabel">
<property name="minimumSize">
<size>
<width>100</width>
<width>80</width>
<height>25</height>
</size>
</property>
@@ -454,7 +509,7 @@
<widget class="QLabel" name="ExpectRenewDurationLabel">
<property name="minimumSize">
<size>
<width>100</width>
<width>80</width>
<height>25</height>
</size>
</property>
@@ -473,7 +528,7 @@
<widget class="QLabel" name="EndTimeLabel">
<property name="minimumSize">
<size>
<width>100</width>
<width>80</width>
<height>30</height>
</size>
</property>
@@ -556,7 +611,7 @@
<widget class="QLabel" name="SeatIDLabel">
<property name="minimumSize">
<size>
<width>100</width>
<width>80</width>
<height>25</height>
</size>
</property>
@@ -575,7 +630,7 @@
<widget class="QLabel" name="ExpectDurationLabel">
<property name="minimumSize">
<size>
<width>100</width>
<width>80</width>
<height>25</height>
</size>
</property>
@@ -594,7 +649,7 @@
<widget class="QLabel" name="DateLabel">
<property name="minimumSize">
<size>
<width>100</width>
<width>80</width>
<height>25</height>
</size>
</property>
@@ -738,7 +793,7 @@
</time>
</property>
<property name="displayFormat">
<string>H:mm</string>
<string>HH:mm</string>
</property>
</widget>
</item>
@@ -746,7 +801,7 @@
<widget class="QLabel" name="PlaceLabel">
<property name="minimumSize">
<size>
<width>100</width>
<width>80</width>
<height>25</height>
</size>
</property>
@@ -869,6 +924,9 @@
<property name="stepType">
<enum>QAbstractSpinBox::StepType::AdaptiveDecimalStepType</enum>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
@@ -902,7 +960,7 @@
<widget class="QLabel" name="MaxBeginTimeDiffLabel">
<property name="minimumSize">
<size>
<width>100</width>
<width>80</width>
<height>25</height>
</size>
</property>
@@ -921,7 +979,7 @@
<widget class="QLabel" name="MaxEndTimeDiffLabel">
<property name="minimumSize">
<size>
<width>100</width>
<width>80</width>
<height>25</height>
</size>
</property>
@@ -965,7 +1023,7 @@
</time>
</property>
<property name="displayFormat">
<string>H:mm</string>
<string>HH:mm</string>
</property>
</widget>
</item>
@@ -973,7 +1031,7 @@
<widget class="QLabel" name="BeginTimeLabel">
<property name="minimumSize">
<size>
<width>100</width>
<width>80</width>
<height>25</height>
</size>
</property>
@@ -1024,7 +1082,7 @@
<widget class="QLabel" name="label">
<property name="minimumSize">
<size>
<width>100</width>
<width>80</width>
<height>25</height>
</size>
</property>
@@ -1047,9 +1105,9 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="SystemConfigWidget">
<widget class="QWidget" name="RunConfigWidget">
<attribute name="title">
<string>系统设置</string>
<string>运行设置</string>
</attribute>
<layout class="QGridLayout" name="SystemConfigWidgetLayout">
<property name="leftMargin">
@@ -1579,7 +1637,7 @@
</property>
<layout class="QGridLayout" name="CurrentConfigLayout">
<item row="1" column="0">
<widget class="QLineEdit" name="CurrentSystemConfigEdit">
<widget class="QLineEdit" name="CurrentRunConfigEdit">
<property name="minimumSize">
<size>
<width>0</width>
@@ -1620,7 +1678,7 @@
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="CurrentSystemConfigLabel">
<widget class="QLabel" name="CurrentRunConfigLabel">
<property name="minimumSize">
<size>
<width>0</width>
@@ -1634,7 +1692,7 @@
</size>
</property>
<property name="text">
<string>当前系统配置路径:</string>
<string>当前运行配置路径:</string>
</property>
</widget>
</item>
@@ -1658,7 +1716,7 @@
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="BrowseCurrentSystemConfigButton">
<widget class="QPushButton" name="BrowseCurrentRunConfigButton">
<property name="minimumSize">
<size>
<width>35</width>
@@ -1721,7 +1779,7 @@
</widget>
</item>
<item row="1" column="0">
<widget class="QLineEdit" name="ExportSystemConfigEdit">
<widget class="QLineEdit" name="ExportRunConfigEdit">
<property name="minimumSize">
<size>
<width>0</width>
@@ -1737,7 +1795,7 @@
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="ExportSystemConfigLabel">
<widget class="QLabel" name="ExportRunConfigLabel">
<property name="minimumSize">
<size>
<width>0</width>
@@ -1751,7 +1809,7 @@
</size>
</property>
<property name="text">
<string>系统配置导出路径:</string>
<string>运行配置导出路径:</string>
</property>
</widget>
</item>
@@ -1794,7 +1852,7 @@
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="BrowseExportSystemConfigButton">
<widget class="QPushButton" name="BrowseExportRunConfigButton">
<property name="minimumSize">
<size>
<width>35</width>
@@ -1836,12 +1894,12 @@
</item>
</layout>
<zorder>ExportUserConfigEdit</zorder>
<zorder>ExportSystemConfigLabel</zorder>
<zorder>ExportRunConfigLabel</zorder>
<zorder>BrowseExportUserConfigButton</zorder>
<zorder>ExportUserConfigLabel</zorder>
<zorder>BrowseExportSystemConfigButton</zorder>
<zorder>BrowseExportRunConfigButton</zorder>
<zorder>ExportConfigButton</zorder>
<zorder>ExportSystemConfigEdit</zorder>
<zorder>ExportRunConfigEdit</zorder>
</widget>
</item>
<item>
+4 -4
View File
@@ -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)
+54 -11
View File
@@ -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
+149
View File
@@ -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)