skip to Main Content

I have writing the following code to get key pressed by the user:

class Key:
    def __init__(self) -> None:
        pass
    
    # Define the function that will be called when a key is pressed.
    def on_press(self, key: KeyType) -> None:
        pass
    
    # Define the function that will be called when a key is released.
    def on_release(self, key: KeyType, queue: Queue, listener: Listener) -> None:
        try:
            # Put the character key in the queue.
            queue.put(key.char)

            # Stop the listener
            listener.stop()
        
        except AttributeError:
            # Put non-character keys in the queue.
            queue.put(key.name)

            # Stop the listener
            listener.stop()

    def get_key(self, prompt_string: str) -> str:
        '''
        Prompts the user for a key.
        
        Args:
            prompt_string (str): A string representing the prompt to display to the user
            
        Returns:
            key (str): A key which the user pressed

        Examples:
            To prompt the user for a key and print the key that was pressed, use:

            >>> key = get_key("Press a key: ")
            >>> print("You pressed:", key)
        '''
        
        # Create a new queue object to hold the key value.
        key_queue = Queue()
        
        # Start a keyboard listener with the on_press and on_release functions.
        # Use a lambda function to pass the queue object to the on_release function.
        with Listener(on_press=self.on_press, on_release=lambda key: self.on_release(key, queue=key_queue, listener=listener)) as listener:
            # Print the prompt string to the console.
            # Use flush=True to ensure that the message is printed immediately.
            # Use end='' to make sure that the next print statement is on the same line.
            print(prompt_string, end='', flush=True)
            
            # Initialize the key value to None.
            key = None
            
            # Keep looping until a key value has been retrieved from the queue.
            while key is None:
                key = key_queue.get()
            
            # Return the key value to the calling function.
            return key

I call the above code in a separate file after which I ask the user for a input using the input() function.

# Get key to be autoclicked from user by calling get_key function with a prompt message.
button = key.get_key(f"{INPUT_COLOR}Key to be autoclicked (press any key): {RESET}")
# Print the button that the user has pressed.
print(USER_INPUT_COLOR + button + RESET)
            
# Get delay between key presses in seconds from user by calling get_input function with a prompt message.
delay = float(input(f"{INPUT_COLOR}Delay between key presses (in seconds): {USER_INPUT_COLOR}"))
print(f"{RESET}", end="")

But when the above code is executed I can press a button to enter a key for the button input but for the delay input when try to enter a float value like ‘0.1’, it doesn’t show anything that I am typing. Furthermore when I press enter after typing ‘0.1’ it gives this error: ValueError: could not convert string to float: 'mqe0.1'
or other times the error changes to this: ValueError: could not convert string to float: 'kqer0.1'.

After this the program naturally exists but I find that the terminal seems to be broken because whatever I type seems to not be displayed but it is there because when I enter the terminal responds to whatever I typed but doesn’t show it.

Some extra info:

  • I have checked and made sure all my imports and correct.
  • the f'{INPUT_COLOR}' and f'{RESET} are just constant values for ansi escape codes that I have defined in another file and they are not causing the issue.
  • the distro for linux that I am using is Zorin OS 16 which is built on Ubuntu 20.04.
  • I am running the above code in bash terminal, which is provided by default when I installed Zorin (this is a relatively new install).

What I have tried:
I tried adding a listener.stop() to the listener for getting key presses but that doesn’t change anything

Is this a problem with Pynput on linux or a problem with the code I have written?
Any help would be appreciated!

2

Answers


  1. Chosen as BEST ANSWER

    So this problem of the bash terminal breaking, where it doesn't respond to what you are typing can be fixed by executing the command reset in the terminal.

    So for the app I just needed to add a os.system("reset") line after you are done using pynput to listen for key presses.

    So the example code becomes:

    # Get key to be autoclicked from user by calling get_key function with a prompt message.
    button = key.get_key(f"{INPUT_COLOR}Key to be autoclicked (press any key): {RESET}")
    # Print the button that the user has pressed.
    print(USER_INPUT_COLOR + button + RESET)
    
    # Reset the terminal
    os.system("reset")
    
    # Get delay between key presses in seconds from user by calling get_input function with a prompt message.
    delay = float(input(f"{INPUT_COLOR}Delay between key presses (in seconds): {USER_INPUT_COLOR}"))
    print(f"{RESET}", end="")
    

    This does erase the previous output from the terminal, but you can code a workaround for that.


  2. Your problem come from pynput who capture key events in the terminal, but that disturb the normal behavior of the input() function.

    You should add a delay so that the keyboard listener have enough time.

    for example your code will become:

    from pynput.keyboard import Key as KeyType, Listener
    from queue import Queue
    import time
    
    class Key:
        def __init__(self):
            pass
        
        def on_press(self, key):
            pass
        
        def on_release(self, key, queue, listener):
            try:
                queue.put(key.char)
                listener.stop()
            except AttributeError:
                queue.put(key.name)
                listener.stop()
    
        def get_key(self, prompt_string):
            key_queue = Queue()
            with Listener(on_press=self.on_press, on_release=lambda key: self.on_release(key, queue=key_queue, listener=listener)) as listener:
                print(prompt_string, end='', flush=True)
                key = None
                while key is None:
                    key = key_queue.get()
                return key
    
    key = Key()
    
    button = key.get_key("Key to be autoclicked (press any key): ")
    print(button)
    
    time.sleep(0.1)
    
    delay = float(input("Delay between key presses (in seconds): "))
    print(delay)
    

    If you still experience problem with the input, maybe try an alternative library for capturing key event.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search