pynput icon indicating copy to clipboard operation
pynput copied to clipboard

GlobalHotKeys doesn't like Arrow Keys

Open brisingre opened this issue 3 years ago • 7 comments

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.

brisingre avatar Jan 17 '22 11:01 brisingre

Quick update: I have reproduced this on Raspbian as well as Windows 10.

brisingre avatar Jan 21 '22 10:01 brisingre

I've reproduced on macOS. Possibly same problem as #439

cltrudeau avatar Feb 08 '22 19:02 cltrudeau

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 , I use <53>, the ESC key works fine. So there's something wrong in the parsing method.

btonasse avatar Feb 16 '22 15:02 btonasse

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.

brisingre avatar Feb 16 '22 20:02 brisingre

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

btonasse avatar Feb 17 '22 07:02 btonasse

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

cwtravis avatar Apr 14 '22 19:04 cwtravis

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   ❌
    }
...

QHQIII avatar May 14 '22 11:05 QHQIII