Repository navigation
Expand file tree
/
Copy pathMouseMapper.py
More file actions
265 lines (229 loc) · 10.6 KB
/
Copy pathMouseMapper.py
File metadata and controls
265 lines (229 loc) · 10.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
import threading
import time
import tkinter as tk
from pathlib import Path
from tkinter import ttk, messagebox
import keyboard
import pyautogui
WIN_WIDTH = 720
WIN_HEIGHT = 510
VERSION = "1.1"
class MouseClickerApp:
def __init__(self, master):
self.master = master
self.master.title(f"鼠标映射器 - v{VERSION}")
# 状态变量
self.is_enabled = False # 记录是否启动
self.running = True # 用于守护线程
self.left_key = None
self.right_key = None
self.left_pressed = False
self.right_pressed = False
self.click_mode = tk.StringVar(value="click")
self.modifier_var = tk.StringVar(value="ctrl") # 默认 Ctrl+<Key>
self.hotkeys_registered = {}
# 可用的键盘按键列表
self.available_keys = [
'Space', 'Enter',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12'
]
self.create_widgets()
self.dummy_thread() # 保持主线程不退出
self.master.protocol("WM_DELETE_WINDOW", self.on_close) # 重写关闭操作
def create_widgets(self):
"""GUI布局"""
main = tk.Frame(self.master, padx=20, pady=15)
main.pack(fill=tk.BOTH, expand=True)
tk.Label(main, text="鼠标按键映射器", font=("黑体", 16)).pack(pady=(0, 12))
# 1. 下拉框选择映射键
combo_line = tk.Frame(main)
combo_line.pack(fill=tk.X, pady=6)
tk.Label(combo_line, text="鼠标左键映射:", font=("黑体", 11)).pack(side=tk.LEFT, padx=(0, 6))
self.left_combo = ttk.Combobox(combo_line, values=self.available_keys, width=10,
state="readonly", font=("黑体", 11))
self.left_combo.pack(side=tk.LEFT)
self.left_combo.bind("<<ComboboxSelected>>", self.on_left_key_selected)
self.right_combo = ttk.Combobox(combo_line, values=self.available_keys, width=10,
state="readonly", font=("黑体", 11))
self.right_combo.pack(side=tk.RIGHT)
tk.Label(combo_line, text="鼠标右键映射:", font=("黑体", 11)).pack(side=tk.RIGHT, padx=(20, 6))
self.right_combo.bind("<<ComboboxSelected>>", self.on_right_key_selected)
# 2. 快捷键组合
mod_frame = tk.LabelFrame(main, text="快捷键组合", font=("黑体", 11), padx=10, pady=8)
mod_frame.pack(fill=tk.X, pady=8)
tk.Radiobutton(mod_frame, text="直接采用设定按键", variable=self.modifier_var, cursor="hand2",
value="none", command=self.on_modifier_changed, font=("黑体", 11)).pack(anchor=tk.W)
r2r3_frame = tk.Frame(mod_frame)
r2r3_frame.pack(fill=tk.X, pady=4)
for col, (txt, val) in enumerate([("Ctrl+", "ctrl"), ("Ctrl+Shift+", "ctrl+shift"), ("Ctrl+Alt+", "ctrl+alt")]):
tk.Radiobutton(r2r3_frame, text=txt + "<Key>", variable=self.modifier_var,
value=val, command=self.on_modifier_changed, cursor="hand2",
font=("黑体", 11)).grid(row=0, column=col, sticky="w", padx=(0, 25))
for col, (txt, val) in enumerate([("Alt+", "alt"), ("Shift+", "shift"), ("Alt+Shift+", "alt+shift")]):
tk.Radiobutton(r2r3_frame, text=txt + "<Key>", variable=self.modifier_var,
value=val, command=self.on_modifier_changed, cursor="hand2",
font=("黑体", 11)).grid(row=1, column=col, sticky="w", padx=(0, 25))
for col in range(3): # 让每列对齐
r2r3_frame.grid_columnconfigure(col, weight=1, uniform="a")
# 3. 动作模式
mode_frame = tk.LabelFrame(main, text="鼠标动作模式", font=("黑体", 11), padx=10, pady=6)
mode_frame.pack(fill=tk.X, pady=8)
tk.Radiobutton(mode_frame, text="点击模式(每按下一次快捷键,进行一次完整点击)", variable=self.click_mode,
value="click", font=("黑体", 11), cursor="hand2").pack(anchor=tk.W)
tk.Radiobutton(mode_frame, text="切换模式(按下快捷键后,切换“按住”“释放”状态)", variable=self.click_mode,
value="toggle", font=("黑体", 11), cursor="hand2").pack(anchor=tk.W)
# 4. 显示快捷键
status_line = tk.Frame(main)
status_line.pack(fill=tk.X, pady=6)
self.left_status = tk.Label(status_line, text="左键: 未设置", fg="red", font=("黑体", 11))
self.left_status.pack(side=tk.LEFT, padx=(0, 25))
self.right_status = tk.Label(status_line, text="右键: 未设置", fg="red", font=("黑体", 11))
self.right_status.pack(side=tk.LEFT)
# 5. 按钮行
btn_line = tk.Frame(main)
btn_line.pack(pady=10)
self.enable_btn = tk.Button(btn_line, text="启 用", width=12, font=("黑体", 12, "bold"),
fg="green", cursor="hand2", command=self.toggle_enable)
self.enable_btn.pack(side=tk.LEFT, padx=(0, 15))
tk.Button(btn_line, text="清除设置", width=10, font=("黑体", 11), cursor="hand2",
command=self.clear_settings).pack(side=tk.LEFT)
# ---------- 6. 版权 ----------
tk.Label(main, text=f"JupiterLyr © Version {VERSION}", font=("Times New Roman", 9)).pack(pady=(15, 0))
def toggle_enable(self):
"""切换启停状态"""
if self.is_enabled:
self.do_stop()
else:
self.do_start()
self.update_enable_button()
def do_start(self):
"""真正启用"""
if not self.left_key and not self.right_key:
messagebox.showwarning("提示", "请先设置左键或右键映射!")
return
self.register_current_keys()
self.is_enabled = True
def do_stop(self):
"""真正停止"""
self.unregister_all()
self.is_enabled = False
self.release_mouse()
def update_enable_button(self):
self.enable_btn.config(text="停 止" if self.is_enabled else "启 用", fg="red" if self.is_enabled else "green")
def register_current_keys(self):
"""按当前设置注册热键"""
self.unregister_all()
mod = self.modifier_var.get()
if self.left_key:
hotkey = self._build_hotkey(mod, self.left_key)
keyboard.add_hotkey(hotkey, self.handle_left_click, suppress=True)
self.hotkeys_registered['left'] = hotkey
if self.right_key:
hotkey = self._build_hotkey(mod, self.right_key)
keyboard.add_hotkey(hotkey, self.handle_right_click, suppress=True)
self.hotkeys_registered['right'] = hotkey
self.update_status()
def unregister_all(self):
"""注销所有已注册热键"""
for hk in self.hotkeys_registered.values():
keyboard.remove_hotkey(hk)
self.hotkeys_registered.clear()
@staticmethod
def _build_hotkey(mod, key):
"""若使用原始设定按键,返回按键本身;否则与组合键联合返回"""
return key if mod == "none" else f"{mod}+{key}"
def on_modifier_changed(self):
if self.is_enabled:
self.register_current_keys()
def on_left_key_selected(self, _):
k = self.left_combo.get()
if k == self.right_key:
messagebox.showwarning("警告", "该主键已被右键使用!")
self.left_combo.set("")
return
self.left_key = k
if self.is_enabled:
self.register_current_keys()
def on_right_key_selected(self, _):
k = self.right_combo.get()
if k == self.left_key:
messagebox.showwarning("警告", "该主键已被左键使用!")
self.right_combo.set("")
return
self.right_key = k
if self.is_enabled:
self.register_current_keys()
def update_status(self):
mod = self.modifier_var.get().replace('+', '+').title()
if self.left_key:
self.left_status.config(text=f"左键: {self._build_hotkey(mod, self.left_key)}", foreground="green")
else:
self.left_status.config(text="左键: 未设置", foreground="red")
if self.right_key:
self.right_status.config(text=f"右键: {self._build_hotkey(mod, self.right_key)}", foreground="green")
else:
self.right_status.config(text="右键: 未设置", foreground="red")
def handle_left_click(self):
if self.click_mode.get() == "toggle":
if not self.left_pressed:
pyautogui.mouseDown(button='left')
self.left_pressed = True
else:
pyautogui.mouseUp(button='left')
self.left_pressed = False
else:
pyautogui.click(button='left')
def handle_right_click(self):
if self.click_mode.get() == "toggle":
if not self.right_pressed:
pyautogui.mouseDown(button='right')
self.right_pressed = True
else:
pyautogui.mouseUp(button='right')
self.right_pressed = False
else:
pyautogui.click(button='right')
def clear_settings(self):
self.left_combo.set("")
self.right_combo.set("")
self.left_key = None
self.right_key = None
if self.is_enabled:
self.register_current_keys()
else:
self.update_status()
self.release_mouse()
def release_mouse(self):
"""释放鼠标"""
if self.left_pressed:
pyautogui.mouseUp(button='left')
self.left_pressed = False
if self.right_pressed:
pyautogui.mouseUp(button='right')
self.right_pressed = False
def dummy_thread(self):
"""线程监听"""
def _run():
while self.is_enabled:
time.sleep(0.2)
threading.Thread(target=_run, daemon=True).start()
def on_close(self):
"""窗口关闭时清理"""
self.do_stop()
self.is_enabled = False
self.master.quit()
def start(self):
self.master.mainloop()
if __name__ == "__main__":
root = tk.Tk()
root.geometry(str(WIN_WIDTH) + "x" + str(WIN_HEIGHT))
root.resizable(False, False)
try:
root.iconbitmap(Path(__file__).parent / 'resource/icon2.png')
except Exception as e:
messagebox.showwarning("Warning", f"The icon file not found!\n{e}")
app = MouseClickerApp(root)
app.start()