Python cross-platform listening for keypresses?

rennat picture rennat · Feb 18, 2011 · Viewed 19.2k times · Source

I need to listen for certain keypresses in a python terminal program without pausing execution with raw_input. I've seen people use a few windows specific ways of listening for keystrokes and I've seen people use large modules like tkinter and pygame which I want to avoid.

Is there a lightweight module out there that does this cross platform (at least ubuntu, windows, mac)? or is there a way to use just the event system from tkinter, pygame, etc...?

If not, how should I approach tackling this? My first thought is to redirect stdin to another process and keep checking to see if it contains one of my event keys.


edit

Thank you @unutbu for taking the time to mark this question that is 3 years old and successfully answered as a duplicate of another question whose answers do not apply to this question because I specifically asked about a non-blocking solution.

Answer

scoffey picture scoffey · Feb 18, 2011

I don't know of any cross-platform lightweight module that listens for keypresses. But here's a suggestion in case you want to implement something simple:

Check out this question on getting a single keypress at a time in the Python FAQ. You could experiment a bit with blocking reads from sys.stdin and threading. But this may only work on Unix. On Windows, you can use msvcrt.kbhit.

Combining the keypress recipe from the Python FAQ and the msvcrt module, the resulting kbhit function would go like this:

try:
    from msvcrt import kbhit
except ImportError:
    import termios, fcntl, sys, os
    def kbhit():
        fd = sys.stdin.fileno()
        oldterm = termios.tcgetattr(fd)
        newattr = termios.tcgetattr(fd)
        newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
        termios.tcsetattr(fd, termios.TCSANOW, newattr)
        oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
        fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
        try:
            while True:
                try:
                    c = sys.stdin.read(1)
                    return True
                except IOError:
                    return False
        finally:
            termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
            fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)