@@ -19,7 +19,19 @@ def __init__(self, root: tk.Tk, title: str, create_key_display: bool = True):
1919 self .score_text : Optional [str ] = None
2020 self .player : Optional [Player ] = None
2121
22- self .frm = tk .Frame (root , padx = 10 , pady = 10 )
22+ # 初始化 ttkbootstrap(如可用),默认主题 superhero
23+ self ._ttkb = None
24+ self .style = None
25+ try :
26+ import ttkbootstrap as ttkb # type: ignore
27+ self ._ttkb = ttkb
28+ self .style = ttkb .Style (theme = 'superhero' )
29+ except Exception :
30+ self ._ttkb = None
31+ self .style = None
32+
33+ # 主框架
34+ self .frm = ttk .Frame (root , padding = (10 , 10 ))
2335 self .frm .pack (fill = "both" , expand = True )
2436
2537 # 乐器选择(钢琴/架子鼓)
@@ -56,46 +68,78 @@ def __init__(self, root: tk.Tk, title: str, create_key_display: bool = True):
5668 self ._start_key_listener ()
5769
5870 def _create_instrument_frame (self ):
59- frm = tk .LabelFrame (self .frm , text = "乐器" )
60- frm .pack (fill = "x" )
71+ frm = ttk .LabelFrame (self .frm , text = "乐器" )
72+ frm .pack (fill = "x" , pady = ( 0 , 8 ) )
6173 self .instrument_var = tk .StringVar (value = "piano" )
62- tk .Radiobutton (frm , text = "钢琴 (.lrcp / .mid)" , variable = self .instrument_var , value = "piano" ).pack (side = "left" , padx = 4 )
63- tk .Radiobutton (frm , text = "架子鼓 (.lrcd / .mid)" , variable = self .instrument_var , value = "drum" ).pack (side = "left" , padx = 8 )
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 )
6476
6577 def get_instrument (self ) -> str :
6678 v = self .instrument_var .get ().strip ().lower ()
6779 return "drum" if v == "drum" else "piano"
6880
6981 def _create_file_bar (self ):
70- file_bar = tk .Frame (self .frm )
82+ file_bar = ttk .Frame (self .frm )
7183 file_bar .pack (fill = "x" )
72- tk .Button (file_bar , text = "载入乐谱" , command = self .load_score ).pack (side = "left" )
73- self .lbl_file = tk .Label (file_bar , text = "未载入" )
84+ ttk .Button (file_bar , text = "载入乐谱" , command = self .load_score ).pack (side = "left" )
85+ self .lbl_file = ttk .Label (file_bar , text = "未载入" )
7486 self .lbl_file .pack (side = "left" , padx = 8 )
7587
88+ # 右上角:主题切换(ttkbootstrap 可用时)
89+ if self .style is not None :
90+ try :
91+ # 仅列出 ttkbootstrap 主题
92+ theme_names = [t for t in self .style .theme_names () if t ]
93+ current_theme = None
94+ try :
95+ current_theme = self .style .theme_use ()
96+ except Exception :
97+ current_theme = theme_names [0 ] if theme_names else ""
98+
99+ right_box = ttk .Frame (file_bar )
100+ right_box .pack (side = "right" )
101+ ttk .Label (right_box , text = "主题:" ).pack (side = "left" )
102+ 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 .pack (side = "left" , padx = 4 )
105+
106+ def on_theme_changed (event = None ):
107+ name = self .theme_var .get ()
108+ try :
109+ self .style .theme_use (name )
110+ except Exception :
111+ pass
112+ self .cbo_theme .bind ("<<ComboboxSelected>>" , on_theme_changed )
113+ except Exception :
114+ pass
115+
76116 def _create_params_frame (self ):
77- params = tk .LabelFrame (self .frm , text = "参数" )
117+ params = ttk .LabelFrame (self .frm , text = "参数" )
78118 params .pack (fill = "x" , pady = 8 )
79- tk .Label (params , text = "速度比例(1.0为原速):" ).grid (row = 0 , column = 0 , sticky = "e" )
119+ ttk .Label (params , text = "速度比例(1.0为原速):" ).grid (row = 0 , column = 0 , sticky = "e" )
80120 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" ])
81121 self .ent_speed .set ("1.0" )
82122 self .ent_speed .grid (row = 0 , column = 1 , sticky = "w" , padx = 6 )
83- tk .Label (params , text = "起始倒计时(秒):" ).grid (row = 0 , column = 2 , sticky = "e" )
123+ ttk .Label (params , text = "起始倒计时(秒):" ).grid (row = 0 , column = 2 , sticky = "e" )
84124 self .ent_countin = ttk .Combobox (params , width = 8 , state = "readonly" , values = ["0" , "1" , "2" , "3" , "4" , "5" ])
85125 self .ent_countin .set ("2" )
86126 self .ent_countin .grid (row = 0 , column = 3 , sticky = "w" , padx = 6 )
87- tk .Label (params , text = "全局延迟(毫秒):" ).grid (row = 0 , column = 4 , sticky = "e" )
88- self .ent_latency = tk .Spinbox (params , width = 8 , from_ = - 200 , to = 200 , increment = 5 )
127+ ttk .Label (params , text = "全局延迟(毫秒):" ).grid (row = 0 , column = 4 , sticky = "e" )
128+ # Spinbox 使用 ttkbootstrap(如可用)
129+ if self ._ttkb is not None and hasattr (self ._ttkb , 'Spinbox' ):
130+ self .ent_latency = self ._ttkb .Spinbox (params , width = 8 , from_ = - 200 , to = 200 , increment = 5 )
131+ else :
132+ self .ent_latency = tk .Spinbox (params , width = 8 , from_ = - 200 , to = 200 , increment = 5 )
89133 self .ent_latency .delete (0 , "end" )
90134 self .ent_latency .insert (0 , "0" )
91135 self .ent_latency .grid (row = 0 , column = 5 , sticky = "w" , padx = 6 )
92136
93137 # 添加进度更新频率配置
94- tk .Label (params , text = "进度更新频率:" ).grid (row = 1 , column = 0 , sticky = "e" )
138+ ttk .Label (params , text = "进度更新频率:" ).grid (row = 1 , column = 0 , sticky = "e" )
95139 self .ent_progress_freq = ttk .Combobox (params , width = 8 , state = "readonly" , values = ["1" , "2" , "3" , "5" , "10" ])
96140 self .ent_progress_freq .set ("1" )
97141 self .ent_progress_freq .grid (row = 1 , column = 1 , sticky = "w" , padx = 6 )
98- tk .Label (params , text = "(1=每个动作都更新, 2=每2个动作更新, 以此类推)" ).grid (row = 1 , column = 2 , columnspan = 4 , sticky = "w" )
142+ ttk .Label (params , text = "(1=每个动作都更新, 2=每2个动作更新, 以此类推)" ).grid (row = 1 , column = 2 , columnspan = 4 , sticky = "w" )
99143
100144 # 记录参数控件,便于统一禁用/启用
101145 self .param_widgets = [
@@ -106,34 +150,34 @@ def _create_params_frame(self):
106150 ]
107151
108152 def _create_control_frame (self ):
109- ctrl = tk .Frame (self .frm )
153+ ctrl = ttk .Frame (self .frm )
110154 ctrl .pack (fill = "x" , pady = 6 )
111- self .btn_start = tk .Button (ctrl , text = "开始演奏" , command = self .toggle_play_pause , state = "disabled" )
155+ self .btn_start = ttk .Button (ctrl , text = "开始演奏" , command = self .toggle_play_pause , state = "disabled" )
112156 self .btn_start .pack (side = "left" , padx = 4 )
113- self .btn_stop = tk .Button (ctrl , text = "停止" , command = self .stop_play , state = "disabled" )
157+ self .btn_stop = ttk .Button (ctrl , text = "停止" , command = self .stop_play , state = "disabled" )
114158 self .btn_stop .pack (side = "left" , padx = 4 )
115159 # 新增:按键显示设置按钮
116- self .btn_keycast = tk .Button (ctrl , text = "按键显示设置" , command = self .open_keycast_settings )
160+ self .btn_keycast = ttk .Button (ctrl , text = "按键显示设置" , command = self .open_keycast_settings )
117161 self .btn_keycast .pack (side = "left" , padx = 4 )
118162 # 新增:动作录制按钮(按当前乐器类型)
119- self .btn_record = tk .Button (ctrl , text = "动作录制" , command = lambda : open_recorder_window (self .root , self .get_instrument ()))
163+ self .btn_record = ttk .Button (ctrl , text = "动作录制" , command = lambda : open_recorder_window (self .root , self .get_instrument ()))
120164 self .btn_record .pack (side = "left" , padx = 4 )
121165
122- self .lbl_status = tk .Label (ctrl , text = "状态:等待载入乐谱" )
166+ self .lbl_status = ttk .Label (ctrl , text = "状态:等待载入乐谱" )
123167 self .lbl_status .pack (side = "left" , padx = 10 )
124168
125169 # 添加进度条框架
126- progress_frame = tk .Frame (self .frm )
170+ progress_frame = ttk .Frame (self .frm )
127171 progress_frame .pack (fill = "x" , pady = 4 )
128- self .lbl_progress = tk .Label (progress_frame , text = "进度:0%" )
172+ self .lbl_progress = ttk .Label (progress_frame , text = "进度:0%" )
129173 self .lbl_progress .pack (side = "left" , padx = 10 )
130174 self .progress_bar = ttk .Progressbar (progress_frame , mode = 'determinate' , length = 300 )
131175 self .progress_bar .pack (side = "left" , padx = 4 , fill = "x" , expand = True )
132176
133177 def _create_tips_frame (self ):
134- tips = tk .LabelFrame (self .frm , text = "使用提示" )
178+ tips = ttk .LabelFrame (self .frm , text = "使用提示" )
135179 tips .pack (fill = "x" , pady = 8 )
136- tk .Label (tips , justify = "left" , anchor = "w" , text = (
180+ ttk .Label (tips , justify = "left" , anchor = "w" , text = (
137181 "1) 乐谱支持延长音:写法 [起始时间][结束时间] TOKENS\n "
138182 "2) 单时间戳仍可用作短音:[时间] TOKENS\n "
139183 "3) 载入后切换到游戏窗口,回到本工具点击开始;\n "
@@ -143,25 +187,30 @@ def _create_tips_frame(self):
143187
144188 def _create_key_display_frame (self ):
145189 """创建按键显示框架(窗口内)"""
146- key_frame = tk .LabelFrame (self .frm , text = "按键显示" )
190+ key_frame = ttk .LabelFrame (self .frm , text = "按键显示" )
147191 key_frame .pack (fill = "x" , pady = 8 )
148192
149- # 创建按键显示标签
150- self .lbl_keys = tk .Label (
151- key_frame ,
152- text = "等待按键..." ,
153- font = ("Consolas" , 16 , "bold" ),
154- fg = "white" ,
155- bg = "#C0C0C0" , # 使用深灰色模拟半透明效果
156- height = 2 ,
157- anchor = "center" ,
158- relief = "flat" , # 去掉边框,让背景更平滑
159- borderwidth = 0
160- )
193+ # 创建按键显示标签(使用主题样式)
194+ if self ._ttkb is not None :
195+ self .lbl_keys = self ._ttkb .Label (
196+ key_frame ,
197+ text = "等待按键..." ,
198+ font = ("Consolas" , 16 , "bold" ),
199+ bootstyle = "secondary inverse" ,
200+ anchor = "center" ,
201+ padding = 6 ,
202+ )
203+ else :
204+ self .lbl_keys = ttk .Label (
205+ key_frame ,
206+ text = "等待按键..." ,
207+ font = ("Consolas" , 16 , "bold" ),
208+ anchor = "center" ,
209+ )
161210 self .lbl_keys .pack (fill = "x" , padx = 4 , pady = 4 )
162211
163212 # 添加说明文字
164- tk .Label (key_frame , text = "实时显示当前按下的按键 (窗口内)" , font = ("微软雅黑" , 9 ), fg = "gray" ).pack (anchor = "w" , padx = 4 )
213+ ttk .Label (key_frame , text = "实时显示当前按下的按键 (窗口内)" , font = ("微软雅黑" , 9 )).pack (anchor = "w" , padx = 4 )
165214
166215 def open_keycast_settings (self ):
167216 """打开覆盖层设置窗口"""
@@ -196,23 +245,35 @@ def open_keycast_settings(self):
196245 row += 1
197246
198247 ttk .Label (win , text = "透明度(0.2~1.0):" ).grid (row = row , column = 0 , sticky = "e" , padx = 6 , pady = 6 )
199- tk .Scale (win , from_ = 0.2 , to = 1.0 , orient = "horizontal" , resolution = 0.05 , variable = var_opacity , length = 200 ).grid (row = row , column = 1 , sticky = "w" , padx = 6 )
248+ 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 )
250+ else :
251+ opacity_scale = tk .Scale (win , from_ = 0.2 , to = 1.0 , orient = "horizontal" , resolution = 0.05 , variable = var_opacity , length = 200 )
252+ opacity_scale .grid (row = row , column = 1 , sticky = "w" , padx = 6 )
200253 row += 1
201254
202255 ttk .Label (win , text = "显示最近几个按键:" ).grid (row = row , column = 0 , sticky = "e" , padx = 6 , pady = 6 )
203- tk .Spinbox (win , from_ = 1 , to = 20 , textvariable = var_max_keys , width = 8 ).grid (row = row , column = 1 , sticky = "w" , padx = 6 )
256+ if self ._ttkb is not None and hasattr (self ._ttkb , 'Spinbox' ):
257+ maxkeys_spin = self ._ttkb .Spinbox (win , from_ = 1 , to = 20 , textvariable = var_max_keys , width = 8 )
258+ else :
259+ maxkeys_spin = tk .Spinbox (win , from_ = 1 , to = 20 , textvariable = var_max_keys , width = 8 )
260+ maxkeys_spin .grid (row = row , column = 1 , sticky = "w" , padx = 6 )
204261 row += 1
205262
206263 ttk .Label (win , text = "每个按键显示(秒):" ).grid (row = row , column = 0 , sticky = "e" , padx = 6 , pady = 6 )
207- tk .Spinbox (win , from_ = 0.5 , to = 10.0 , increment = 0.5 , textvariable = var_disp_time , width = 8 ).grid (row = row , column = 1 , sticky = "w" , padx = 6 )
264+ 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 )
266+ else :
267+ disptime_spin = tk .Spinbox (win , from_ = 0.5 , to = 10.0 , increment = 0.5 , textvariable = var_disp_time , width = 8 )
268+ disptime_spin .grid (row = row , column = 1 , sticky = "w" , padx = 6 )
208269 row += 1
209270
210271 ttk .Label (win , text = "位置:" ).grid (row = row , column = 0 , sticky = "e" , padx = 6 , pady = 6 )
211272 ttk .Combobox (win , state = "readonly" , values = list (pos_map .keys ()), textvariable = var_position , width = 14 ).grid (row = row , column = 1 , sticky = "w" , padx = 6 )
212273 row += 1
213274
214275 # 按钮
215- btns = tk .Frame (win )
276+ btns = ttk .Frame (win )
216277 btns .grid (row = row , column = 0 , columnspan = 2 , pady = 10 )
217278
218279 def on_ok ():
@@ -265,10 +326,7 @@ def on_press(key):
265326 # 如果没有安装pynput,显示提示信息
266327 if hasattr (self , 'lbl_keys' ):
267328 self .lbl_keys .config (
268- text = "需要安装 pynput 模块\n pip install pynput" ,
269- font = ("微软雅黑" , 10 ),
270- fg = "red" ,
271- bg = "lightgray"
329+ text = "需要安装 pynput 模块\n pip install pynput"
272330 )
273331
274332 def _update_key_display (self ):
0 commit comments