检测键盘输入并支持英语等其他语言

Mic*_*yev 3 python utf-8 listener python-3.x pynput

我想检测 python 中的英语和希伯来语击键,我使用的是 Python 版本 3.7。我已经尝试了很多方法,但没有任何效果。问题是,无论我做什么,它都无法识别英语以外的语言的字母。如果我用希伯来语书写,它仍然会以英语出现在文本文件中。我需要即使我按 Alt+Shift 将输入更改为另一种语言,它也会检测到该语言。

重要提示:我需要 Windows 版本。

假设这个简单的例子:

# coding: utf-8
from pynput import keyboard
    
def on_press(key):
    with open('keys.txt', 'a',encoding ='utf-8') as file:
        file.write("{0}\n".format( str(key)))
       
with keyboard.Listener(on_press=on_press) as listener:
    listener.join()
Run Code Online (Sandbox Code Playgroud)

谢谢。

Jos*_*efZ 5

    \n
  1. ToUnicodeEx您需要使用函数将指定的虚拟键代码和键盘状态转换为相应的一个或多个 Unicode 字符。
  2. \n
  3. 为此(主要参见下面代码的第一部分def ToUn),您需要知道活动输入区域设置标识符(以前称为键盘布局)。这不是一件容易的事,它的解决方案是基于我自己的研究,请参阅def get_current_keyboard_layout(). 主要问题是该GetKeyboardLayout函数仅在某些 shell/终端下为当前线程返回正确的值(例如在Python\xc2\xa0IDLEVisual\xc2\xa0Studio\xc2\xa0CodeVisual\xc2\xa0Studio\xc2\xa02019下,\ xe2\x80\xa6),如果从Run对话框 ( WinKey+ R) 或cmd.exe从调用帮助程序pwsh.exe进程的任何应用程序启动,则会失败。conhost.exe
  4. \n
  5. 原始代码经过改进,可提供大量详细的输出。
  6. \n
\n

您可以在以下(部分注释)脚本中轻松识别上述三个部分:

