0

I am trying to make a program that prints out a sentence with pynput by only pressing one key, however if that sentence contains that particular key it just starts typing that sentence infinite times. I would like to find a way to ignore the inputs from pynput, but not from my keyboard. Thanks in advance.

code:

from pynput.keyboard import Key, Listener, Controller

kb = Controller()


def on_press(key):
    if not 'char' in dir(key):
        return

    if key.char == "s":
        kb.press(Key.backspace) # deletes "s"
        kb.release(Key.backspace)
        kb.type("sample")
        return


def on_release(key):
    if key == Key.esc:
        return False


with Listener(on_press=on_press, on_release=on_release) as listener:
    listener.join()

output: samplsamplsamplsampl....

simo.
  • 69
  • 5
  • why `kb.type` instead of `print`? Also, as instructed in [ask], can you please write a descriptive, non-ambiguous title? For more guidance, see [How do I write a good title?](//meta.stackexchange.com/q/10647/997587) – starball Jan 23 '23 at 08:06

1 Answers1

2

A simple solution would be to introduce a global variable as a locking mechanism:

from pynput.keyboard import Key, Listener, Controller

kb = Controller()

lock = False

def on_press(key):
    global lock

    if not hasattr(key, 'char'):
        return

    if lock:
        return

    if key.char == "s":
        lock = True
        kb.press(Key.backspace) # deletes "s"
        kb.release(Key.backspace)
        kb.type("sample")
        lock = False
        return


def on_release(key):
    if key == Key.esc:
        return False


with Listener(on_press=on_press, on_release=on_release) as listener:
    listener.join()

This way you avoid responding to keypresses while you are in the process of typing keys.

larsks
  • 277,717
  • 41
  • 399
  • 399
  • 1
    For this use case I believe it is necessary that setting `lock = False` occurs in the `on_release()` function. Including it within the on_press function seems to not prevent the endless loop caused by `kb.type(param)` typing the same character again. I think this is because Controller() and Listener() are both on their own threads, so the Listener is has already set `lock = False` before Controller has finished typing "sample", so then Controller triggers Listener again. – nigh_anxiety Jan 22 '23 at 02:55
  • 1
    For the record, when I run it locally it works just fine as written, but perhaps what you've proposed is more robust. – larsks Jan 22 '23 at 05:39
  • 1
    It may be a difference in OS behavior. Running what you originally had written on my Windows 10 machine still caused the original reported problem behavior. – nigh_anxiety Jan 22 '23 at 05:50
  • It didn't work for me with either of the solutions, it kinda feels like the Listener is calling on_press faster than the previous call can finish, is there a way to make it wait for the previous call to finish? – simo. Jan 22 '23 at 10:17
  • 1
    @simo Locking should be the best way to do that, and I had success by putting `global lock` and `lock = False` within the `on_release()` callback function. Based on this other post, you may get some benefit by adding a small wait with something like `time.sleep(0.05)` after sending the command to kb.type(). That should force the system to switch threads to the keyboard Controller and stop listening for a bit. https://stackoverflow.com/questions/67879004/keyboard-module-toggle-listener – nigh_anxiety Jan 22 '23 at 16:26
  • yeah, that didn't work either. It just made the infinite loop slower. I'll try another solution. Thanks for trying to help anyways – simo. Jan 22 '23 at 19:24