130
votes

I am making a stopwatch type program in Python and I would like to know how to detect if a key is pressed (such as p for pause and s for stop), and I would not like it to be something like raw_input, which waits for the user's input before continuing execution.

Anyone know how to do this in a while loop?

I would like to make this cross-platform but, if that is not possible, then my main development target is Linux.

13
for OS X stackoverflow.com/a/47197390/5638869 works in Python 2 and 3 - neoDev

13 Answers

93
votes

Python has a keyboard module with many features. Install it, perhaps with this command:

pip3 install keyboard

Then use it in code like:

import keyboard  # using module keyboard
while True:  # making a loop
    try:  # used try so that if user pressed other than the given key error will not be shown
        if keyboard.is_pressed('q'):  # if key 'q' is pressed 
            print('You Pressed A Key!')
            break  # finishing the loop
    except:
        break  # if user pressed a key other than the given key the loop will break
68
votes

For those who are on windows and were struggling to find an working answer here's mine: pynput

from pynput.keyboard import Key, Listener

def on_press(key):
    print('{0} pressed'.format(
        key))

def on_release(key):
    print('{0} release'.format(
        key))
    if key == Key.esc:
        # Stop listener
        return False

# Collect events until released
with Listener(
        on_press=on_press,
        on_release=on_release) as listener:
    listener.join()

The function above will print whichever key you are pressing plus start an action as you release the 'esc' key. The keyboard documentation is here for a more variated usage.

Markus von Broady highlighted a potential issue that is: This answer doesn't require you being in the current window to this script be activated, a solution to windows would be:

from win32gui import GetWindowText, GetForegroundWindow
current_window = (GetWindowText(GetForegroundWindow()))
desired_window_name = "Stopwatch" #Whatever the name of your window should be

#Infinite loops are dangerous.
while True: #Don't rely on this line of code too much and make sure to adapt this to your project.
    if current_window == desired_window_name:

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

More things can be done with keyboard module. You can install this module using pip install keyboard Here are some of the methods:


Method #1:

Using the function read_key():

import keyboard

while True:
    if keyboard.read_key() == "p":
        print("You pressed p")
        break

This is gonna break the loop as the key p is pressed.


Method #2:

Using function wait:

import keyboard

keyboard.wait("p")
print("You pressed p")

It will wait for you to press p and continue the code as it is pressed.


Method #3:

Using the function on_press_key:

import keyboard

keyboard.on_press_key("p", lambda _:print("You pressed p"))

It needs a callback function. I used _ because the keyboard function returns the keyboard event to that function.

Once executed, it will run the function when the key is pressed. You can stop all hooks by running this line:

keyboard.unhook_all()

Method #4:

This method is sort of already answered by user8167727 but I disagree with the code they made. It will be using the function is_pressed but in an other way:

import keyboard

while True:
    if keyboard.is_pressed("p"):
        print("You pressed p")
        break

It will break the loop as p is pressed.


Notes:

  • keyboard will read keypresses from the whole OS.
  • keyboard requires root on linux
37
votes

As OP mention about raw_input - that means he want cli solution. Linux: curses is what you want (windows PDCurses). Curses, is an graphical API for cli software, you can achieve more than just detect key events.

This code will detect keys until new line is pressed.

import curses
import os

def main(win):
    win.nodelay(True)
    key=""
    win.clear()                
    win.addstr("Detected key:")
    while 1:          
        try:                 
           key = win.getkey()         
           win.clear()                
           win.addstr("Detected key:")
           win.addstr(str(key)) 
           if key == os.linesep:
              break           
        except Exception as e:
           # No input   
           pass         

curses.wrapper(main)
17
votes

For Windows you could use msvcrt like this:

   import msvcrt
   while True:
       if msvcrt.kbhit():
           key = msvcrt.getch()
           print(key)   # just to show the result
12
votes

Use this code for find the which key pressed

from pynput import keyboard

def on_press(key):
    try:
        print('alphanumeric key {0} pressed'.format(
            key.char))
    except AttributeError:
        print('special key {0} pressed'.format(
            key))

def on_release(key):
    print('{0} released'.format(
        key))
    if key == keyboard.Key.esc:
        # Stop listener
        return False

# Collect events until released
with keyboard.Listener(
        on_press=on_press,
        on_release=on_release) as listener:
    listener.join()
