Search code examples
pythonpython-3.xkeyboardpython-multithreading

Python Keyboard module - Exit blocking read event function



Hello,
I have the below code that corrects user input and I want to exit the blocking function keyboard.read_event when the control is returned from the correction thread.
The whole program works well but I cannot exit immediately after the corrector thread is finished (the program waits for key press).
I tried using a custom Exception for interrupting the keyboard.read_event function, but I didn't manage to make it work.

import keyboard
import threading
import time

class Interrupt_Custom_Exception(Exception):
   """Base class for other exceptions"""
   pass

#########################################################
def delete_and_write(times_to_delete, word_to_write):
    print("------------Deleting & Rewrite Started---")
    time.sleep(2)
    print("------------Deleting & Rewrite Ended---")
    # simulate deletion and rewrite
    #**here I tried the raise Interrupt_Custom_Exception and tried to catch it at the code in the class, but didn't work**


def write_the_suppressed_string(string):
    keyboard.write(string)
#########################################################

class keyboard_monitor(threading.Thread):
    def __init__(self,thread_name, threadID, word_typed,  keyboard_suppress, counter_for_key_pressed):
        threading.Thread.__init__(self)
        self.name = thread_name
        self.threaID = threadID
        self.fstring = word_typed
        self.counter_for_key_presses = counter_for_key_pressed
        self.suppressed = keyboard_suppress
        self.temp = ""

    def stop(self):
        self._is_running = False

    def run(self):

        if (self.suppressed is False):

            while(True):

                event = keyboard.read_event(suppress = self.suppressed)

                if (event.event_type == keyboard.KEY_DOWN):

                    if (event.name == "space"):

                        suppressed_monitor = keyboard_monitor("suppressed_monitor", 2, self.fstring, True, self.counter_for_key_presses)
                        suppressed_monitor.start()
                        suppressed_monitor.join()

                        print("RETURNED TO MAIN MONITOR")
                        self.counter_for_key_presses = 0
                        self.fstring = ""
                    elif (event.name in "abcdefghijklmnopqrstuvwxyz"):
                        self.fstring = ''.join([self.fstring, event.name])
                        self.counter_for_key_presses += 1

        elif (self.suppressed is True):

            def listen_to_keyboard():
                event = keyboard.read_event(suppress=self.suppressed)
                # **here is where the program waits and don't continue when the correction thread is  finished.**
                if (event.event_type == keyboard.KEY_DOWN):
                    print("---KEYS PRESSED WHILE SUPPRESSED = {}---".format(event.name))
                    if (event.name in "abcdefghijklmnopqrstuvwxyz"):
                        self.fstring = ''.join([self.fstring, event.name])
                        self.counter_for_key_presses += 1

            try:

                #########################################################
                # INITIALY CORRECTING THE WORD PASSED FROM THE NORMAL KEY MONITOR
                self.temp = self.fstring
                self.fstring = ""
                thread_delete_and_rewrite = threading.Thread(
                    target = delete_and_write, args=(self.counter_for_key_presses, self.temp))
                thread_delete_and_rewrite.start()
                # raise Interrupt_Custom_Exception

                #########################################################

                print("-BEFORE WHILE LOOP-")
                while(thread_delete_and_rewrite.is_alive() is True): # **this works ok but if the control enters the listen_to_keyboard function waits there until a key is pressed. I want somehow to stop this manually and continue the code after this while**
                    print("--ENTERING THE WHILE LOOP--")
                    listen_to_keyboard()
                    print("----EXITING THE WHILE LOOP----\n")            
            except Interrupt_Custom_Exception:
                print("!!!!!!!!!!!!!!!!!CAUGHT IT!!!!!!!!!!!!!!!!!!!")
                print("----EXITING THE WHILE LOOP----\n")

            print("------BEFORE FINAL WRITE------")

            if (self.fstring != ""):
                thread_write = threading.Thread(
                                target = write_the_suppressed_string, args=(self.fstring, ))
                thread_write.start()
                thread_write.join()
            print("SUPPRESSED ENDED")
            self._is_running = False


if __name__ == "__main__":
    kb_not_suppressed = keyboard_monitor("not_suppressed_monitor", 1, "", False, 0)
    kb_not_suppressed.start()
    kb_not_suppressed.join()

Any idea on how to exit this blocking function would be very very useful.
Thanks in advance.


Solution

  • It's not possible unless you find some keyboard.read_event that has a timeout, or does a non-blocking check if there's a event. I haven't found any of those in keyboard module ;/

    A big workaround would be to keyboard.press in case you want to exit. Not sure if you can detect if it's not from the user. It's up to you if it's acceptable.