You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

按键绑定激活后抑制键盘输入并输出剪贴板字符的程序问题求助

自定义按键绑定剪贴板逐字符输出程序问题

需求

  • 设置任意按键绑定后启动程序
  • 可正常进行日常输入操作
  • 激活绑定按键后,所有键盘输入被抑制,每按一次任意键,输出剪贴板内容的下一个字符(例如剪贴板是"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”作为激活按键

解决方案及修改后的代码

核心修改点

  1. 每次激活绑定重新获取剪贴板内容:避免程序启动后剪贴板更新但内容未同步的问题
  2. 去掉反复启停listener的逻辑:改用pynput.keyboard.Controller模拟输入,无需反复重启监听
  3. 增加输出完毕自动停止抑制的逻辑:当剪贴板字符列表为空时自动恢复正常输入
  4. 优化全局变量管理:减少冗余变量,简化逻辑
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 19:51:11