Closed basharkey closed 3 years ago
Wrote a quick monkey patch to solve the issue but I don't know the full implications of this fix.
import platform
def windows_hotkey_fix():
from pynput.keyboard import HotKey
def press_fixed(self, key):
"""Updates the hotkey state for a pressed key.
If the key is not currently pressed, but is the last key for the full
combination, the activation callback will be invoked.
Please note that the callback will only be invoked once.
:param key: The key being pressed.
:type key: Key or KeyCode
"""
if key in self._keys and key not in self._state:
self._state.add(key)
if self._state == self._keys:
# reset state
self._state = set()
self._on_activate()
keyboard.HotKey.press = press_fixed
if platform.system() == 'Windows':
windows_hotkey_fix()
Thank you for your report and workaround.
As stated in the documentation, the callbacks must not block for long, as that will lead to random issues, such as this. I am reluctant to add a workaround for this specific issue, as many more are certain to be found elsewhere. Perhaps the documentation should be updated with stronger wording?
For anyone interested I came up with a more elegant solution using queues and threads as recommended by the documentation for callbacks that block for long amounts of time.
from pynput import keyboard
import time
from queue import Queue
from threading import Thread
def on_hotkey_a():
print('Hot key A pressed!')
time.sleep(2)
print('done')
def on_hotkey_b():
print('Hot key B pressed!')
def on_press(key):
print(f"pressed: {key}")
for hotkey in HOTKEYS:
hotkey.press(l.canonical(key))
# Handle pressed keys
def on_release(key):
print(f"released: {key}")
for hotkey in HOTKEYS:
hotkey.release(l.canonical(key))
# Handle released keys
def worker(q):
while True:
func = q.get()
func()
q.task_done()
print('task done')
q = Queue(maxsize=0)
num_threads = 1
a_callback = lambda: q.put(on_hotkey_a)
b_callback = lambda: q.put(on_hotkey_b)
HOTKEYS = [
keyboard.HotKey(keyboard.HotKey.parse('<shift>+a'), a_callback),
keyboard.HotKey(keyboard.HotKey.parse('<shift>+b'), b_callback)]
for i in range(num_threads):
worker = Thread(target=worker, args=(q,))
worker.setDaemon(True)
worker.start()
with keyboard.Listener(
on_press=on_press,
on_release=on_release) as l:
l.join()
On Windows when a global hotkey triggers a callback that contains a sleep greater than 1 second the keys used to trigger the global hotkey will not be released after the callback has finished executing. This means that pynput still thinks that certain keys are pressed even through they have been released.
Notice when run on Linux the hotkeys, keys ('\<shift>+a') are released after the callbacks execution.
However when ran on Windows the hotkeys, keys ('\<shift>+a') are never released.
Code used to test: