pynput
pynput copied to clipboard
GlobalHotKeys doesn't like Arrow Keys
Description
GlobalHotKeys doesn't like <up>, <down>, <left>, or <right>
, and possibly some other special keys. They just don't work. (Some special keys such as <alt>
work fine, I don't have a comprehensive list of which keys this affects yet.)
Platform and pynput version Windows 10 LTSC x64 pynput version 1.7.6
To Reproduce
import time
from pynput import keyboard
def on_q():
print("q pressed")
def on_alt():
print("alt pressed")
def on_up():
print("up pressed")
globalHotkeys = keyboard.GlobalHotKeys({
'q': on_q,
'<alt>': on_alt,
'<up>': on_up
# this doesn't work. It accepts <up> as a key name, but on_up isn't called when I press the up arrow key.
})
globalHotkeys.start()
should_quit = False
while not should_quit:
time.sleep(.1)
When you run this script, you'll see pressing q calls on_q and pressing alt calls on_alt but pressing up does not call on_up.
Quick update: I have reproduced this on Raspbian as well as Windows 10.
I've reproduced on macOS. Possibly same problem as #439
I have reproduced this issue as well. Cannot use global hotkeys with certain special keys like ESC. It doesn't throw an error, it just doesn't do anything.
EDIT: However, if, instead of
EDIT: However, if, instead of , I use <53>, the ESC key works fine. So there's something wrong in the parsing method.
Is there a list of these numbers? This could be a viable workaround for me.
The HotKey.parse() method gives you the code. My workaround is to replace the troublesome special keys before calling GlobalHotkeys.
Note that this logic is currently encapsulated in a class in my project, so not everything will make sense. All you need to know is that self._hotkeys is a dict where the hotkey string is the key and the callback is the value.
from pynput import keyboard as kb
def _replace_special_keys(self) -> HotkeyDict:
"""
Due to a bug in pynput, special, non-modifier keys like ESC and F1 do not work.
However, they can work if their key code is provided instead (e.g. <53> isntead of <esc> on MacOS)
This method therefore replaces these special keys with their keycode.
"""
new_hotkeys: HotkeyDict = {}
for hotkey in self._hotkeys:
parsed_keys = kb.HotKey.parse(hotkey)
for key in parsed_keys:
# Check if key is nor a KeyCode and not a modifier key,
# in which case replace it with the str representation of its KeyCode object e.g. <esc> becomes <53>
if type(key) != KeyCode and key.name not in {'shift', 'ctrl', 'alt', 'cmd'}:
self._logger.debug(
f"Key <{key.name}> replaced with its keycode string: {key.value}")
new_hotkey_name = hotkey.replace(
f"<{key.name}>", str(key.value))
new_hotkeys[new_hotkey_name] = self._hotkeys[hotkey]
else:
# Copy hotkey to new HotkeyDict as is
new_hotkeys[hotkey] = self._hotkeys[hotkey]
return new_hotkeys
I made a pull request that addresses this issue. It changes the parser function to use the KeyCode values if the key is not a modifier key or character.
https://github.com/moses-palmer/pynput/pull/466
Another question, when I use vk value <49> instead of the key char in code, it doesn't do anything.
...
{
"<esc>": on_activate, # esc ❌
"<27>": on_activate, # esc ✔
"<f1>": on_activate, # f1 ❌
"<112>": on_activate, # f1 ✔
"a": on_activate, # a ✔
"<49>": on_activate, # a ❌
}
...