pythonmultithreadingkeyboard

Python threading blocked by keyboard library?


While building a script that can be toggled to spam, I encountered the following problem.

First is the normal functioning version of the script:

import keyboard
import threading

def spam_this():
    status = 0
    while True:
        if keyboard.is_pressed("F9") and status == 0:
            status = 1
            event.wait(1)
        if keyboard.is_pressed("F9") and status == 1:
            status = 0
            event.wait(1)
        while status == 1:
            if keyboard.is_pressed("F9") and status == 1:
                status = 0
                event.wait(1)
            print("test")

event = threading.Event()
threading.Thread(target=spam_this).start()

The script above works perfectly. However, when I change the line print("test") to keyboard.write("test"), the script breaks.

import keyboard
import threading

def spam_this():
    status = 0
    while True:
        if keyboard.is_pressed("F9") and status == 0:
            status = 1
            event.wait(1)
        if keyboard.is_pressed("F9") and status == 1:
            status = 0
            event.wait(1)
        while status == 1:
            if keyboard.is_pressed("F9") and status == 1:
                status = 0
                event.wait(1)
            keyboard.write("test")

event = threading.Event()
threading.Thread(target=spam_this).start()

This version of the script with the keyboard.write() function can be initiated with the implemented toggle key "F9", but when I try to toggle off the switch by pressing "F9" again, it does not stop like the print("test") version of itself.

Note: I am not sure how to word this problem in the title. I use the term "blocked" because the effect is similar to what blocking method like time.sleep() would do when trying to create a while True: loop with a toggle.


Solution

  • import threading
    import keyboard
    
    # global variable
    status = False
    
    def spam_this():
        print('start: spam_this')
        while True:
            if status is True:
                print("test")
                keyboard.write("test")
                event.wait(0.1)
            
                
    def test(event=None):
        global status
        status = not status
        print('change:', status)
    
    event = threading.Event()
    keyboard.add_hotkey("F9", test)
    #alternatively:
    #keyboard.on_key_press("F9", test)
    threading.Thread(target=spam_this).start()
    

    there are a few modifications that has been made to @furas's answers.

    1. for the test function, a parameter event=None was added, otherwise the keyboard.on_press_key will pass the F9 key-pressed-event to the test function, which results in a typeError.

    2. in the test function, the event.wait() was removed due to an error, and upon testing, the keyboard.on_press_key and keyboard.add_hotkey function does have inbuilt delays, so as long as the F9 key was not HELD down, the inbuilt delays are more than enough.

    3. regardless of whether using on_key_press or add_hotkey, the thread should be initialized AFTER the desired "hotkey method" is called. otherwise the thread will block the main loop of the python script.(this part I cannot explain why, it was just trial and error leading to this conclusion.)

    4. the keyboard.wait() was removed, because this function was not used.

    NUMBER 1 and 3 ARE THE MOST IMPORTANT CHANGES (for the people who doesn't want to read through the minor bits)