Search code examples
pythonloopspyautoguipynput

pyautogui typewrite won't stop typing


I am writing a script such that when a user presses Shift+P, a text string will be entered. It works, and when I press Shift+P, it enters the text, but it does not stop entering the text. I assume this is something I did and am not seeing. Why does this keep looping and typing? How do I make it stop after it finishes typing "Hello, World" once?

from pynput import keyboard
import pyautogui as pg

COMBINATIONS = [
        {keyboard.Key.shift, keyboard.KeyCode(char="p")},
        {keyboard.Key.shift, keyboard.KeyCode(char="P")}
        ]

current = set()

def execute():
    pg.press("backspace")
    pg.typewrite("Hello, World\n", 0.25)

def on_press(key):
    if any ([key in COMBO for COMBO in COMBINATIONS]):
        current.add(key)
        if any(all(k in current for k in COMBO) for COMBO in COMBINATIONS):
            execute()

def on_release(key):
    if any([key in COMBO for COMBO in COMBINATIONS]):
        current.remove(key)

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

Solution

  • What happens here is tricky. py.typewrite calls the on_press signal handler. But it doesn't just call it ... it calls on_press with keyboard.Key.Shift, because of the capital H (shift-h) and the exclamation mark (shift-1) in Hello World!

    First, you can see the difference if you send hello world and not Hello World! With the lower-case version, typewriter never sends a shift key, so we don't run on_press and it doesn't think, oh, we have p and shift down, we need to run Hello World again once we're done.

    A solution is to make a global, process_keystrokes, and turn it off when running execute(). It also seems like a good idea to clear the set of keys down, since we don't know what the user can/may do when typewriter is sending keystrokes.

    from pynput import keyboard
    import pyautogui as pg
    
    COMBINATIONS = [
            {keyboard.Key.shift, keyboard.KeyCode(char="p")},
            {keyboard.Key.shift, keyboard.KeyCode(char="P")}
            ]
    
    current = set()
    
    pg.FAILSAFE = True # it is by default, but just to note we can go to the very upper left to stop the code
    
    process_keystrokes = True
    
    def execute():
        global process_keystrokes
        process_keystrokes = False # set process_keystrokes to false while saying HELLO WORLD
        pg.press("backspace")
        pg.typewrite("#Hello, World\n", 1)
        process_keystrokes = True
    
    def on_press(key):
        if not process_keystrokes: return
        if any ([key in COMBO for COMBO in COMBINATIONS]):
            current.add(key)
            print("Added", key)
            if any(all(k in current for k in COMBO) for COMBO in COMBINATIONS):
                execute()
                current.clear() # note this may potentially not track if we held the shift key down and hit P again. But unfortunately typewriter can stomp all over the SHIFT, and I don't know what to do. There seems to be no way to tell if the user let go of the shift key, so it seems safest to clear all possible keystrokes.
    
    def on_release(key):
        if not process_keystrokes: return
        if any([key in COMBO for COMBO in COMBINATIONS]):
            print("Removed", key)
            current.remove(key)
    
    with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
        listener.join()