Skip to content

Commit 8e9f7bf

Browse files
[feat] 增加演奏进度条
1. 单人与多人ui上增加静态的演奏进度条
1 parent 1a0ec97 commit 8e9f7bf

File tree

4 files changed

+70
-10
lines changed

4 files changed

+70
-10
lines changed

src/app.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import os
22
import tkinter as tk
3-
from tkinter import filedialog, messagebox
3+
from tkinter import filedialog, messagebox, ttk
44
from typing import List, Optional
55

66
from src.player import Player
@@ -44,6 +44,13 @@ def _create_params_frame(self):
4444
self.ent_latency = tk.Entry(params, width=8)
4545
self.ent_latency.insert(0, "0")
4646
self.ent_latency.grid(row=0, column=5, sticky="w", padx=6)
47+
48+
# 添加进度更新频率配置
49+
tk.Label(params, text="进度更新频率:").grid(row=1, column=0, sticky="e")
50+
self.ent_progress_freq = tk.Entry(params, width=8)
51+
self.ent_progress_freq.insert(0, "1")
52+
self.ent_progress_freq.grid(row=1, column=1, sticky="w", padx=6)
53+
tk.Label(params, text="(1=每个动作都更新, 2=每2个动作更新, 以此类推)").grid(row=1, column=2, columnspan=4, sticky="w")
4754

4855
def _create_control_frame(self):
4956
ctrl = tk.Frame(self.frm)
@@ -54,6 +61,14 @@ def _create_control_frame(self):
5461
self.btn_stop.pack(side="left", padx=4)
5562
self.lbl_status = tk.Label(ctrl, text="状态:等待载入乐谱")
5663
self.lbl_status.pack(side="left", padx=10)
64+
65+
# 添加进度条框架
66+
progress_frame = tk.Frame(self.frm)
67+
progress_frame.pack(fill="x", pady=4)
68+
self.lbl_progress = tk.Label(progress_frame, text="进度:0%")
69+
self.lbl_progress.pack(side="left", padx=10)
70+
self.progress_bar = ttk.Progressbar(progress_frame, mode='determinate', length=300)
71+
self.progress_bar.pack(side="left", padx=4, fill="x", expand=True)
5772

5873
def _create_tips_frame(self):
5974
tips = tk.LabelFrame(self.frm, text="使用提示")
@@ -65,6 +80,21 @@ def _create_tips_frame(self):
6580
"4) 如无响应尝试以管理员身份运行 Python。"
6681
)).pack(fill="x")
6782

83+
def update_progress(self, current: int, total: int):
84+
"""更新进度条和进度标签"""
85+
if total > 0:
86+
percentage = int((current / total) * 100)
87+
self.progress_bar['value'] = percentage
88+
self.lbl_progress.config(text=f"进度:{percentage}% ({current}/{total})")
89+
else:
90+
self.progress_bar['value'] = 0
91+
self.lbl_progress.config(text="进度:0%")
92+
93+
def reset_progress(self):
94+
"""重置进度条"""
95+
self.progress_bar['value'] = 0
96+
self.lbl_progress.config(text="进度:0%")
97+
6898
def load_score(self):
6999
"""加载乐谱文件,支持 .lrcp 或 .mid;若为 .mid 则先自动转换为 .lrcp 再读取"""
70100
path = filedialog.askopenfilename(
@@ -119,6 +149,7 @@ def stop_play(self):
119149
if self.player:
120150
self.player.stop()
121151
self.player = None
152+
self.reset_progress()
122153

123154

124155
if __name__ == '__main__':

src/app_multi.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ def __init__(self, root: tk.Tk):
2626
'4) 适度调整偏移可减少漏音(建议 -20~20 范围内微调)。'
2727
)).pack(fill="x")
2828

29-
# 添加多人模式特有的参数
29+
# 添加多人模式特有的参数 - 使用正确的行数
3030
params = self.frm.winfo_children()[1] # 获取第二个子元素(params)
31-
tk.Label(params, text="多音偏移(ms):").grid(row=1, column=0, sticky="e")
31+
tk.Label(params, text="多音偏移(ms):").grid(row=2, column=0, sticky="e")
3232
self.ent_offsets = tk.Entry(params, width=20)
3333
self.ent_offsets.insert(0, "-15,0,15")
34-
self.ent_offsets.grid(row=1, column=1, columnspan=3, sticky="w", padx=4)
35-
tk.Label(params, text="范围-50~50,按顺序循环应用到同一时间的多个音").grid(row=1, column=4, columnspan=2, sticky="w")
34+
self.ent_offsets.grid(row=2, column=1, columnspan=3, sticky="w", padx=4)
35+
tk.Label(params, text="范围-50~50,按顺序循环应用到同一时间的多个音").grid(row=2, column=4, columnspan=2, sticky="w")
3636

