Home > Mobile >  Python threading blocked by keyboard library?
Python threading blocked by keyboard library?

Time:11-27

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 implimented 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.

CodePudding user response:

keyboard may run own code also in thread and there can be conflict between two threads. Python uses GIL to run only one thread at a time - so when your thread is running then it may block thread which should write text on screen. Code works better if I use even.wait(0.1) after write() so Python can switch threads and send text on screen. If you use longer value then it also work but if you press key very fast then it may stil run write or wait and it can't check is_pressed("F9") and not stop it - you should see it if you use event.wait(1) after write

There is also other problem - it may write last text after pressing key and it should use break to exit while and skip write

import threading
import keyboard

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:
                print('stop')
                status = 0
                event.wait(1)
                break
            keyboard.write("test")
            event.wait(0.1)

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

But I would reduce it to

import threading
import keyboard

def spam_this():
    print('start spam_this')
    status = False
    while True:
        if keyboard.is_pressed("F9"):
            status = not status
            print('status:', status)
            event.wait(0.1)
        if status is True:
            keyboard.write("test")
            event.wait(0.1)

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

But it has another problem - if you keep pressed key longer time then it will switch status many times. I added print('status:', status) to show it.

I was thinking to use keyboard.add_hotkey like this

import threading
import keyboard

# global variable
status = False

def spam_this():
    print('start: spam_this')
    while True:
        if status is True:
            keyboard.write("test")
            event.wait(0.1)

def test():
    global status
    
    status = not status
    print('change:', status)
    event.wait(0.5)
    
event = threading.Event()
threading.Thread(target=spam_this).start()
    
keyboard.add_hotkey("F9", test)
keyboard.wait()

but when you keep pressed key then system may start sending the same key again.

I didn't test keyboard.on_press_key() - maybe it would resolve it.

Doc: keyboard

CodePudding user response:

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)

  • Related