Skip to content

Commit 0014dc7

Browse files
[feat]
1. 增加自定义按键映射,尚未完成 2. 删除无效信息
1 parent 1fe7aed commit 0014dc7

File tree

5 files changed

+285
-5
lines changed

5 files changed

+285
-5
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ OverField_Auto_Piano
7272
│ └─ app_transcription.py # MP3 转录 MID界面入口
7373
└─ utils
7474
├─ constant.py # 键位映射 & 正则(含 drum_map)
75-
├─ key_cast_overlay_demo.py # 按键叠加层
75+
├─ key_cast_overlay.py # 按键叠加层
7676
├─ lrcp_recorder.py # 录制实时演奏生成 .lrcp / .lrcd
7777
├─ midi2lrcd.py # MIDI -> LRCD 转换函数 & CLI
7878
├─ midi2lrcp.py # MIDI -> LRCP 转换函数 & CLI

src/app.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from src.player import Player
1010
from src.event import Event
11-
from utils.key_cast_overlay_demo import KeyCastOverlay
11+
from utils.key_cast_overlay import KeyCastOverlay
1212
from utils.lrcp_recorder import open_recorder_window
1313

1414

@@ -165,8 +165,7 @@ def _create_control_frame(self):
165165
self.btn_keycast = ttk.Button(ctrl, text="按键显示设置", command=self.open_keycast_settings)
166166
self.btn_keycast.pack(side="left", padx=4)
167167
# 新增:动作录制按钮(按当前乐器类型)
168-
self.btn_record = ttk.Button(ctrl, text="动作录制",
169-
command=lambda: open_recorder_window(self.root, self.get_instrument()))
168+
self.btn_record = ttk.Button(ctrl, text="动作录制", command=lambda: open_recorder_window(self.root, self.get_instrument()))
170169
self.btn_record.pack(side="left", padx=4)
171170

172171
self.lbl_status = ttk.Label(ctrl, text="状态:等待载入乐谱")

src/app_single.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ def row(lbl, txt):
4444
row("低音 L:", "L1 L2 L3 L4 L5 L6 L7 -> A S D F G H J")
4545
row("中音 M:", "M1 M2 M3 M4 M5 M6 M7 -> Q W E R T Y U")
4646
row("高音 H:", "H1 H2 H3 H4 H5 H6 H7 -> 1 2 3 4 5 6 7")
47-
row("和弦 :", "C Dm Em F G Am G7 -> Z X C V B N M")
4847
else:
4948
row("架子鼓:", "踩镲闭->1 高音吊镲->2 一嗵鼓->3 二嗵鼓->4 叮叮镲->5")
5049
row("", "踩镲开->Q 军鼓->W 底鼓->E 落地嗵鼓->R 中音吊镲->T")

