keyboard
keyboard copied to clipboard
Arrow keys trigger with number keys
when add callback for 2, 4, 6, 8 keys, the callback also trigger with arrow keys
how to fix it
I have the same issue! Any ideas, anyone?
I have the same issue! Any ideas, anyone?
It's been some time I came across this so take this with a grain of salt; But i used on_key_down / up events only and implemented my own hotkey manager checking for the names of the arrow keys (I think). Im not sure if i still have access to the code.
Excerpt:
class Keys(IntEnum):
ARROW_DOWN = 80
NUMPAD_2 = 80
def keys_to_hotkey_string(keys: tuple | list | set) -> str:
"""
python sort is stable so we can safely use it as hotkey unique identifier.
We sort it to make it more resilient to developer input
"""
return "+".join(sorted(keys))
class Hotkey:
def __init__(self, *keys: str | Keys, display_text: str | None = None, is_numpad: bool = False):
"""
Hotkeys are firstly converted in a "key:is_numpad" format.
"key" is either the Key-Enum value which is the actual keycode.
If anything but a Key instance is passed this code tries to convert it to a keycode using
the keyboard lib.
"""
self._hotkey: str = keys_to_hotkey_string([
f"{key}:{1 if is_numpad else 0}" if isinstance(key, Keys)
else f"{keyboard.key_to_scan_codes(key.lower())[0]}:{1 if is_numpad else 0}"
for key in keys
])
def _event_to_unique(event: KeyboardEvent) -> str:
return f"{event.scan_code}:{int(event.is_keypad)}"
class KeyInput:
@classmethod
def _on_keyboard_event(cls, event: KeyboardEvent) -> None:
event_as_unique: str = cls._event_to_unique(event)
logger.debug(f"KeyEvent: {event_as_unique}:{event}")
match event.event_type:
case keyboard.KEY_DOWN:
cls._on_key_down(event, event_as_unique)
case keyboard.KEY_UP:
cls._on_key_up(event, event_as_unique)
@classmethod
def _on_key_down(cls, event: KeyboardEvent, unique: str) -> None:
cls._key_state.add(unique)
if (hotkey := cls._hotkeys.get(cls.keys_to_hotkey_string(cls._key_state))) is not None:
if not hotkey.is_disabled():
logger.debug(f"Is hotkey: {hotkey} <{cls._key_state}>")
hotkey.invoke()
cls._key_state.clear()
return
cls._key_down_wrapper(event)
@classmethod
def _on_key_up(cls, event: KeyboardEvent, unique: str) -> None:
cls._key_state.discard(unique)
cls.on_keyboard_key_up.emit(text=event.name, keycode=event.scan_code)
if __name__ == "__main__":
arrow_down_hotkey: Hotkey = Hotkey(Keys.ARROW_DOWN)
arrow_down_hotkey.link(None, lambda: print("ARROW INVOKED"))
KeyInput.register_hotkey(arrow_down_hotkey)
numpad_2: Hotkey = Hotkey(Keys.NUMPAD_2, is_numpad=True)
numpad_2.link(None, lambda: print("NUMPAD_2 INVOKED"))
KeyInput.register_hotkey(numpad_2)
KeyInput.hook()
keyboard.hook(KeyInput._on_keyboard_event)
I have left out some methods as they are not really essential to this solution. Essentially just storing a callable in a list/dict and invoke it once the hotkey is detected in _on_key_down