Skip to content

Commit a05ff52

Browse files
committed
设置班级名称
1 parent 727a9c9 commit a05ff52

File tree

4 files changed

+329
-21
lines changed

4 files changed

+329
-21
lines changed

app/Language/modules/import_student_name.py renamed to app/Language/modules/roll_call_list.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
# 班级名称设置窗口
2+
set_class_name = {
3+
"ZH_CN": {
4+
"title": {"name": "班级名称设置", "description": "设置班级名称窗口标题"},
5+
"description": {"name": "在此窗口中,您可以添加、编辑或删除班级名称。\n每个班级名称将创建一个对应的JSON文件,用于存储该班级的学生名单信息。\n\n请每行输入一个班级名称,例如:\n高一1班\n高一2班\n高一3班", "description": "班级名称设置窗口描述"},
6+
"input_title": {"name": "班级名称列表", "description": "班级名称输入区域标题"},
7+
"input_placeholder": {"name": "请输入班级名称,每行一个班级名称", "description": "班级名称输入框占位符"},
8+
"save_button": {"name": "保存", "description": "保存按钮文本"},
9+
"cancel_button": {"name": "取消", "description": "取消按钮文本"},
10+
"error_title": {"name": "错误", "description": "错误消息标题"},
11+
"success_title": {"name": "成功", "description": "成功消息标题"},
12+
"info_title": {"name": "提示", "description": "信息消息标题"},
13+
"no_class_names_error": {"name": "请至少输入一个班级名称", "description": "未输入班级名称时的错误提示"},
14+
"invalid_names_error": {"name": "以下班级名称包含非法字符或是保留字: {names}", "description": "班级名称验证失败时的错误提示"},
15+
"save_error": {"name": "保存班级名称失败", "description": "保存班级名称时的错误提示"},
16+
"success_message": {"name": "成功创建 {count} 个新班级", "description": "成功创建班级时的提示消息"},
17+
"no_new_classes_message": {"name": "所有班级名称都已存在,没有创建新班级", "description": "没有创建新班级时的提示消息"},
18+
"unsaved_changes_title": {"name": "未保存的更改", "description": "未保存更改对话框标题"},
19+
"unsaved_changes_message": {"name": "您有未保存的更改,确定要关闭窗口吗?", "description": "未保存更改对话框内容"},
20+
"discard_button": {"name": "放弃更改", "description": "放弃更改按钮文本"},
21+
"continue_editing_button": {"name": "继续编辑", "description": "继续编辑按钮文本"},
22+
},
23+
}
24+
125
# 导入学生姓名语言配置
226
import_student_name = {
327
"ZH_CN": {
@@ -45,4 +69,5 @@
4569
"existing_data_option_overwrite": {"name": "覆盖现有数据", "description": "覆盖现有数据选项"},
4670
"existing_data_option_cancel": {"name": "取消导入", "description": "取消导入选项"},
4771
}
48-
}
72+
}
73+

app/page_building/another_window.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,37 @@
33
from app.page_building.window_template import SimpleWindowTemplate
44
from app.view.another_window.contributor import contributor_page
55
from app.view.another_window.import_student_name import ImportStudentNameWindow
6+
from app.view.another_window.set_class_name import SetClassNameWindow
67
from app.Language.obtain_language import *
78

89
# 全局变量,用于保持窗口引用,防止被垃圾回收
910
_window_instances = {}
1011

12+
# ==================================================
13+
# 班级名称设置窗口
14+
# ==================================================
15+
class set_class_name_window_template(PageTemplate):
16+
"""班级名称设置窗口类
17+
使用PageTemplate创建班级名称设置页面"""
18+
def __init__(self, parent=None):
19+
super().__init__(content_widget_class=SetClassNameWindow, parent=parent)
20+
21+
def create_set_class_name_window():
22+
"""
23+
创建班级名称设置窗口
24+
25+
Returns:
26+
创建的窗口实例
27+
"""
28+
title = get_content_name_async("set_class_name", "title")
29+
window = SimpleWindowTemplate(title, width=800, height=600)
30+
window.add_page_from_template("set_class_name", set_class_name_window_template)
31+
window.switch_to_page("set_class_name")
32+
_window_instances["set_class_name"] = window
33+
window.windowClosed.connect(lambda: _window_instances.pop("set_class_name", None))
34+
window.show()
35+
return
36+
1137
# ==================================================
1238
# 导入学生名单导入窗口
1339
# ==================================================
@@ -51,7 +77,7 @@ def create_contributor_window():
5177
创建的窗口实例
5278
"""
5379
title = get_content_name_async("about", "contributor")
54-
window = SimpleWindowTemplate(title, width=700, height=500)
80+
window = SimpleWindowTemplate(title, width=800, height=600)
5581
window.add_page_from_template("contributor", contributor_window_template)
5682
window.switch_to_page("contributor")
5783
_window_instances["contributor"] = window
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
# ==================================================
2+
# 导入库
3+
# ==================================================
4+
import os
5+
import re
6+
import json
7+
from typing import Dict, List, Optional, Tuple, Any
8+
9+
from loguru import logger
10+
from PyQt6.QtWidgets import *
11+
from PyQt6.QtGui import *
12+
from PyQt6.QtCore import *
13+
from qfluentwidgets import *
14+
15+
from app.tools.variable import *
16+
from app.tools.path_utils import *
17+
from app.tools.personalised import *
18+
from app.tools.settings_default import *
19+
from app.tools.settings_access import *
20+
from app.Language.obtain_language import *
21+
from app.tools.config import *
22+
from app.tools.list import *
23+
24+
class SetClassNameWindow(QWidget):
25+
"""班级名称设置窗口"""
26+
def __init__(self, parent=None):
27+
"""初始化班级名称设置窗口"""
28+
super().__init__(parent)
29+
30+
# 初始化变量
31+
self.saved = False
32+
33+
# 初始化UI
34+
self.init_ui()
35+
36+
# 连接信号
37+
self.__connect_signals()
38+
39+
def init_ui(self):
40+
"""初始化UI"""
41+
# 设置窗口标题
42+
self.setWindowTitle(get_content_name_async("set_class_name", "title"))
43+
44+
# 创建主布局
45+
self.main_layout = QVBoxLayout(self)
46+
self.main_layout.setContentsMargins(20, 20, 20, 20)
47+
self.main_layout.setSpacing(15)
48+
49+
# 创建标题
50+
self.title_label = TitleLabel(get_content_name_async("set_class_name", "title"))
51+
self.main_layout.addWidget(self.title_label)
52+
53+
# 创建说明标签
54+
self.description_label = BodyLabel(get_content_name_async("set_class_name", "description"))
55+
self.description_label.setWordWrap(True)
56+
self.main_layout.addWidget(self.description_label)
57+
58+
# 创建班级名称输入区域
59+
self.__create_class_name_input_area()
60+
61+
# 创建按钮区域
62+
self.__create_button_area()
63+
64+
# 添加伸缩项
65+
self.main_layout.addStretch(1)
66+
67+
def __create_class_name_input_area(self):
68+
"""创建班级名称输入区域"""
69+
# 创建卡片容器
70+
input_card = CardWidget()
71+
input_layout = QVBoxLayout(input_card)
72+
73+
# 创建输入区域标题
74+
input_title = SubtitleLabel(get_content_name_async("set_class_name", "input_title"))
75+
input_layout.addWidget(input_title)
76+
77+
# 创建文本编辑框
78+
self.text_edit = PlainTextEdit()
79+
self.text_edit.setPlaceholderText(get_content_name_async("set_class_name", "input_placeholder"))
80+
81+
# 加载现有班级名称
82+
try:
83+
class_names = get_class_name_list()
84+
if class_names:
85+
self.text_edit.setPlainText("\n".join(class_names))
86+
except Exception as e:
87+
logger.error(f"加载班级名称失败: {str(e)}")
88+
89+
input_layout.addWidget(self.text_edit)
90+
91+
# 添加到主布局
92+
self.main_layout.addWidget(input_card)
93+
94+
def __create_button_area(self):
95+
"""创建按钮区域"""
96+
# 创建按钮布局
97+
button_layout = QHBoxLayout()
98+
99+
# 伸缩项
100+
button_layout.addStretch(1)
101+
102+
# 保存按钮
103+
self.save_button = PrimaryPushButton(get_content_name_async("set_class_name", "save_button"))
104+
self.save_button.setIcon(FluentIcon.SAVE)
105+
button_layout.addWidget(self.save_button)
106+
107+
# 取消按钮
108+
self.cancel_button = PushButton(get_content_name_async("set_class_name", "cancel_button"))
109+
self.cancel_button.setIcon(FluentIcon.CANCEL)
110+
button_layout.addWidget(self.cancel_button)
111+
112+
# 添加到主布局
113+
self.main_layout.addLayout(button_layout)
114+
115+
def __connect_signals(self):
116+
"""连接信号与槽"""
117+
self.save_button.clicked.connect(self.__save_class_names)
118+
self.cancel_button.clicked.connect(self.__cancel)
119+
120+
def __save_class_names(self):
121+
"""保存班级名称"""
122+
try:
123+
# 获取输入的班级名称
124+
class_names_text = self.text_edit.toPlainText().strip()
125+
if not class_names_text:
126+
# 显示错误消息
127+
config = NotificationConfig(
128+
title=get_content_name_async("set_class_name", "error_title"),
129+
content=get_content_name_async("set_class_name", "no_class_names_error"),
130+
duration=3000
131+
)
132+
show_notification(NotificationType.ERROR, config, parent=self)
133+
return
134+
135+
# 分割班级名称
136+
class_names = [name.strip() for name in class_names_text.split('\n') if name.strip()]
137+
138+
# 验证班级名称
139+
invalid_names = []
140+
for name in class_names:
141+
# 检查是否包含非法字符
142+
if re.search(r'[\/:*?"<>|]', name):
143+
invalid_names.append(name)
144+
# 检查是否为保留字
145+
elif name.lower() == "class":
146+
invalid_names.append(name)
147+
148+
if invalid_names:
149+
# 显示错误消息
150+
config = NotificationConfig(
151+
title=get_content_name_async("set_class_name", "error_title"),
152+
content=get_content_name_async("set_class_name", "invalid_names_error").format(
153+
names=", ".join(invalid_names)
154+
),
155+
duration=5000
156+
)
157+
show_notification(NotificationType.ERROR, config, parent=self)
158+
return
159+
160+
# 获取班级名单目录
161+
roll_call_list_dir = get_path("app/resources/list/roll_call_list")
162+
roll_call_list_dir.mkdir(parents=True, exist_ok=True)
163+
164+
# 创建或更新班级文件
165+
created_count = 0
166+
for class_name in class_names:
167+
class_file = roll_call_list_dir / f"{class_name}.json"
168+
if not class_file.exists():
169+
# 创建空的班级文件
170+
with open_file(class_file, "w", encoding="utf-8") as f:
171+
json.dump({}, f, ensure_ascii=False, indent=4)
172+
created_count += 1
173+
174+
# 显示成功消息
175+
if created_count > 0:
176+
config = NotificationConfig(
177+
title=get_content_name_async("set_class_name", "success_title"),
178+
content=get_content_name_async("set_class_name", "success_message").format(
179+
count=created_count
180+
),
181+
duration=3000
182+
)
183+
show_notification(NotificationType.SUCCESS, config, parent=self)
184+
else:
185+
config = NotificationConfig(
186+
title=get_content_name_async("set_class_name", "info_title"),
187+
content=get_content_name_async("set_class_name", "no_new_classes_message"),
188+
duration=3000
189+
)
190+
show_notification(NotificationType.INFO, config, parent=self)
191+
192+
# 标记为已保存
193+
self.saved = True
194+
195+
# 获取父窗口并关闭
196+
parent = self.parent()
197+
while parent:
198+
# 查找SimpleWindowTemplate类型的父窗口
199+
if hasattr(parent, 'windowClosed') and hasattr(parent, 'close'):
200+
parent.close()
201+
break
202+
parent = parent.parent()
203+
204+
except Exception as e:
205+
# 显示错误消息
206+
config = NotificationConfig(
207+
title=get_content_name_async("set_class_name", "error_title"),
208+
content=f"{get_content_name_async('set_class_name', 'save_error')}: {str(e)}",
209+
duration=3000
210+
)
211+
show_notification(NotificationType.ERROR, config, parent=self)
212+
logger.error(f"保存班级名称失败: {e}")
213+
214+
def __cancel(self):
215+
"""取消操作"""
216+
# 获取父窗口并关闭
217+
parent = self.parent()
218+
while parent:
219+
# 查找SimpleWindowTemplate类型的父窗口
220+
if hasattr(parent, 'windowClosed') and hasattr(parent, 'close'):
221+
parent.close()
222+
break
223+
parent = parent.parent()
224+
225+
def closeEvent(self, event):
226+
"""窗口关闭事件处理"""
227+
if not self.saved:
228+
# 创建确认对话框
229+
dialog = Dialog(
230+
get_content_name_async("set_class_name", "unsaved_changes_title"),
231+
get_content_name_async("set_class_name", "unsaved_changes_message"),
232+
self
233+
)
234+
235+
dialog.yesButton.setText(get_content_name_async("set_class_name", "discard_button"))
236+
dialog.cancelButton.setText(get_content_name_async("set_class_name", "continue_editing_button"))
237+
238+
# 显示对话框并获取用户选择
239+
if dialog.exec():
240+
# 用户选择放弃更改,关闭窗口
241+
event.accept()
242+
else:
243+
# 用户选择继续编辑,取消关闭事件
244+
event.ignore()
245+
else:
246+
# 已保存,直接关闭
247+
event.accept()

0 commit comments

Comments
 (0)