\n
import os\nimport sys\n\nif os.name != \'nt\':\n    raise Exception("IMPORTANT: I need the Windows version.") \n_verbose = ((len(sys.argv) > 1) and bool(sys.argv[1]))\n\n### Translate the specified virtual-key code and keyboard state\n#   to the corresponding Unicode character or characters.\n#   learn.microsoft.com/en-gb/windows/win32/api/winuser/nf-winuser-tounicodeex\n### Adapted from\n#   the solution to /sf/ask/2675699421/\n#   by /sf/users/16498891/\n###\n\nfrom ctypes import (\n    WinDLL, POINTER, create_string_buffer, create_unicode_buffer,\n    c_int32, c_uint, c_uint, c_char, c_wchar, c_int, c_uint, c_void_p\n    )\n_ToUnicodeEx = WinDLL(\'user32\').ToUnicodeEx\n_ToUnicodeEx.argtypes = [\n        c_uint,           # wVirtKey   virtual-key code to be translated\n        c_uint,           # wScanCode  hardware scan code of \xcb\x99wVirtKey\xcb\x99\n        POINTER(c_char),  # lpKeyState current keyboard state (256-byte array)\n        POINTER(c_wchar), # pwszBuff   buffer that receives translated chars\n        c_int,            # cchBuff    size of the `pwszBuff` buffer (in chars)\n        c_uint,           # wFlags     behavior of the function\n        c_void_p          # dwhkl      input locale identifier\n]\n_ToUnicodeEx.restype = c_int\n\n\ndef ToUn(vk,sc,wfl,hkid):\n    kst = create_string_buffer(256)\n    b = create_unicode_buffer(5)\n    if _ToUnicodeEx(vk,sc,kst,b,5,wfl,hkid):\n        return b.value\n    else:\n        return chr( 0xFFFD) # Replacement Character\n\n\n### Retrieve the active input locale identifier\n#   (formerly called the keyboard layout)\n#   https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeyboardlayout\n###\n#   Method based on my own research; non-optimized, debugged on Windows 10\xe2\x80\xa6 \n###\n\nfrom ctypes import WinDLL\nuser32 = WinDLL(\'user32\', use_last_error=True)\n\n\ndef list_parents(pid, proclist):\n    \'\'\'For verbose output\'\'\'\n    aux = [_ for _ in proclist if _[0] == pid]\n    if len( aux) > 0:\n        auxcon = [x for x in proclist if (\n                x[1] == aux[0][0] and x[2] == "conhost.exe")]\n        list_parents(aux[0][1], proclist)\n        print(\'parent\', aux[0], auxcon if (len(auxcon) == 0) else auxcon[0])\n\ndef get_servant_conhost(pid, proclist):\n    """Find \xe2\x80\x9cattendant\xe2\x80\x9d host process (conhost.exe)"""\n    aux = [_ for _ in proclist if _[0] == pid]\n    if len( aux) > 0:\n        auxcon = [x for x in proclist if (\n                x[1] == aux[0][0] and x[2] == "conhost.exe")]\n        if len( auxcon) == 0:\n            auxconret = get_servant_conhost(aux[0][1], proclist)\n            return auxconret\n        else:\n            auxconret = auxcon[0]\n            auxconret.append( aux[0][2])\n            return auxconret\n    else:\n        return []\n\n\ndef get_conhost_threads():\n    if sys.executable.lower().endswith(\'\\\\pythonw.exe\'):\n        return []\n    import wmi\n    c = wmi.WMI()\n    w_where = \' or \'.join([\n        \'Name like "p%.exe"\',  # py.exe|python.exe|pwsh.exe|powershell.exe \n        \'Name = "conhost.exe"\',\n        \'Name = "cmd.exe"\'\n    ])\n    w_properties = \'ProcessId, ParentProcessId, Name\'\n    w_wql = f\'SELECT {w_properties} FROM Win32_Process where {w_where}\'\n    w_wqlaux = c.query(w_wql)\n    proc_list = [[wqlitem.__getattr__(\'ProcessId\'),\n          wqlitem.__getattr__(\'ParentProcessId\'),\n          wqlitem.__getattr__(\'Name\')] for wqlitem in w_wqlaux] \n    if _verbose:\n        list_parents(os.getpid(), proc_list)\n    servant_conhost = get_servant_conhost(os.getpid(), proc_list)\n    if len( servant_conhost) == 0:\n        return []\n    else:\n        try:\n            w_where = f\'ProcessHandle = {servant_conhost[0]}\'\n            w_wql = f\'SELECT Handle FROM Win32_Thread WHERE {w_where}\'\n            w_wqlHandle = c.query(w_wql)\n            wqlthreads = [x.__getattr__(\'Handle\') for x in w_wqlHandle]\n        except:\n            wqlthreads = []\n    return wqlthreads\n\n\n# required if run from `cmd` or from the `Run` dialog box (`<WinKey>+R`) \nconhost_threads = get_conhost_threads()\nif _verbose:\n    print( \'threads\', conhost_threads)\n                                    \n\ndef get_current_keyboard_layout():\n    foregroundWindow  = user32.GetForegroundWindow();\n    foregroundProcess = user32.GetWindowThreadProcessId(int(foregroundWindow), 0);\n    keyboardLayout    = user32.GetKeyboardLayout(int(foregroundProcess));\n    keyboardLayout0   = user32.GetKeyboardLayout(int(0));\n    if keyboardLayout == 0  or len(conhost_threads):                 \n        if keyboardLayout == 0:\n            keyboardLayout = keyboardLayout0\n        for thread in conhost_threads:\n            aux = user32.GetKeyboardLayout( int(thread))\n            if aux != 0 and aux != keyboardLayout0:\n                if _verbose:\n                    print(\'thread\', thread)\n                keyboardLayout = aux\n                break\n    return keyboardLayout\n\n\n### improved original code\n#   Detect keyboard input with support of other languages from English\n#   /sf/ask/5000197051/\n###\n#   written for extensively verbose output\n###\n\nimport unicodedata\nfrom pynput import keyboard\nlast_key = keyboard.Key.media_next  # improbable last key pressed\n\n\ndef on_press(key):\n    global last_key\n    if isinstance(key, keyboard.Key):\n        if key == keyboard.Key.space:\n            c_hkl = get_current_keyboard_layout()\n            chklp = f\'{(c_hkl & 0xFFFFFFFF):08x}\'\n            # 0x39 = SpaceBarScanCode https://kbdlayout.info/kbduk/scancodes\n            print(key.value.char,\n                  f\'{key.value.vk} ({key.value.vk:02x})\',\n                  f\'{0x39} ({0x39:02x})\',\n                  f\'{c_hkl:10} ({chklp})\',\n                  unicodedata.name(key.value.char, \'?\')\n                  )\n        else:\n            if last_key != key:\n                print( f\'{"":39}\', key.value, key.name )\n                last_key = key\n    else:\n        c_hkl = get_current_keyboard_layout()\n        chklp = f\'{(c_hkl & 0xFFFFFFFF):08x}\'\n        c_char = ToUn(key.vk, key._scan, 0, c_hkl)\n        print(c_char,\n              f\'{key.vk} ({key.vk:02x})\',\n              f\'{key._scan} ({key._scan:02x})\',\n              f\'{c_hkl:10} ({chklp})\',\n              unicodedata.name(c_char[0], \'?\')\n              )\n\n\ndef on_release(key):\n    if key == keyboard.Key.esc:     \n        return False             # Stop listener\n\n\nprint(\'\\n  vk_code scancode   HKL dec (HKL hexa) character name\')\nwith keyboard.Listener(on_press=on_press, on_release=on_release) as listener:\n    x=listener.join()\n
Run Code Online (Sandbox Code Playgroud)\n

输出(主要测试F不同键盘布局(输入语言)循环切换的按键 LeftAltShift):\\SO\\71431386.py

\n
  vk_code scancode   HKL dec (HKL hexa) character name\nf 70 (46) 33 (21)   67438601 (04050809) LATIN SMALL LETTER F\n                                        <164> alt_l\n                                        <160> shift\n\xd1\x84 70 (46) 33 (21) -265092071 (f0330419) CYRILLIC SMALL LETTER EF\n                                        <164> alt_l\n                                        <160> shift\nf 70 (46) 33 (21)   67437573 (04050405) LATIN SMALL LETTER F\n                                        <164> alt_l\n                                        <160> shift\n\xcf\x86 70 (46) 33 (21) -266992632 (f0160408) GREEK SMALL LETTER PHI\n                                        <164> alt_l\n                                        <160> shift\nf 70 (46) 33 (21)   67438601 (04050809) LATIN SMALL LETTER F\n  32 (20) 57 (39)   67438601 (04050809) SPACE\n                                        <27> esc\n
Run Code Online (Sandbox Code Playgroud)\n