@@ -40,15 +40,17 @@ def __init__(self, root: tk.Tk, title: str, create_key_display: bool = True):
4040 self ._create_params_frame ()
4141 self ._create_control_frame ()
4242 self ._create_tips_frame ()
43-
43+
4444 # 根据参数决定是否创建按键显示框架
4545 if create_key_display :
4646 self ._create_key_display_frame ()
47-
47+
4848 # 初始化按键显示相关变量(窗口内)
4949 self .keys = deque (maxlen = 14 ) # 显示最近maxlen个按键
5050 self .last_press_time = {}
5151 self .running = True
52+ self .key_listener = None # 键盘监听器实例
53+ self .key_listener_active = False # 键盘监听器是否激活
5254
5355 # 覆盖层(窗口外)默认设置与实例(默认开启)
5456 self .keycast_settings = {
@@ -63,16 +65,15 @@ def __init__(self, root: tk.Tk, title: str, create_key_display: bool = True):
6365 self .keycast_overlay = KeyCastOverlay (self .root , self .keycast_settings )
6466 except Exception :
6567 self .keycast_overlay = None
66-
67- # 启动按键监听(窗口内展示)
68- self ._start_key_listener ()
6968
7069 def _create_instrument_frame (self ):
7170 frm = ttk .LabelFrame (self .frm , text = "乐器" )
7271 frm .pack (fill = "x" , pady = (0 , 8 ))
7372 self .instrument_var = tk .StringVar (value = "piano" )
74- ttk .Radiobutton (frm , text = "钢琴 (.lrcp / .mid)" , variable = self .instrument_var , value = "piano" ).pack (side = "left" , padx = 4 )
75- ttk .Radiobutton (frm , text = "架子鼓 (.lrcd / .mid)" , variable = self .instrument_var , value = "drum" ).pack (side = "left" , padx = 8 )
73+ ttk .Radiobutton (frm , text = "钢琴 (.lrcp / .mid)" , variable = self .instrument_var , value = "piano" ).pack (side = "left" ,
74+ padx = 4 )
75+ ttk .Radiobutton (frm , text = "架子鼓 (.lrcd / .mid)" , variable = self .instrument_var , value = "drum" ).pack (side = "left" ,
76+ padx = 8 )
7677
7778 def get_instrument (self ) -> str :
7879 v = self .instrument_var .get ().strip ().lower ()
@@ -100,7 +101,8 @@ def _create_file_bar(self):
100101 right_box .pack (side = "right" )
101102 ttk .Label (right_box , text = "主题:" ).pack (side = "left" )
102103 self .theme_var = tk .StringVar (value = current_theme )
103- self .cbo_theme = ttk .Combobox (right_box , width = 16 , state = "readonly" , values = theme_names , textvariable = self .theme_var )
104+ self .cbo_theme = ttk .Combobox (right_box , width = 16 , state = "readonly" , values = theme_names ,
105+ textvariable = self .theme_var )
104106 self .cbo_theme .pack (side = "left" , padx = 4 )
105107
106108 def on_theme_changed (event = None ):
@@ -109,6 +111,7 @@ def on_theme_changed(event=None):
109111 self .style .theme_use (name )
110112 except Exception :
111113 pass
114+
112115 self .cbo_theme .bind ("<<ComboboxSelected>>" , on_theme_changed )
113116 except Exception :
114117 pass
@@ -117,7 +120,8 @@ def _create_params_frame(self):
117120 params = ttk .LabelFrame (self .frm , text = "参数" )
118121 params .pack (fill = "x" , pady = 8 )
119122 ttk .Label (params , text = "速度比例(1.0为原速):" ).grid (row = 0 , column = 0 , sticky = "e" )
120- self .ent_speed = ttk .Combobox (params , width = 8 , state = "readonly" , values = ["0.5" , "0.75" , "1.0" , "1.25" , "1.5" , "1.75" , "2.0" , "2.25" , "2.5" ])
123+ self .ent_speed = ttk .Combobox (params , width = 8 , state = "readonly" ,
124+ values = ["0.5" , "0.75" , "1.0" , "1.25" , "1.5" , "1.75" , "2.0" , "2.25" , "2.5" ])
121125 self .ent_speed .set ("1.0" )
122126 self .ent_speed .grid (row = 0 , column = 1 , sticky = "w" , padx = 6 )
123127 ttk .Label (params , text = "起始倒计时(秒):" ).grid (row = 0 , column = 2 , sticky = "e" )
@@ -133,13 +137,14 @@ def _create_params_frame(self):
133137 self .ent_latency .delete (0 , "end" )
134138 self .ent_latency .insert (0 , "0" )
135139 self .ent_latency .grid (row = 0 , column = 5 , sticky = "w" , padx = 6 )
136-
140+
137141 # 添加进度更新频率配置
138142 ttk .Label (params , text = "进度更新频率:" ).grid (row = 1 , column = 0 , sticky = "e" )
139143 self .ent_progress_freq = ttk .Combobox (params , width = 8 , state = "readonly" , values = ["1" , "2" , "3" , "5" , "10" ])
140144 self .ent_progress_freq .set ("1" )
141145 self .ent_progress_freq .grid (row = 1 , column = 1 , sticky = "w" , padx = 6 )
142- ttk .Label (params , text = "(1=每个动作都更新, 2=每2个动作更新, 以此类推)" ).grid (row = 1 , column = 2 , columnspan = 4 , sticky = "w" )
146+ ttk .Label (params , text = "(1=每个动作都更新, 2=每2个动作更新, 以此类推)" ).grid (row = 1 , column = 2 , columnspan = 4 ,
147+ sticky = "w" )
143148
144149 # 记录参数控件,便于统一禁用/启用
145150 self .param_widgets = [
@@ -160,12 +165,13 @@ def _create_control_frame(self):
160165 self .btn_keycast = ttk .Button (ctrl , text = "按键显示设置" , command = self .open_keycast_settings )
161166 self .btn_keycast .pack (side = "left" , padx = 4 )
162167 # 新增:动作录制按钮(按当前乐器类型)
163- self .btn_record = ttk .Button (ctrl , text = "动作录制" , command = lambda : open_recorder_window (self .root , self .get_instrument ()))
168+ self .btn_record = ttk .Button (ctrl , text = "动作录制" ,
169+ command = lambda : open_recorder_window (self .root , self .get_instrument ()))
164170 self .btn_record .pack (side = "left" , padx = 4 )
165-
171+
166172 self .lbl_status = ttk .Label (ctrl , text = "状态:等待载入乐谱" )
167173 self .lbl_status .pack (side = "left" , padx = 10 )
168-
174+
169175 # 添加进度条框架
170176 progress_frame = ttk .Frame (self .frm )
171177 progress_frame .pack (fill = "x" , pady = 4 )
@@ -184,12 +190,11 @@ def _create_tips_frame(self):
184190 "4) 如无响应尝试以管理员身份运行。"
185191 )).pack (fill = "x" )
186192
187-
188193 def _create_key_display_frame (self ):
189194 """创建按键显示框架(窗口内)"""
190195 key_frame = ttk .LabelFrame (self .frm , text = "按键显示" )
191196 key_frame .pack (fill = "x" , pady = 8 )
192-
197+
193198 # 创建按键显示标签(使用主题样式)
194199 if self ._ttkb is not None :
195200 self .lbl_keys = self ._ttkb .Label (
@@ -208,7 +213,7 @@ def _create_key_display_frame(self):
208213 anchor = "center" ,
209214 )
210215 self .lbl_keys .pack (fill = "x" , padx = 4 , pady = 4 )
211-
216+
212217 # 添加说明文字
213218 ttk .Label (key_frame , text = "实时显示当前按下的按键 (窗口内)" , font = ("微软雅黑" , 9 )).pack (anchor = "w" , padx = 4 )
214219
@@ -246,9 +251,11 @@ def open_keycast_settings(self):
246251
247252 ttk .Label (win , text = "透明度(0.2~1.0):" ).grid (row = row , column = 0 , sticky = "e" , padx = 6 , pady = 6 )
248253 if self ._ttkb is not None and hasattr (self ._ttkb , 'Scale' ):
249- opacity_scale = self ._ttkb .Scale (win , from_ = 0.2 , to = 1.0 , orient = "horizontal" , length = 200 , variable = var_opacity )
254+ opacity_scale = self ._ttkb .Scale (win , from_ = 0.2 , to = 1.0 , orient = "horizontal" , length = 200 ,
255+ variable = var_opacity )
250256 else :
251- opacity_scale = tk .Scale (win , from_ = 0.2 , to = 1.0 , orient = "horizontal" , resolution = 0.05 , variable = var_opacity , length = 200 )
257+ opacity_scale = tk .Scale (win , from_ = 0.2 , to = 1.0 , orient = "horizontal" , resolution = 0.05 , variable = var_opacity ,
258+ length = 200 )
252259 opacity_scale .grid (row = row , column = 1 , sticky = "w" , padx = 6 )
253260 row += 1
254261
@@ -262,20 +269,22 @@ def open_keycast_settings(self):
262269
263270 ttk .Label (win , text = "每个按键显示(秒):" ).grid (row = row , column = 0 , sticky = "e" , padx = 6 , pady = 6 )
264271 if self ._ttkb is not None and hasattr (self ._ttkb , 'Spinbox' ):
265- disptime_spin = self ._ttkb .Spinbox (win , from_ = 0.5 , to = 10.0 , increment = 0.5 , textvariable = var_disp_time , width = 8 )
272+ disptime_spin = self ._ttkb .Spinbox (win , from_ = 0.5 , to = 10.0 , increment = 0.5 , textvariable = var_disp_time ,
273+ width = 8 )
266274 else :
267275 disptime_spin = tk .Spinbox (win , from_ = 0.5 , to = 10.0 , increment = 0.5 , textvariable = var_disp_time , width = 8 )
268276 disptime_spin .grid (row = row , column = 1 , sticky = "w" , padx = 6 )
269277 row += 1
270278
271279 ttk .Label (win , text = "位置:" ).grid (row = row , column = 0 , sticky = "e" , padx = 6 , pady = 6 )
272- ttk .Combobox (win , state = "readonly" , values = list (pos_map .keys ()), textvariable = var_position , width = 14 ).grid (row = row , column = 1 , sticky = "w" , padx = 6 )
280+ ttk .Combobox (win , state = "readonly" , values = list (pos_map .keys ()), textvariable = var_position , width = 14 ).grid (
281+ row = row , column = 1 , sticky = "w" , padx = 6 )
273282 row += 1
274283
275284 # 按钮
276285 btns = ttk .Frame (win )
277286 btns .grid (row = row , column = 0 , columnspan = 2 , pady = 10 )
278-
287+
279288 def on_ok ():
280289 new_cfg = {
281290 "opacity" : max (0.2 , min (1.0 , float (var_opacity .get ()))),
@@ -291,44 +300,78 @@ def on_ok():
291300 # 同步到记录
292301 self .keycast_settings ["enabled" ] = bool (var_enabled .get ())
293302 win .destroy ()
294-
303+
295304 def on_cancel ():
296305 # 取消不更改设置
297306 win .destroy ()
298-
307+
299308 ttk .Button (btns , text = "确认" , command = on_ok ).pack (side = "left" , padx = 8 )
300309 ttk .Button (btns , text = "取消" , command = on_cancel ).pack (side = "left" , padx = 8 )
301310
302311 def _start_key_listener (self ):
303312 """启动按键监听线程(窗口内展示)"""
313+ self .keycast_overlay .start_key_listener ()
314+ if self .key_listener_active :
315+ return # 已经启动,避免重复启动
316+
304317 try :
305318 from pynput import keyboard
306-
319+
307320 def on_press (key ):
308321 """键盘按下事件"""
322+ if not self .key_listener_active :
323+ return
324+
309325 try :
310326 k = key .char .upper ()
311327 except AttributeError :
312328 k = str (key ).replace ("Key." , "" ).upper ()
313-
329+
314330 self .keys .append (k )
315331 self .last_press_time [k ] = time .time ()
316332 self ._update_key_display ()
317-
333+
318334 # 启动键盘监听器
319335 self .key_listener = keyboard .Listener (on_press = on_press )
320336 self .key_listener .start ()
321-
337+ self .key_listener_active = True
338+
322339 # 启动清理过期按键的线程
323340 threading .Thread (target = self ._cleanup_keys_loop , daemon = True ).start ()
324-
341+
325342 except ImportError :
326343 # 如果没有安装pynput,显示提示信息
327344 if hasattr (self , 'lbl_keys' ):
328345 self .lbl_keys .config (
329346 text = "需要安装 pynput 模块\n pip install pynput"
330347 )
331348
349+ def _stop_key_listener (self ):
350+ """停止按键监听线程"""
351+ if not self .key_listener_active :
352+ return # 已经停止,避免重复停止
353+
354+ self .key_listener_active = False
355+
356+ if self .key_listener :
357+ try :
358+ self .key_listener .stop ()
359+ self .key_listener = None
360+ except Exception :
361+ pass
362+
363+ # 清空按键显示
364+ self .keys .clear ()
365+ self .last_press_time .clear ()
366+ self ._update_key_display ()
367+
368+ # 清空覆盖层显示
369+ if self .keycast_overlay :
370+ try :
371+ self .keycast_overlay .clear_keys ()
372+ except Exception :
373+ pass
374+
332375 def _update_key_display (self ):
333376 """更新按键显示(窗口内)"""
334377 if hasattr (self , 'lbl_keys' ):
@@ -340,7 +383,7 @@ def _update_key_display(self):
340383
341384 def _cleanup_keys_loop (self ):
342385 """后台循环,清理过期按键(窗口内)"""
343- while self .running :
386+ while self .running and self . key_listener_active :
344387 now = time .time ()
345388 removed = False
346389 for k in list (self .keys ):
@@ -450,6 +493,8 @@ def stop_play(self):
450493 self .player = None
451494 self .reset_progress ()
452495 self .enable_params ()
496+ # 停止键盘监听
497+ self ._stop_key_listener ()
453498 # 恢复按钮与状态
454499 self .btn_start .config (state = "normal" , text = "开始演奏" )
455500 self .btn_stop .config (state = "disabled" )
0 commit comments