utils/custom_key.py

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
import tkinter as tk
2+
from tkinter import ttk, messagebox
3+
import json
4+
import os
5+
from typing import Dict, List
6+
7+
# 默认键位配置
8+
DEFAULT_KEY_MAPS = {
9+
"开放空间": {
10+
"low_map": {str(i): k for i, k in zip(range(1, 8), list("asdfghj"))},
11+
"mid_map": {str(i): k for i, k in zip(range(1, 8), list("qwertyu"))},
12+
"high_map": {str(i): k for i, k in zip(range(1, 8), list("1234567"))}
13+
},
14+
"原神": {
15+
"low_map": {str(i): k for i, k in zip(range(1, 8), list("zxcvbnm"))},
16+
"mid_map": {str(i): k for i, k in zip(range(1, 8), list("asdfghj"))},
17+
"high_map": {str(i): k for i, k in zip(range(1, 8), list("qwertyu"))}
18+
}
19+
}
20+
21+
CONFIG_FILE = "key_map_config.json"
22+
23+
24+
class CustomKeyMap:
25+
"""自定义键位映射管理类"""
26+
27+
def __init__(self):
28+
self.current_profile = "开放空间"
29+
self.key_maps = DEFAULT_KEY_MAPS.copy()
30+
self.load_config()
31+
32+
def load_config(self):
33+
"""从配置文件加载键位配置"""
34+
try:
35+
if os.path.exists(CONFIG_FILE):
36+
with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
37+
config = json.load(f)
38+
# 兼容旧版本配置(移除和弦映射)
39+
for profile_name, profile_data in config.get("profiles", {}).items():
40+
if "chord_map" in profile_data:
41+
del profile_data["chord_map"]
42+
self.key_maps.update(config.get("profiles", {}))
43+
self.current_profile = config.get("current_profile", "开放空间")
44+
except Exception as e:
45+
print(f"加载配置文件失败: {e}")
46+
47+
def save_config(self):
48+
"""保存键位配置到文件"""
49+
try:
50+
config = {
51+
"current_profile": self.current_profile,
52+
"profiles": self.key_maps
53+
}
54+
with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
55+
json.dump(config, f, ensure_ascii=False, indent=2)
56+
except Exception as e:
57+
print(f"保存配置文件失败: {e}")
58+
59+
def get_current_map(self) -> Dict:
60+
"""获取当前配置的键位映射"""
61+
return self.key_maps.get(self.current_profile, DEFAULT_KEY_MAPS["开放空间"])
62+
63+
def get_profile_names(self) -> List[str]:
64+
"""获取所有配置文件的名称"""
65+
return list(self.key_maps.keys())
66+
67+
def create_profile(self, name: str, profile_data: Dict):
68+
"""创建新的键位配置"""
69+
if name in self.key_maps:
70+
return False
71+
self.key_maps[name] = profile_data
72+
self.save_config()
73+
return True
74+
75+
def update_profile(self, name: str, profile_data: Dict):
76+
"""更新键位配置"""
77+
if name not in self.key_maps:
78+
return False
79+
self.key_maps[name] = profile_data
80+
self.save_config()
81+
return True
82+
83+
def delete_profile(self, name: str):
84+
"""删除键位配置"""
85+
if name in ["开放空间", "原神"]:
86+
return False # 不允许删除默认配置
87+
if name in self.key_maps:
88+
del self.key_maps[name]
89+
if self.current_profile == name:
90+
self.current_profile = "开放空间"
91+
self.save_config()
92+
return True
93+
return False
94+
95+
def set_current_profile(self, profile_name: str):
96+
"""设置当前使用的配置"""
97+
if profile_name in self.key_maps:
98+
self.current_profile = profile_name
99+
self.save_config()
100+
return True
101+
return False
102+
103+
104+
class KeyMapEditor:
105+
"""键位自定义映射界面"""
106+
107+
def __init__(self, master, key_map_manager: CustomKeyMap):
108+
self.master = master
109+
self.manager = key_map_manager
110+
self.current_profile = self.manager.current_profile
111+
self.setup_ui()
112+
self.load_current_profile()
113+
114+
def setup_ui(self):
115+
"""设置UI界面"""
116+
self.master.title("钢琴键位自定义映射")
117+
self.master.geometry("350x400")
118+
119+
# 主框架
120+
main_frame = ttk.Frame(self.master, padding="10")
121+
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
122+
123+
# 配置文件选择
124+
ttk.Label(main_frame, text="选择配置:").grid(row=0, column=0, sticky=tk.W, pady=5)
125+
self.profile_var = tk.StringVar()
126+
self.profile_combo = ttk.Combobox(main_frame, textvariable=self.profile_var, state="readonly")
127+
self.profile_combo.grid(row=0, column=1, sticky=(tk.W, tk.E), pady=5, padx=5)
128+
self.profile_combo.bind('<<ComboboxSelected>>', self.on_profile_change)
129+
130+
# 新建配置按钮
131+
ttk.Button(main_frame, text="新建配置", command=self.create_new_profile).grid(row=0, column=2, padx=5)
132+
133+
# 配置名称编辑
134+
ttk.Label(main_frame, text="配置名称:").grid(row=1, column=0, sticky=tk.W, pady=5)
135+
self.name_var = tk.StringVar()
136+
ttk.Entry(main_frame, textvariable=self.name_var).grid(row=1, column=1, sticky=(tk.W, tk.E), pady=5, padx=5)
137+
138+
# 笔记本控件
139+
notebook = ttk.Notebook(main_frame)
140+
notebook.grid(row=2, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=10)
141+
142+
# 低音区标签页
143+
low_frame = ttk.Frame(notebook, padding="10")
144+
notebook.add(low_frame, text="低音区 (L1-L7)")
145+
self.setup_key_frame(low_frame, "low")
146+
147+
# 中音区标签页
148+
mid_frame = ttk.Frame(notebook, padding="10")
149+
notebook.add(mid_frame, text="中音区 (M1-M7)")
150+
self.setup_key_frame(mid_frame, "mid")
151+
152+
# 高音区标签页
153+
high_frame = ttk.Frame(notebook, padding="10")
154+
notebook.add(high_frame, text="高音区 (H1-H7)")
155+
self.setup_key_frame(high_frame, "high")
156+
157+
# 按钮框架
158+
button_frame = ttk.Frame(main_frame)
159+
button_frame.grid(row=3, column=0, columnspan=3, pady=10)
160+
161+
ttk.Button(button_frame, text="保存当前配置", command=self.save_profile).pack(side=tk.LEFT, padx=5)
162+
ttk.Button(button_frame, text="删除当前配置", command=self.delete_profile).pack(side=tk.LEFT, padx=5)
163+
ttk.Button(button_frame, text="应用当前配置", command=self.apply_changes).pack(side=tk.LEFT, padx=5)
164+
165+
# 配置网格权重
166+
main_frame.columnconfigure(1, weight=1)
167+
main_frame.rowconfigure(2, weight=1)
168+
169+
self.update_profile_list()
170+
171+
def setup_key_frame(self, parent, key_type):
172+
"""设置音区键位框架"""
173+
for i in range(7):
174+
note_num = i + 1
175+
ttk.Label(parent, text=f"{key_type.upper()} {note_num}:").grid(row=i, column=0, sticky=tk.W, pady=2)
176+
var = tk.StringVar()
177+
entry = ttk.Entry(parent, textvariable=var, width=5)
178+
entry.grid(row=i, column=1, sticky=tk.W, pady=2, padx=5)
179+
setattr(self, f"{key_type}_{note_num}_var", var)
180+
181+
def update_profile_list(self):
182+
"""更新配置文件列表"""
183+
profiles = self.manager.get_profile_names()
184+
self.profile_combo['values'] = profiles
185+
if self.current_profile in profiles:
186+
self.profile_var.set(self.current_profile)
187+
188+
def load_current_profile(self):
189+
"""加载当前配置"""
190+
profile = self.manager.get_current_map()
191+
self.name_var.set(self.current_profile)
192+
193+
# 加载低音区
194+
for i in range(1, 8):
195+
var = getattr(self, f"low_{i}_var")
196+
var.set(profile["low_map"].get(str(i), ""))
197+
198+
# 加载中音区
199+
for i in range(1, 8):
200+
var = getattr(self, f"mid_{i}_var")
201+
var.set(profile["mid_map"].get(str(i), ""))
202+
203+
# 加载高音区
204+
for i in range(1, 8):
205+
var = getattr(self, f"high_{i}_var")
206+
var.set(profile["high_map"].get(str(i), ""))
207+
208+
def on_profile_change(self, event):
209+
"""配置文件变更事件"""
210+
self.current_profile = self.profile_var.get()
211+
self.manager.set_current_profile(self.current_profile)
212+
self.load_current_profile()
213+
214+
def create_new_profile(self):
215+
"""创建新配置"""
216+
name = f"custom_{len(self.manager.key_maps) + 1}"
217+
new_profile = DEFAULT_KEY_MAPS["开放空间"].copy()
218+
219+
if self.manager.create_profile(name, new_profile):
220+
self.update_profile_list()
221+
self.profile_var.set(name)
222+
self.on_profile_change(None)
223+
224+
def save_profile(self):
225+
"""保存当前配置"""
226+
profile_data = {
227+
"low_map": {},
228+
"mid_map": {},
229+
"high_map": {}
230+
}
231+
232+
# 收集低音区
233+
for i in range(1, 8):
234+
var = getattr(self, f"low_{i}_var")
235+
key_val = var.get().strip()
236+
if key_val: # 只保存非空的键位
237+
profile_data["low_map"][str(i)] = key_val
238+
239+
# 收集中音区
240+
for i in range(1, 8):
241+
var = getattr(self, f"mid_{i}_var")
242+
key_val = var.get().strip()
243+
if key_val:
244+
profile_data["mid_map"][str(i)] = key_val
245+
246+
# 收集高音区
247+
for i in range(1, 8):
248+
var = getattr(self, f"high_{i}_var")
249+
key_val = var.get().strip()
250+
if key_val:
251+
profile_data["high_map"][str(i)] = key_val
252+
253+
if self.manager.update_profile(self.current_profile, profile_data):
254+
messagebox.showinfo("成功", "配置保存成功!")
255+
256+
def delete_profile(self):
257+
"""删除当前配置"""
258+
if self.current_profile in ["开放空间", "原神"]:
259+
messagebox.showwarning("警告", "不能删除默认配置!")
260+
return
261+
262+
if messagebox.askyesno("确认", f"确定要删除配置 '{self.current_profile}' 吗?"):
263+
if self.manager.delete_profile(self.current_profile):
264+
self.update_profile_list()
265+
self.load_current_profile()
266+
messagebox.showinfo("成功", "配置已删除!")
267+
268+
def apply_changes(self):
269+
"""应用更改"""
270+
self.save_profile()
271+
messagebox.showinfo("成功", "配置已应用!")
272+
273+
274+
def show_key_map_editor():
275+
root = tk.Tk()
276+
manager = CustomKeyMap()
277+
editor = KeyMapEditor(root, manager)
278+
root.mainloop()
279+
280+
281+
if __name__ == "__main__":
282+
show_key_map_editor()

0 commit comments

Comments
 (0)