1- from PySide6 .QtCore import QObject , Signal , QTimer , QEasingCurve , QFileSystemWatcher
1+ from PySide6 .QtCore import (
2+ QObject ,
3+ Signal ,
4+ QTimer ,
5+ QEasingCurve ,
6+ QFileSystemWatcher ,
7+ QThreadPool ,
8+ QRunnable ,
9+ )
210from PySide6 .QtGui import QFont
311from dataclasses import dataclass
412from loguru import logger
@@ -72,6 +80,9 @@ def __init__(self):
7280 self .current_group_index = 0
7381 self .current_gender_index = 0
7482 self .half_repeat = 0
83+ self ._precomputed_result = None
84+ self ._precompute_key = None
85+ self ._precompute_running = False
7586
7687 def build_draw_context (
7788 self ,
@@ -179,6 +190,9 @@ def load_data(
179190 加载学生数据
180191 """
181192 try :
193+ self ._precomputed_result = None
194+ self ._precompute_key = None
195+ self ._precompute_running = False
182196 self .current_class_name = class_name
183197 self .current_group_filter = group_filter
184198 self .current_gender_filter = gender_filter
@@ -252,6 +266,9 @@ def draw_final_students(self, count):
252266 """
253267 执行最终抽取
254268 """
269+ precomputed = self .take_precomputed_result (count )
270+ if precomputed :
271+ return precomputed
255272 # 使用RollCallUtils中的核心抽取逻辑,它处理了去重、权重等复杂逻辑
256273 result = RollCallUtils .draw_random_students (
257274 self .current_class_name ,
@@ -277,6 +294,91 @@ def save_result(self, selected_students, selected_students_dict):
277294 self .half_repeat ,
278295 )
279296
297+ def _build_precompute_key (self , count ):
298+ try :
299+ count = int (count or 0 )
300+ except Exception :
301+ count = 0
302+ return (
303+ self .current_class_name ,
304+ self .current_group_filter ,
305+ self .current_gender_filter ,
306+ self .current_group_index ,
307+ self .current_gender_index ,
308+ self .half_repeat ,
309+ count ,
310+ )
311+
312+ def start_precompute_final (self , count ):
313+ try :
314+ count = int (count or 0 )
315+ except Exception :
316+ count = 0
317+ if count <= 0 or not self .current_class_name :
318+ return
319+ key = self ._build_precompute_key (count )
320+ if self ._precompute_running and self ._precompute_key == key :
321+ return
322+ self ._precompute_key = key
323+ self ._precomputed_result = None
324+ self ._precompute_running = True
325+
326+ class_name = self .current_class_name
327+ group_index = self .current_group_index
328+ group_filter = self .current_group_filter
329+ gender_index = self .current_gender_index
330+ gender_filter = self .current_gender_filter
331+ half_repeat = self .half_repeat
332+ draw_count = count
333+
334+ class _Signals (QObject ):
335+ loaded = Signal (object , object )
336+
337+ class _Loader (QRunnable ):
338+ def __init__ (self , fn , signals , key ):
339+ super ().__init__ ()
340+ self .fn = fn
341+ self .signals = signals
342+ self .key = key
343+
344+ def run (self ):
345+ try :
346+ result = self .fn ()
347+ self .signals .loaded .emit (self .key , result )
348+ except Exception as e :
349+ logger .exception (f"预计算最终抽取结果失败: { e } " )
350+
351+ def _collect ():
352+ return RollCallUtils .draw_random_students (
353+ class_name ,
354+ group_index ,
355+ group_filter ,
356+ gender_index ,
357+ gender_filter ,
358+ draw_count ,
359+ half_repeat ,
360+ )
361+
362+ signals = _Signals ()
363+ signals .loaded .connect (self ._on_precompute_loaded )
364+ runnable = _Loader (_collect , signals , key )
365+ QThreadPool .globalInstance ().start (runnable )
366+
367+ def _on_precompute_loaded (self , key , result ):
368+ if key != self ._precompute_key :
369+ return
370+ self ._precomputed_result = result
371+ self ._precompute_running = False
372+
373+ def take_precomputed_result (self , count ):
374+ key = self ._build_precompute_key (count )
375+ if self ._precomputed_result and self ._precompute_key == key :
376+ result = self ._precomputed_result
377+ self ._precomputed_result = None
378+ self ._precompute_running = False
379+ return result
380+ return None
381+
280382 def reset_records (self ):
281383 """重置抽取记录"""
282384 remove_record (
@@ -321,6 +423,8 @@ def start_roll_call_draw(widget):
321423 autoplay_count = plan .autoplay_count if plan else 0
322424 animation_interval = plan .animation_interval if plan else 0
323425 animation_music = plan .animation_music if plan else None
426+ if animation in [0 , 1 ]:
427+ manager .start_precompute_final (widget .current_count )
324428
325429 if animation == 0 :
326430 if animation_music :
@@ -527,12 +631,18 @@ def play_voice_result(widget):
527631 "basic_voice_settings" , "edge_tts_voice_name"
528632 )
529633
634+ class_name = (
635+ widget .final_class_name
636+ if hasattr (widget , "final_class_name" ) and widget .final_class_name
637+ else widget .list_combobox .currentText ()
638+ )
639+
530640 widget .tts_handler .voice_play (
531641 config = voice_settings ,
532642 student_names = student_names ,
533643 engine_type = engine_type ,
534644 voice_name = edge_tts_voice_name ,
535- class_name = widget . list_combobox . currentText () ,
645+ class_name = class_name ,
536646 )
537647 except Exception as e :
538648 logger .exception (f"播放语音失败: { e } " , exc_info = True )
@@ -584,7 +694,19 @@ def display_result_animated(widget, selected_students, class_name):
584694 settings_group = "roll_call_settings" ,
585695 )
586696
587- ResultDisplayUtils .display_results_in_grid (widget .result_grid , student_labels )
697+ cached_widgets = ResultDisplayUtils .collect_grid_widgets (widget .result_grid )
698+ if cached_widgets and len (cached_widgets ) == len (student_labels ):
699+ updated = ResultDisplayUtils .update_grid_labels (
700+ widget .result_grid , student_labels , cached_widgets
701+ )
702+ if updated :
703+ ResultDisplayUtils .dispose_widgets (student_labels )
704+ else :
705+ ResultDisplayUtils .display_results_in_grid (
706+ widget .result_grid , student_labels
707+ )
708+ else :
709+ ResultDisplayUtils .display_results_in_grid (widget .result_grid , student_labels )
588710
589711 RollCallUtils .show_notification_if_enabled (
590712 class_name = class_name ,
0 commit comments