From 778af6c9edf674656711fc75f6aaf6c0988b6035 Mon Sep 17 00:00:00 2001 From: lkjing2021345 <1908499668@qq.com> Date: Thu, 26 Mar 2026 22:28:17 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BA=8C=E6=AC=A1=E5=85=83?= =?UTF-8?q?=E4=BA=BA=E7=89=A9=E8=81=94=E5=8A=A8UI=EF=BC=8C=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E9=94=AE=E7=9B=98=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- key_config,json | 11 +++ key_config.json | 18 +++++ key_overlay.py | 208 ++++++++++++++++++++++++++++++++---------------- 3 files changed, 168 insertions(+), 69 deletions(-) create mode 100644 key_config,json create mode 100644 key_config.json diff --git a/key_config,json b/key_config,json new file mode 100644 index 0000000..308c5de --- /dev/null +++ b/key_config,json @@ -0,0 +1,11 @@ + { + "window_pos": {"x": 100, "y": 100}, + "alpha": 0.8, + "monitored_keys": ["w", "a", "s", "d", "space", "shift"], + "counts": { + "w": 10, + "a": 5, + "s": 0, + "d": 2 + } + } \ No newline at end of file diff --git a/key_config.json b/key_config.json new file mode 100644 index 0000000..1e83fea --- /dev/null +++ b/key_config.json @@ -0,0 +1,18 @@ +{ + "alpha": 0.8, + "position": "+100+100", + "monitored_keys": [ + "w", + "a", + "s", + "d", + "space" + ], + "counts": { + "w": 9, + "a": 0, + "s": 0, + "d": 0, + "space": 0 + } +} \ No newline at end of file diff --git a/key_overlay.py b/key_overlay.py index 8172779..3f55688 100644 --- a/key_overlay.py +++ b/key_overlay.py @@ -3,7 +3,7 @@ from tkinter import simpledialog, messagebox import json import os from pynput import keyboard -import threading +from PIL import Image, ImageTk # 配置文件路径 CONFIG_FILE = "key_config.json" @@ -11,148 +11,218 @@ CONFIG_FILE = "key_config.json" class KeyOverlayApp: def __init__(self): self.root = tk.Tk() - self.root.title("KeyOverlay Py") + self.root.title("KeyOverlay Ultra") - # 初始配置 + # 初始默认配置 self.config = { "alpha": 0.8, "position": "+100+100", "monitored_keys": ["w", "a", "s", "d", "space"], - "counts": {} + "counts": {}, + "ui_mode": "list" # "list" 或 "keyboard" } self.load_config() - # UI 设置 - self.root.overrideredirect(True) # 无边框 - self.root.attributes("-topmost", True) # 置顶 + # 窗口基础设置 + self.root.overrideredirect(True) + self.root.attributes("-topmost", True) self.root.attributes("-alpha", self.config["alpha"]) self.root.geometry(self.config["position"]) self.root.configure(bg='#282c34') - # 标签字典,用于动态更新界面 - self.labels = {} + # 状态变量 + self.key_labels = {} # 存储按键 UI 组件 + self.char_image_label = None + self.idle_photo = None + self.pressed_photo = None + self.active_keys = set() # 当前按下的键 + + # 加载图片 + self.load_images() + + # 初始化 UI self.setup_ui() - # 拖动功能变量 + # 拖动功能 self._drag_data = {"x": 0, "y": 0} self.root.bind("", self.start_drag) self.root.bind("", self.do_drag) # 右键菜单 self.menu = tk.Menu(self.root, tearoff=0) + self.menu.add_command(label="切换样式 (列表/键盘)", command=self.toggle_ui_mode) + self.menu.add_separator() self.menu.add_command(label="添加按键", command=self.add_key_dialog) self.menu.add_command(label="删除按键", command=self.remove_key_dialog) self.menu.add_command(label="设置透明度", command=self.set_alpha_dialog) self.menu.add_command(label="重置计数", command=self.reset_counts) self.menu.add_separator() self.menu.add_command(label="退出并保存", command=self.quit_app) - self.root.bind("", self.show_menu) - # 启动监听线程 - self.listener = keyboard.Listener(on_press=self.on_press) + # 启动监听 (包含按下和松开) + self.listener = keyboard.Listener(on_press=self.on_press, on_release=self.on_release) self.listener.start() self.root.mainloop() + def load_images(self): + """加载人物图片,如果不存在则显示占位符""" + try: + # 假设图片大小调整为 150x150 + size = (150, 150) + if os.path.exists("idle.png"): + img = Image.open("idle.png").convert("RGBA").resize(size) + self.idle_photo = ImageTk.PhotoImage(img) + if os.path.exists("pressed.png"): + img = Image.open("pressed.png").convert("RGBA").resize(size) + self.pressed_photo = ImageTk.PhotoImage(img) + except Exception as e: + print(f"图片加载失败: {e}") + def load_config(self): if os.path.exists(CONFIG_FILE): with open(CONFIG_FILE, "r") as f: try: - saved_config = json.load(f) - self.config.update(saved_config) - except: - pass - # 初始化缺失的计数 + self.config.update(json.load(f)) + except: pass for key in self.config["monitored_keys"]: if key not in self.config["counts"]: self.config["counts"][key] = 0 def save_config(self): - # 保存位置信息 self.config["position"] = f"+{self.root.winfo_x()}+{self.root.winfo_y()}" with open(CONFIG_FILE, "w") as f: json.dump(self.config, f, indent=4) def setup_ui(self): - # 清除旧标签 - for label in self.labels.values(): - label.destroy() - self.labels = {} + """核心 UI 构建,支持两种模式""" + # 清空当前界面 + for widget in self.root.winfo_children(): + if widget != self.menu: widget.destroy() + self.key_labels = {} - # 创建新标签 - for i, key in enumerate(self.config["monitored_keys"]): - count = self.config["counts"].get(key, 0) - lbl = tk.Label(self.root, text=f"{key.upper()}: {count}", - fg="white", bg="#282c34", - font=("Consolas", 12, "bold"), - padx=10, pady=5) - lbl.pack(fill=tk.X) - self.labels[key] = lbl + # 1. 人物显示区域 (始终在最上方) + self.char_image_label = tk.Label(self.root, bg='#282c34') + if self.idle_photo: + self.char_image_label.config(image=self.idle_photo) + else: + self.char_image_label.config(text="(No Image)", fg="gray") + self.char_image_label.pack(pady=5) + + # 2. 按键显示区域 + container = tk.Frame(self.root, bg='#282c34') + container.pack(padx=10, pady=5) + + if self.config["ui_mode"] == "keyboard": + # 键盘样式:网格/横向排列 + for i, key in enumerate(self.config["monitored_keys"]): + frame = tk.Frame(container, bg='#3e4451', highlightbackground="gray", highlightthickness=1) + frame.pack(side=tk.LEFT, padx=2) + + name_lbl = tk.Label(frame, text=key.upper(), fg="white", bg='#3e4451', font=("Consolas", 10)) + name_lbl.pack() + count_lbl = tk.Label(frame, text=str(self.config["counts"].get(key, 0)), fg="#61afef", bg='#3e4451', font=("Consolas", 8)) + count_lbl.pack() + + self.key_labels[key] = {"frame": frame, "name": name_lbl, "count": count_lbl} + else: + # 列表样式 + for key in self.config["monitored_keys"]: + lbl = tk.Label(container, text=f"{key.upper()}: {self.config['counts'].get(key, 0)}", + fg="white", bg='#282c34', font=("Consolas", 12, "bold")) + lbl.pack(fill=tk.X) + self.key_labels[key] = {"label": lbl} def on_press(self, key): - try: - # 尝试获取按键名称 - if hasattr(key, 'char') and key.char is not None: - k = key.char.lower() - else: - k = key.name.lower() - except: - return - + k = self._get_key_str(key) if k in self.config["monitored_keys"]: - self.config["counts"][k] = self.config["counts"].get(k, 0) + 1 - # 在主线程更新 UI - count = self.config["counts"][k] - self.root.after(0, lambda: self.labels[k].config(text=f"{k.upper()}: {count}")) + # 状态更新 + if k not in self.active_keys: + self.config["counts"][k] += 1 + self.active_keys.add(k) + + # UI 实时反馈 + self.root.after(0, lambda: self.update_key_ui(k, True)) - # 拖动逻辑 + def on_release(self, key): + k = self._get_key_str(key) + if k in self.active_keys: + self.active_keys.remove(k) + # UI 恢复反馈 + self.root.after(0, lambda: self.update_key_ui(k, False)) + + def _get_key_str(self, key): + try: + if hasattr(key, 'char') and key.char: return key.char.lower() + return key.name.lower() + except: return None + + def update_key_ui(self, key, is_pressed): + """更新按键高亮和人物图片""" + # 更新人物图片 + if self.char_image_label and self.idle_photo and self.pressed_photo: + if len(self.active_keys) > 0: + self.char_image_label.config(image=self.pressed_photo) + else: + self.char_image_label.config(image=self.idle_photo) + + # 更新按键方块 + if key in self.key_labels: + if self.config["ui_mode"] == "keyboard": + data = self.key_labels[key] + color = "#98c379" if is_pressed else "#3e4451" # 按下变绿 + data["frame"].config(bg=color) + data["name"].config(bg=color) + data["count"].config(bg=color, text=str(self.config["counts"][key])) + else: + lbl = self.key_labels[key]["label"] + lbl.config(text=f"{key.upper()}: {self.config['counts'][key]}", + fg="#98c379" if is_pressed else "white") + + # 基础交互函数 def start_drag(self, event): - self._drag_data["x"] = event.x - self._drag_data["y"] = event.y + self._drag_data["x"], self._drag_data["y"] = event.x, event.y def do_drag(self, event): x = self.root.winfo_x() - self._drag_data["x"] + event.x y = self.root.winfo_y() - self._drag_data["y"] + event.y self.root.geometry(f"+{x}+{y}") - # 菜单功能 def show_menu(self, event): self.menu.post(event.x_root, event.y_root) + def toggle_ui_mode(self): + self.config["ui_mode"] = "keyboard" if self.config["ui_mode"] == "list" else "list" + self.setup_ui() + def add_key_dialog(self): - new_key = simpledialog.askstring("添加按键", "输入按键名称 (如: q, space, shift):") - if new_key: - new_key = new_key.lower() - if new_key not in self.config["monitored_keys"]: - self.config["monitored_keys"].append(new_key) - if new_key not in self.config["counts"]: - self.config["counts"][new_key] = 0 + nk = simpledialog.askstring("添加", "按键名:") + if nk: + nk = nk.lower() + if nk not in self.config["monitored_keys"]: + self.config["monitored_keys"].append(nk) + self.config["counts"][nk] = 0 self.setup_ui() def remove_key_dialog(self): - key_to_remove = simpledialog.askstring("删除按键", "输入要删除的按键名称:") - if key_to_remove and key_to_remove.lower() in self.config["monitored_keys"]: - self.config["monitored_keys"].remove(key_to_remove.lower()) + rk = simpledialog.askstring("删除", "按键名:") + if rk and rk.lower() in self.config["monitored_keys"]: + self.config["monitored_keys"].remove(rk.lower()) self.setup_ui() def set_alpha_dialog(self): - new_alpha = simpledialog.askfloat("设置透明度", "输入透明度 (0.1 - 1.0):", minvalue=0.1, maxvalue=1.0) - if new_alpha: - self.config["alpha"] = new_alpha - self.root.attributes("-alpha", new_alpha) + a = simpledialog.askfloat("透明度", "0.1-1.0:", minvalue=0.1, maxvalue=1.0) + if a: self.root.attributes("-alpha", a); self.config["alpha"] = a def reset_counts(self): - if messagebox.askyesno("重置", "确定要清空所有按键计数吗?"): - for key in self.config["counts"]: - self.config["counts"][key] = 0 + if messagebox.askyesno("重置", "清空所有计数?"): + for k in self.config["counts"]: self.config["counts"][k] = 0 self.setup_ui() def quit_app(self): - self.save_config() - self.root.destroy() - os._exit(0) + self.save_config(); self.root.destroy(); os._exit(0) if __name__ == "__main__": KeyOverlayApp()