1

I would like this Python 3.10 script (where the pynput code is partially based on this answer) to enter the while loop and at the same time monitor the keys pressed on the keyboard. When q is pressed, I would like it to end.

(I do not know threads very well, but the while loop probably should run in the main thread and the keybord monitor should run in a child, concurrent thread).

#!/usr/bin/python3

import threading
import sys
from pynput import keyboard

def on_key_press(key):
    try:
        k = key.char
    except:
        k = key.name
    if k in ['q']:
        exit_time = True

exit_time = False

print("Press q to close.")

keyboard_listener = keyboard.Listener(on_press=on_key_press)
keyboard_listener.start()
keyboard_listener.join()

while not exit_time:
    sleep(1)

print("Goodbye")
sys.exit(0)

It instead gets locked in an endless wait after keyboard_listener.start(). I don't know if keyboard_listener.join() doesn't run at all, or if it causes the program to lock.

However, the while loop is not run. If I end the program with Ctrl+C:

^CTraceback (most recent call last):
  File "/my/source/./file.py", line 22, in <module>
    keyboard_listener.join()
  File "/my/.local/lib/python3.10/site-packages/pynput/_util/__init__.py", line 295, in join
    super(AbstractListener, self).join(timeout, *args)
  File "/usr/lib/python3.10/threading.py", line 1096, in join
    self._wait_for_tstate_lock()
  File "/usr/lib/python3.10/threading.py", line 1116, in _wait_for_tstate_lock
    if lock.acquire(block, timeout):
KeyboardInterrupt
5
  • 1
    do you understand what join means ? did you read the docs ?
    – Ahmed AEK
    Commented Apr 4 at 16:55
  • @AhmedAEK I read the docs, but not knowing threads very well, I'm not sure about what they are stating.
    – BowPark
    Commented Apr 4 at 17:07
  • is this while loop really empty ? or are you doing any work inside it ? so is all the work done inside on_key_press, or are you doing other work at the same time ?
    – Ahmed AEK
    Commented Apr 4 at 17:09
  • 1
    unreated, but fyi: exit_time = True is only local, you need to bring the global into scope with global exit_time .... not that this will solve your actual bug.
    – Ahmed AEK
    Commented Apr 4 at 17:10
  • @AhmedAEK Thanks for global. In this example, while is empty, but I would also like to try doing some work inside it, asynchronously, and not on on_key_press.
    – BowPark
    Commented Apr 4 at 17:13

1 Answer 1

1

you are joining the listener thread, ie: waiting for it to exit. remove the while loop. the join is already waiting for the thread to exit.

also from the docs

Call pynput.keyboard.Listener.stop from anywhere, raise StopException or return False from a callback to stop the listener.

you are waiting for the listener to exit, but you never really tell it to exit, you should probably return False to tell it to exit.

def on_key_press(key):
    try:
        k = key.char
    except:
        k = key.name
    if k in ['q']:
        return False  # end the listener and unlock the main thread

if you want to do work within the while loop then remove the join, and use threading.Event, as you can wait for it to be signaled instead of sleeping.

#!/usr/bin/python3

import threading
import sys
from pynput import keyboard
from time import sleep

def on_key_press(key):
    try:
        k = key.char
    except:
        k = key.name
    if k in ['q']:
        thread_exited.set()
        return False # end the listener and unlock the main thread

thread_exited = threading.Event()

print("Press q to close.")

keyboard_listener = keyboard.Listener(on_press=on_key_press)
keyboard_listener.start()

# wait for 1 second for the flag to be set, returns true if it was set
while not thread_exited.wait(1):
    print("doing some work ...")

keyboard_listener.join()

print("Goodbye")

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.