7
votes

Use PyGame to have a window and then you can get the key events.

For the letter p:

import pygame, sys
import pygame.locals

pygame.init()
BLACK = (0,0,0)
WIDTH = 1280
HEIGHT = 1024
windowSurface = pygame.display.set_mode((WIDTH, HEIGHT), 0, 32)

windowSurface.fill(BLACK)

while True:
    for event in pygame.event.get():
        if event.key == pygame.K_p: # replace the 'p' to whatever key you wanted to be pressed
             pass #Do what you want to here
        if event.type == pygame.locals.QUIT:
             pygame.quit()
             sys.exit()
2
votes

I would suggest you use PyGame and add an event handle.

http://www.pygame.org/docs/ref/event.html

2
votes

So I made this ..kind of game.. based on this post (using msvcr library and Python 3.7).

The following is the "main function" of the game, that is detecting the keys pressed:

# Requiered libraries - - - -
import msvcrt
# - - - - - - - - - - - - - -


def _secret_key(self):
    # Get the key pressed by the user and check if he/she wins.

    bk = chr(10) + "-"*25 + chr(10)

    while True:

        print(bk + "Press any key(s)" + bk)
        #asks the user to type any key(s)

        kp = str(msvcrt.getch()).replace("b'", "").replace("'", "")
        # Store key's value.

        if r'\xe0' in kp:
            kp += str(msvcrt.getch()).replace("b'", "").replace("'", "")
            # Refactor the variable in case of multi press.

        if kp == r'\xe0\x8a':
            # If user pressed the secret key, the game ends.
            # \x8a is CTRL+F12, that's the secret key.

            print(bk + "CONGRATULATIONS YOU PRESSED THE SECRET KEYS!\a" + bk)
            print("Press any key to exit the game")
            msvcrt.getch()
            break
        else:
            print("    You pressed:'", kp + "', that's not the secret key(s)\n")
            if self.select_continue() == "n":
                if self.secondary_options():
                    self._main_menu()
                break

If you want the full source code of the porgram you can see it or download it from here:

The Secret Key Game (GitHub)

(note: the secret keypress is: Ctrl+F12)

I hope you can serve as an example and help for those who come to consult this information.

2
votes

You don't mention if this is a GUI program or not, but most GUI packages include a way to capture and handle keyboard input. For example, with tkinter (in Py3), you can bind to a certain event and then handle it in a function. For example:

import tkinter as tk

def key_handler(event=None):
    if event and event.keysym in ('s', 'p'):
        'do something'

r = tk.Tk()
t = tk.Text()
t.pack()
r.bind('<Key>', key_handler)
r.mainloop()

With the above, when you type into the Text widget, the key_handler routine gets called for each (or almost each) key you press.

2
votes

neoDev's comment at the question itself might be easy to miss, but it links to a solution not mentioned in any answer here.

There is no need to import keyboard with this solution.

Solution copied from this other question, all credits to @neoDev.

This worked for me on macOS Sierra and Python 2.7.10 and 3.6.3

import sys,tty,os,termios
def getkey():
    old_settings = termios.tcgetattr(sys.stdin)
    tty.setcbreak(sys.stdin.fileno())
    try:
        while True:
            b = os.read(sys.stdin.fileno(), 3).decode()
            if len(b) == 3:
                k = ord(b[2])
            else:
                k = ord(b)
            key_mapping = {
                127: 'backspace',
                10: 'return',
                32: 'space',
                9: 'tab',
                27: 'esc',
                65: 'up',
                66: 'down',
                67: 'right',
                68: 'left'
            }
            return key_mapping.get(k, chr(k))
    finally:
        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
try:
    while True:
        k = getkey()
        if k == 'esc':
            quit()
        else:
            print(k)
except (KeyboardInterrupt, SystemExit):
    os.system('stty sane')
    print('stopping.')
1
votes
key = cv2.waitKey(1)

This is from the openCV package. It detects a keypress without waiting.

-1
votes

You can use the keyboard module. Just open terminal and type:

pip install keyboard

This is the code:

import keyboard
while true:
    try:
        if keyboard.is_pressed('s'):
            print("stop")
        elif keyboard.is_pressed('p'):
            print("pause")
    except:
        pass