From b1a0c8221c46c8dda8f2284699554256e514584b Mon Sep 17 00:00:00 2001 From: Paolo Pastori <75467826+paolopas@users.noreply.github.com> Date: Thu, 1 Dec 2022 22:23:29 +0100 Subject: [PATCH 1/3] make Listener.canonical a staticmethod, this make the call from callabacks easier. --- lib/pynput/keyboard/_base.py | 3 ++- tools/keyboard-mixin-test.py | 48 ++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 tools/keyboard-mixin-test.py diff --git a/lib/pynput/keyboard/_base.py b/lib/pynput/keyboard/_base.py index 151ee2ed..3a79e62e 100644 --- a/lib/pynput/keyboard/_base.py +++ b/lib/pynput/keyboard/_base.py @@ -712,7 +712,8 @@ def __init__(self, on_press=None, on_release=None, suppress=False, on_press=on_press, on_release=on_release, suppress=suppress) # pylint: enable=W0223 - def canonical(self, key): + @staticmethod + def canonical(key): """Performs normalisation of a key. This method attempts to convert key events to their canonical form, so diff --git a/tools/keyboard-mixin-test.py b/tools/keyboard-mixin-test.py new file mode 100644 index 00000000..8aecc067 --- /dev/null +++ b/tools/keyboard-mixin-test.py @@ -0,0 +1,48 @@ + +# insert in front of sys.path because of system package already installed +import os +import sys +sys.path.insert(0, + os.path.join(os.path.dirname(__file__), '..', 'lib')) + +from pynput import keyboard + +""" +I was looking for a way to use both HotKeys and regular callabacks +(i.e. on_press and on_release) and still being able to pass the +suppress argument to the Listener. + +The main issue here is the call for keyboard.Listener.canonical. +""" + +def on_ctrl_c(): + print("=> HotKey('+c')") + raise keyboard.Listener.StopException() + +hotkeys = [ + keyboard.HotKey( + keyboard.HotKey.parse('+c'), + on_ctrl_c) +] + +def on_press(key): + print("=> {}".format(key)) + can_key = keyboard.Listener.canonical(key) + for hk in hotkeys: + hk.press(can_key) + +def on_release(key): + print("=< {}".format(key)) + if key == keyboard.Key.esc: + return False + can_key = keyboard.Listener.canonical(key) + for hk in hotkeys: + hk.release(can_key) + + +with keyboard.Listener( + on_press=on_press, + on_release=on_release, + suppress=True) as listener: + print("Press ESC or Ctrl-C to exit") + listener.join() From 97b2ea7397abb57a9991d8d2906fe2ca27e4062a Mon Sep 17 00:00:00 2001 From: Paolo Pastori <75467826+paolopas@users.noreply.github.com> Date: Fri, 2 Dec 2022 15:10:18 +0100 Subject: [PATCH 2/3] helper decorators to add HotKeys capabilities to event callbacks added --- tools/keyboard-mixin-test.py | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/tools/keyboard-mixin-test.py b/tools/keyboard-mixin-test.py index 8aecc067..b7e9a2ee 100644 --- a/tools/keyboard-mixin-test.py +++ b/tools/keyboard-mixin-test.py @@ -5,6 +5,8 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'lib')) +import functools + from pynput import keyboard """ @@ -15,8 +17,32 @@ The main issue here is the call for keyboard.Listener.canonical. """ +# these might be provided by the package +def notify_hotkeys(hotkeys, is_press): + """Decorator to add HotKey capabilities to event callbacks. + """ + def decorator_factory(callback): + @functools.wraps(callback) + def wrapper(key): + if callback(key) is False: + return False + can_key = keyboard.Listener.canonical(key) + hot_callback = 'press' if is_press else 'release' + for hk in hotkeys: + getattr(hk, hot_callback)(can_key) + return wrapper + return decorator_factory + +def activate_hotkeys(hotkeys): + return notify_hotkeys(hotkeys, True) + +def deactivate_hotkeys(hotkeys): + return notify_hotkeys(hotkeys, False) + + def on_ctrl_c(): print("=> HotKey('+c')") + # here return False should work as for other callbacks raise keyboard.Listener.StopException() hotkeys = [ @@ -25,19 +51,15 @@ def on_ctrl_c(): on_ctrl_c) ] +@activate_hotkeys(hotkeys) def on_press(key): print("=> {}".format(key)) - can_key = keyboard.Listener.canonical(key) - for hk in hotkeys: - hk.press(can_key) +@deactivate_hotkeys(hotkeys) def on_release(key): print("=< {}".format(key)) if key == keyboard.Key.esc: return False - can_key = keyboard.Listener.canonical(key) - for hk in hotkeys: - hk.release(can_key) with keyboard.Listener( From 285df3bd46bc9d5bdb3f5dcc46f5e33419b8d11e Mon Sep 17 00:00:00 2001 From: Paolo Pastori <75467826+paolopas@users.noreply.github.com> Date: Fri, 2 Dec 2022 18:50:28 +0100 Subject: [PATCH 3/3] refined on_activate behaviour for HotKey instances --- lib/pynput/keyboard/__init__.py | 3 ++- tools/keyboard-mixin-test.py | 13 +++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/pynput/keyboard/__init__.py b/lib/pynput/keyboard/__init__.py index b68dd85f..7871effd 100644 --- a/lib/pynput/keyboard/__init__.py +++ b/lib/pynput/keyboard/__init__.py @@ -185,7 +185,8 @@ def press(self, key): if key in self._keys and key not in self._state: self._state.add(key) if self._state == self._keys: - self._on_activate() + if self._on_activate() is False: + raise Listener.StopException() def release(self, key): """Updates the hotkey state for a released key. diff --git a/tools/keyboard-mixin-test.py b/tools/keyboard-mixin-test.py index b7e9a2ee..cb637f5c 100644 --- a/tools/keyboard-mixin-test.py +++ b/tools/keyboard-mixin-test.py @@ -9,19 +9,21 @@ from pynput import keyboard +__author__ = "Paolo Pastori" + """ I was looking for a way to use both HotKeys and regular callabacks (i.e. on_press and on_release) and still being able to pass the suppress argument to the Listener. -The main issue here is the call for keyboard.Listener.canonical. +The main issue was the call for keyboard.Listener.canonical. """ # these might be provided by the package def notify_hotkeys(hotkeys, is_press): - """Decorator to add HotKey capabilities to event callbacks. + """Decorator factory to add HotKey capabilities to event callbacks. """ - def decorator_factory(callback): + def decorator(callback): @functools.wraps(callback) def wrapper(key): if callback(key) is False: @@ -31,7 +33,7 @@ def wrapper(key): for hk in hotkeys: getattr(hk, hot_callback)(can_key) return wrapper - return decorator_factory + return decorator def activate_hotkeys(hotkeys): return notify_hotkeys(hotkeys, True) @@ -42,8 +44,7 @@ def deactivate_hotkeys(hotkeys): def on_ctrl_c(): print("=> HotKey('+c')") - # here return False should work as for other callbacks - raise keyboard.Listener.StopException() + return False hotkeys = [ keyboard.HotKey(