3737
def _parse_score(self, score_text: str) -> List[Event]:
3838
return parse_score(score_text, multi=True)
@@ -62,19 +62,27 @@ def start_play(self):
6262
latency = int(float(self.ent_latency.get()))
6363
except:
6464
latency = 0
65+
66+
try:
67+
progress_freq = int(float(self.ent_progress_freq.get()))
68+
except:
69+
progress_freq = 1
6570

6671
# 每次开始前按当前偏移重新生成
6772
self.update_play_events()
6873
self.btn_start.config(state="disabled")
6974
self.btn_stop.config(state="normal")
7075
self.lbl_status.config(text=f'演奏中… (总 {len(self.play_events)} 单音事件)')
76+
77+
# 重置进度条
78+
self.reset_progress()
7179

7280
def on_done():
7381
self.btn_start.config(state="normal")
7482
self.btn_stop.config(state="disabled")
7583
self.lbl_status.config(text='完成/已停止')
7684

77-
self.player = Player(self.play_events, countin, latency, speed, on_done)
85+
self.player = Player(self.play_events, countin, latency, speed, on_done, self.update_progress, progress_freq)
7886
self.player.start()
7987

8088
def parse_offsets(self) -> List[int]:

src/app_single.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,25 @@ def start_play(self):
5555
latency = int(float(self.ent_latency.get()))
5656
except:
5757
latency = 0
58+
59+
try:
60+
progress_freq = int(float(self.ent_progress_freq.get()))
61+
except:
62+
progress_freq = 1
5863

5964
self.btn_start.config(state="disabled")
6065
self.btn_stop.config(state="normal")
6166
self.lbl_status.config(text="演奏中…(切到游戏保持焦点)")
67+
68+
# 重置进度条
69+
self.reset_progress()
6270

6371
def on_done():
6472
self.btn_start.config(state="normal")
6573
self.btn_stop.config(state="disabled")
6674
self.lbl_status.config(text="完成/已停止")
6775

68-
self.player = Player(self.events, countin, latency, speed, on_done)
76+
self.player = Player(self.events, countin, latency, speed, on_done, self.update_progress, progress_freq)
6977
self.player.start()
7078

7179

src/player.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,28 @@
11
import time
22
import threading
3-
from typing import List, Tuple, Union
3+
from typing import List, Tuple, Union, Optional, Callable
44

55
from src.event import Event, SimpleEvent
66
from src.key_sender import key_sender
77

88

99
class Player(threading.Thread):
10-
def __init__(self, events: List[Union[Event, SimpleEvent]], start_delay: float, global_latency_ms: int, speed_ratio: float, on_done):
10+
def __init__(self, events: List[Union[Event, SimpleEvent]], start_delay: float, global_latency_ms: int, speed_ratio: float, on_done, progress_callback: Optional[Callable[[int, int], None]] = None, progress_update_freq: int = 1):
1111
super().__init__(daemon=True)
1212
self.events = events
1313
self.start_delay = max(0.0, start_delay)
1414
self.global_latency = max(0, global_latency_ms) / 1000.0
1515
self.speed_ratio = max(0.05, speed_ratio)
1616
self._stop = threading.Event()
1717
self.on_done = on_done
18+
self.progress_callback = progress_callback
19+
self.progress_update_freq = max(1, progress_update_freq) # 确保至少为1
1820

1921
def stop(self):
2022
self._stop.set()
2123

2224
def run(self):
25+
total_actions = 0
2326
try:
2427
if not self.events:
2528
return
@@ -48,7 +51,9 @@ def run(self):
4851
actions.sort(key=lambda x: x[0])
4952
t0 = time.perf_counter() + self.start_delay
5053
idx = 0
51-
while idx < len(actions) and not self._stop.is_set():
54+
total_actions = len(actions)
55+
56+
while idx < total_actions and not self._stop.is_set():
5257
now = time.perf_counter()
5358
target = t0 + actions[idx][0] + self.global_latency
5459
wait = target - now
@@ -61,9 +66,17 @@ def run(self):
6166
else:
6267
key_sender.release(keys)
6368
idx += 1
69+
70+
# 根据用户配置的频率更新进度
71+
if self.progress_callback and (idx % self.progress_update_freq == 0 or idx == total_actions):
72+
self.progress_callback(idx, total_actions)
73+
6474
finally:
6575
# 确保释放所有剩余按键
6676
key_sender.release_all()
77+
# 报告完成进度
78+
if self.progress_callback:
79+
self.progress_callback(total_actions, total_actions)
6780
if self.on_done:
6881
self.on_done()
6982

0 commit comments

Comments
 (0)