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

Windows下Python按键时捕获设备ID及多键盘区分问题求助

区分Windows下主键盘与外接数字键盘的解决方案

我完全懂你的困扰——在Windows上用keyboard库的时候,KeyboardEvent.device始终返回None,根本没法区分主键盘和外接数字键盘,这直接卡了宏程序的核心功能。其实这个问题的根源是keyboard库在Windows平台的底层实现没有暴露设备识别的相关信息,所以得换个思路来处理。

下面给你两个可行的方案,从简单到进阶,你可以根据自己的技术能力选择:

方案一:用inputs库快速实现设备区分

inputs库是专门针对Windows和Linux的输入设备监听工具,它封装了Windows的Raw Input API,能直接获取每个按键对应的设备信息。操作起来很简单:

  1. 先安装库:
pip install inputs
  1. 示例代码(监听外接数字键盘的按键):
from inputs import get_keyboard_events
from pynput.mouse import Button, Controller
import time

mouse = Controller()
tab_pos = (1639, 16)

# 先枚举所有键盘设备,找到你的外接数字键盘的标识(比如设备名称)
def list_keyboards():
    for device in inputs.devices.keyboards:
        print(f"键盘设备: {device.name}, 句柄: {device._device_handle}")

# 先运行一次这个函数,找到你的外接数字键盘的名称
list_keyboards()

# 假设你的外接数字键盘名称包含"Numeric Keypad"(实际根据你输出的结果修改)
TARGET_KEYBOARD_NAME = "你的外接数字键盘名称"

def handle_key_event(event):
    # 只处理按下事件,且来自目标键盘
    if event.ev_type == "Key" and event.state == 1 and event.device.name == TARGET_KEYBOARD_NAME:
        # 这里判断具体按键,比如外接键盘的"A"键(根据实际键值调整)
        if event.code == "KEY_A":
            prev_pos = mouse.position
            mouse.position = tab_pos
            mouse.click(Button.left, 1)
            mouse.position = prev_pos
            print("触发外接键盘的宏操作")

while True:
    try:
        for event in get_keyboard_events():
            handle_key_event(event)
    except Exception as e:
        print(f"出错了: {e}")
        time.sleep(0.1)

这个方案的好处是不用写复杂的底层代码,inputs已经帮你封装好了设备识别的逻辑,你只需要找到目标键盘的名称就行。

方案二:直接调用Windows Raw Input API(进阶)

如果你需要更精细的控制,或者不想依赖第三方库,可以用ctypes直接调用Windows的Raw Input API。这个方法需要你了解一点Windows API的基础知识,但能完全掌控设备识别的过程。

核心步骤是:

  • 注册Raw Input设备,告诉系统你要监听键盘输入
  • 处理WM_INPUT消息,解析输入数据中的设备ID
  • 对比设备ID,区分主键盘和外接键盘

这里给你一个简化的示例框架:

import ctypes
from ctypes import wintypes
from pynput.mouse import Button, Controller

mouse = Controller()
tab_pos = (1639, 16)

# 定义Windows Raw Input的相关结构体和函数
user32 = ctypes.WinDLL('user32', use_last_error=True)

class RAWINPUTDEVICE(ctypes.Structure):
    _fields_ = [
        ("usUsagePage", wintypes.WORD),
        ("usUsage", wintypes.WORD),
        ("dwFlags", wintypes.DWORD),
        ("hwndTarget", wintypes.HWND)
    ]

class RAWINPUTHEADER(ctypes.Structure):
    _fields_ = [
        ("dwType", wintypes.DWORD),
        ("dwSize", wintypes.DWORD),
        ("hDevice", wintypes.HANDLE),
        ("wParam", wintypes.WPARAM)
    ]

# 注册键盘设备
RIDEV_INPUTSINK = 0x00000100
kbd_device = RAWINPUTDEVICE()
kbd_device.usUsagePage = 0x01
kbd_device.usUsage = 0x06
kbd_device.dwFlags = RIDEV_INPUTSINK
kbd_device.hwndTarget = user32.GetForegroundWindow()

user32.RegisterRawInputDevices(ctypes.byref(kbd_device), 1, ctypes.sizeof(kbd_device))

# 获取设备名称的函数
def get_device_name(hDevice):
    buf_size = wintypes.DWORD(0)
    user32.GetRawInputDeviceInfoW(hDevice, 0x20000007, None, ctypes.byref(buf_size))
    buf = ctypes.create_unicode_buffer(buf_size.value)
    user32.GetRawInputDeviceInfoW(hDevice, 0x20000007, buf, ctypes.byref(buf_size))
    return buf.value

# 监听消息循环
msg = wintypes.MSG()
while user32.GetMessageW(ctypes.byref(msg), None, 0, 0) != 0:
    if msg.message == 0x00FF:  # WM_INPUT
        hRawInput = msg.lParam
        header_size = ctypes.sizeof(RAWINPUTHEADER)
        buf = ctypes.create_string_buffer(header_size)
        user32.GetRawInputData(hRawInput, 0x10000003, buf, ctypes.byref(wintypes.DWORD(header_size)), ctypes.sizeof(RAWINPUTHEADER))
        header = RAWINPUTHEADER.from_buffer(buf)
        device_name = get_device_name(header.hDevice)
        
        # 判断是否是目标外接键盘
        if "你的外接数字键盘名称" in device_name:
            # 这里可以进一步解析按键数据,判断具体按下的键
            print("来自外接键盘的输入")
            # 执行宏操作
            prev_pos = mouse.position
            mouse.position = tab_pos
            mouse.click(Button.left, 1)
            mouse.position = prev_pos
    
    user32.TranslateMessage(ctypes.byref(msg))
    user32.DispatchMessageW(ctypes.byref(msg))

这个方案更底层,适合需要高度定制的场景,但需要你根据自己的设备调整设备名称的判断逻辑。

小提示

  • 不管用哪个方案,第一步都要先找到你的外接数字键盘的设备标识(名称或ID),可以通过设备管理器或者代码枚举的方式获取。
  • 如果你用inputs库遇到权限问题,试试以管理员身份运行脚本。

内容的提问来源于stack exchange,提问作者Emanuel Elliott

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 12:22:34