按键绑定激活后抑制键盘输入并输出剪贴板字符的程序问题求助
自定义按键绑定剪贴板逐字符输出程序问题
需求
- 设置任意按键绑定后启动程序
- 可正常进行日常输入操作
- 激活绑定按键后,所有键盘输入被抑制,每按一次任意键,输出剪贴板内容的下一个字符(例如剪贴板是"The cat",按"j"输出"T",按"7"输出"h")
- 按ESC可随时取消抑制;剪贴板内容输出完毕后自动关闭抑制并恢复正常输入
当前代码
import tkinter as tk from pynput import keyboard import threading import pyperclip # For accessing the clipboard import pyautogui # Global variables block_keys = False selected_key = None keyboard_listener = None clipboard_content = pyperclip.paste() # Get the clipboard content listClipboard = [char for char in clipboard_content] print(listClipboard) # Function to listen for the keybind and start blocking def listen_for_keybind(): def on_press_keybind(key): global block_keys if key == selected_key: print(f"Keybind {selected_key} activated") global char_index char_index = 0 # Reset character index block_keys = True # Start blocking keys start_blocking_listener() # Start blocking key presses when keybind is activated return False # Stop the keybind listener once the keybind is pressed with keyboard.Listener(on_press=on_press_keybind) as keybind_listener: keybind_listener.join() controller = keyboard.Controller() # Function to start the listener for blocking all key presses def start_blocking_listener(): global keyboard_listener, listener # Function to handle key presses # def on_press(key): global listClipboard, keyboard_listener, listener print(f"Key pressed, not registered: {key}") if str(key) == "Key.esc": stop_blocking() return if listClipboard: keyboard_listener.stop() keyboard_listener = None pyautogui.typewrite(listClipboard[0]) print(f"Typed: {listClipboard[0]}") listClipboard.pop(0) keyboard_listener = keyboard.Listener(on_press=on_press, suppress=True) keyboard_listener.start() # Disable keyboard events and print a message on key press keyboard_listener = keyboard.Listener(on_press=on_press, suppress=True) keyboard_listener.start() def stop_blocking(): global keyboard_listener global block_keys block_keys = False if keyboard_listener is not None: keyboard_listener.stop() print("Blocking disabled, typing enabled") # Function to set the keybind def set_keybind(): add_button.config(text="Press any key...", state="disabled") def on_key_press(key): global selected_key selected_key = key add_button.config(text=f"Keybind set to: {key}", state="normal") listener.stop() global listener listener = keyboard.Listener(on_press=on_key_press) listener.start() # Function to activate the keybind listener def activate_keybind(): if selected_key: root.withdraw() # Minimize the window # Start a separate thread to listen for the keybind activation thread = threading.Thread(target=listen_for_keybind) thread.daemon = True thread.start() else: tk.messagebox.showerror("Error", "Please set a keybind first.") # Function to quit the application def quit_app(): stop_blocking() root.quit() # Set up the GUI root = tk.Tk() root.title("Keybind Blocking App") label = tk.Label(root, text="Click the button below to set the keybind:") label.pack(pady=10) add_button = tk.Button(root, text="Add Keybind", command=set_keybind) add_button.pack(pady=5) set_button = tk.Button(root, text="Start Blocking", command=activate_keybind) set_button.pack(pady=5) quit_button = tk.Button(root, text="Quit", command=quit_app) quit_button.pack(pady=5) root.mainloop()
遇到的问题
- 尝试通过启停listener来避免模拟输入被识别为键盘输入,但仍无法连续输出剪贴板的下一个字符
- 存在未设置按键绑定就启动程序的bug,目前仅测试过以“0”作为激活按键
解决方案及修改后的代码
核心修改点
- 每次激活绑定重新获取剪贴板内容:避免程序启动后剪贴板更新但内容未同步的问题
- 去掉反复启停listener的逻辑:改用
pynput.keyboard.Controller模拟输入,无需反复重启监听 - 增加输出完毕自动停止抑制的逻辑:当剪贴板字符列表为空时自动恢复正常输入
- 优化全局变量管理:减少冗余变量,简化逻辑
import tkinter as tk from pynput import keyboard import threading import pyperclip from tkinter import messagebox # Global variables selected_key = None keyboard_listener = None clipboard_chars = [] controller = keyboard.Controller() def listen_for_keybind(): def on_press(key): nonlocal selected_key if key == selected_key: print(f"Keybind {selected_key} activated") # 激活时重新获取剪贴板最新内容 global clipboard_chars clipboard_content = pyperclip.paste() clipboard_chars = list(clipboard_content) print(f"Loaded clipboard content: {clipboard_chars}") start_blocking_listener() return False # 激活后停止按键绑定监听 with keyboard.Listener(on_press=on_press) as listener: listener.join() def start_blocking_listener(): global keyboard_listener def on_press(key): global clipboard_chars print(f"Key pressed, suppressed: {key}") # 按ESC取消抑制 if key == keyboard.Key.esc: stop_blocking() return # 输出剪贴板下一个字符 if clipboard_chars: char = clipboard_chars.pop(0) print(f"Typed: {char}") # 使用pynput控制器模拟输入,避免被自身监听捕获 controller.type(char) # 内容输出完毕自动停止抑制 if not clipboard_chars: stop_blocking() print("Clipboard content exhausted, blocking disabled") # 创建监听并抑制所有按键输入 keyboard_listener = keyboard.Listener(on_press=on_press, suppress=True) keyboard_listener.start() def stop_blocking(): global keyboard_listener if keyboard_listener and keyboard_listener.is_alive(): keyboard_listener.stop() keyboard_listener = None print("Blocking disabled, typing restored") def set_keybind(): add_button.config(text="Press any key...", state="disabled") def on_key_press(key): global selected_key selected_key = key add_button.config(text=f"Keybind set to: {key}", state="normal") listener.stop() global listener listener = keyboard.Listener(on_press=on_key_press) listener.start() def activate_keybind(): if selected_key: root.withdraw() # 启动按键绑定监听线程 thread = threading.Thread(target=listen_for_keybind, daemon=True) thread.start() else: messagebox.showerror("Error", "Please set a keybind first.") def quit_app(): stop_blocking() root.quit() # GUI设置 root = tk.Tk() root.title("Clipboard Typing Tool") label = tk.Label(root, text="Set a keybind to activate clipboard typing:") label.pack(pady=10) add_button = tk.Button(root, text="Add Keybind", command=set_keybind) add_button.pack(pady=5) start_button = tk.Button(root, text="Start Listening", command=activate_keybind) start_button.pack(pady=5) quit_button = tk.Button(root, text="Quit", command=quit_app) quit_button.pack(pady=5) root.mainloop()
修改说明
- 移除冗余的
block_keys变量,直接通过listener的状态判断抑制状态 - 激活绑定后才获取剪贴板内容,保证使用最新的剪贴板数据
- 替换
pyautogui为pynput.keyboard.Controller,避免模拟输入被自身监听捕获,无需反复启停listener - 增加剪贴板内容耗尽后自动停止抑制的逻辑
- 优化GUI文本和标题,更贴合功能定位
内容的提问来源于stack exchange,提问作者Judah Starkenburg
相关产品推荐
相关产品推荐

