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