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

数字小键盘按键截获显示异常,求助完善PyQt6+pynput程序

解决小键盘按键显示错误问题

问题根源

你的程序通过key.vk直接转换为字符,但小键盘按键的虚拟键码(VK)和主键盘字符的ASCII码不匹配。比如小键盘1的VK码是97(对应ASCII的'a'),所以会显示成a而非数字1,其他小键盘符号同理。

修复方案

  1. 新增小键盘VK码到对应字符的映射字典,覆盖数字、运算符号和小数点
  2. 在按键处理逻辑中优先判断是否为小键盘按键,匹配正确的显示字符
  3. 保留原有组合键(Ctrl/Shift/Alt)的处理逻辑,确保小键盘按键和组合键搭配时也能正确显示

修改后的完整代码

from PyQt6 import QtWidgets, QtCore
from PyQt6.QtGui import QFont
from pynput import keyboard, mouse


class KeyLoggerApp(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self.ctrl_pressed = False
        # 小键盘虚拟键码到对应字符的映射(Windows系统)
        self.numpad_vk_map = {
            96: '0', 97: '1', 98: '2', 99: '3', 100: '4',
            101: '5', 102: '6', 103: '7', 104: '8', 105: '9',
            111: '/', 106: '*', 109: '-', 107: '+', 110: '.'
        }

        self.setMinimumSize(400, 300)
        font = QFont()
        font.setPointSize(18)

        self.layout = QtWidgets.QVBoxLayout()
        self.ctrl_label = QtWidgets.QLabel(self)
        self.ctrl_label.setFont(font)
        self.shift_label = QtWidgets.QLabel(self)
        self.shift_label.setFont(font)
        self.alt_label = QtWidgets.QLabel(self)
        self.alt_label.setFont(font)
        self.mouse_left_label = QtWidgets.QLabel(self)
        self.mouse_left_label.setFont(font)
        self.mouse_middle_label = QtWidgets.QLabel(self)
        self.mouse_middle_label.setFont(font)
        self.mouse_right_label = QtWidgets.QLabel(self)
        self.mouse_right_label.setFont(font)
        self.key_label = QtWidgets.QLabel(self)
        self.key_label.setFont(font)

        self.layout.addWidget(self.ctrl_label)
        self.layout.addWidget(self.shift_label)
        self.layout.addWidget(self.alt_label)
        self.layout.addWidget(self.mouse_left_label)
        self.layout.addWidget(self.mouse_middle_label)
        self.layout.addWidget(self.mouse_right_label)
        self.layout.addWidget(self.key_label)

        self.central_widget = QtWidgets.QWidget()
        self.central_widget.setLayout(self.layout)
        self.setCentralWidget(self.central_widget)

        self.toggle_button = QtWidgets.QPushButton("Toggle Always on Top", self)
        self.toggle_button.clicked.connect(self.toggle_on_top)
        self.addToolBar(QtCore.Qt.ToolBarArea.TopToolBarArea, self.create_toolbar())

        self.key_listener = keyboard.Listener(
            on_press=self.on_key_press,
            on_release=self.on_key_release)
        self.key_listener.start()

        self.mouse_listener = mouse.Listener(
            on_click=self.on_mouse_click,
            on_release=self.on_mouse_release)
        self.mouse_listener.start()

    def create_toolbar(self):
        toolbar = QtWidgets.QToolBar()
        toolbar.addWidget(self.toggle_button)
        return toolbar

    def toggle_on_top(self):
        if self.windowFlags() & QtCore.Qt.WindowType.WindowStaysOnTopHint:
            self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowType.WindowStaysOnTopHint)
        else:
            self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowType.WindowStaysOnTopHint)
        self.show()

    def on_key_press(self, key):
        if key == keyboard.Key.ctrl_l or key == keyboard.Key.ctrl_r:
            self.ctrl_label.setText('Ctrl pressed')
        elif key == keyboard.Key.shift:
            self.shift_label.setText('Shift pressed')
        elif key == keyboard.Key.alt_l or key == keyboard.Key.alt_r:
            self.alt_label.setText('Alt pressed')
        elif key == keyboard.Key.space:
            self.key_label.setText('Space pressed')
        else:
            try:
                if isinstance(key, keyboard.KeyCode):
                    # 优先处理小键盘按键
                    if key.vk in self.numpad_vk_map:
                        self.key_label.setText("'{0}' pressed".format(self.numpad_vk_map[key.vk]))
                    elif key.char is not None and ord(key.char) < 32:
                        self.key_label.setText("'{0}' pressed".format(chr(ord(key.char) + 64).upper()))
                    elif (key.vk >= 48 and key.vk <= 57) or (key.vk >= 65 and key.vk <= 90) or (
                            key.vk >= 97 and key.vk <= 122):
                        self.key_label.setText("'{0}' pressed".format(chr(key.vk)))
                    else:
                        key_char = self.convert_to_english_layout(key.char)
                        self.key_label.setText("alphanumeric key '{0}' pressed".format(key_char.upper()))
            except AttributeError:
                self.key_label.setText("special key '{0}' pressed".format(str(key).upper()))

    def on_key_release(self, key):
        if key == keyboard.Key.ctrl_l or key == keyboard.Key.ctrl_r:
            self.ctrl_label.clear()
        elif key == keyboard.Key.shift:
            self.shift_label.clear()
        elif key == keyboard.Key.alt_l or key == keyboard.Key.alt_r:
            self.alt_label.clear()
        else:
            if isinstance(key, keyboard.KeyCode):
                # 优先处理小键盘按键释放
                if key.vk in self.numpad_vk_map:
                    self.key_label.setText("{0} released".format(self.numpad_vk_map[key.vk]))
                elif key.char is not None and ord(key.char) < 32:
                    self.key_label.setText("{0} released".format(str(chr(ord(key.char) + 64).upper())))
                elif (key.vk >= 48 and key.vk <= 57) or (key.vk >= 65 and key.vk <= 90) or (
                        key.vk >= 97 and key.vk <= 122):
                    self.key_label.setText("{0} released".format(chr(key.vk)))
                else:
                    key_char = self.convert_to_english_layout(key.char)
                    if key_char is not None:
                        self.key_label.setText("{0} released".format(key_char.upper()))
                    else:
                        self.key_label.setText("None released")
            elif key == keyboard.Key.space:
                self.key_label.setText('Space released')
            else:
                self.key_label.setText("{0} released".format(str(key).upper()))

    def convert_to_english_layout(self, char):
        uk_to_en = {
            'й': 'q', 'ц': 'w', 'у': 'e', 'к': 'r', 'е': 't', 'н': 'y', 'г': 'u', 'ш': 'i', 'щ': 'o', 'з': 'p',
            'х': '[', 'ї': ']', 'ф': 'a', 'і': 's', 'в': 'd', 'а': 'f', 'п': 'g', 'р': 'h', 'о': 'j', 'л': 'k',
            'д': 'l', 'ж': ';', 'є': '\'', 'я': 'z', 'ч': 'x', 'с': 'c', 'м': 'v', 'и': 'b', 'т': 'n', 'ь': 'm',
            'б': ',', 'ю': '.', 'ґ': '`'
        }

        if char is not None:
            char = char.lower()

        return uk_to_en.get(char, char)

    def on_mouse_click(self, x, y, button, pressed):
        if button == mouse.Button.left:
            self.mouse_left_label.setText('Mouse left button pressed' if pressed else '')
        elif button == mouse.Button.middle:
            self.mouse_middle_label.setText('Mouse middle button pressed' if pressed else '')
        elif button == mouse.Button.right:
            self.mouse_right_label.setText('Mouse right button pressed' if pressed else '')

    def on_mouse_release(self, x, y, button):
        if button == mouse.Button.left:
            self.mouse_left_label.clear()
        elif button == mouse.Button.middle:
            self.mouse_middle_label.clear()
        elif button == mouse.Button.right:
            self.mouse_right_label.clear()

if __name__ == "__main__":
    app = QtWidgets.QApplication([])
    window = KeyLoggerApp()
    window.show()
    app.exec()

说明

  • 新增了self.numpad_vk_map字典,对应Windows系统中小键盘按键的虚拟键码和实际显示字符
  • 在on_key_press和on_key_release方法中,优先判断按键是否属于小键盘,匹配正确的显示内容
  • 保留了原有功能的完整性,包括组合键检测、鼠标按键监控和输入法布局转换

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 00:45:57