ActiveState Code

Recipe 134892: getch()-like unbuffered character reading from stdin on both Windows and Unix


A small utility class to read single characters from standard input, on both Windows and UNIX systems. It provides a getch() function-like instance.

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class _Getch:
    """Gets a single character from standard input.  Does not echo to the
screen."""
    def __init__(self):
        try:
            self.impl = _GetchWindows()
        except ImportError:
            self.impl = _GetchUnix()

    def __call__(self): return self.impl()


class _GetchUnix:
    def __init__(self):
        import tty, sys

    def __call__(self):
        import sys, tty, termios
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch


class _GetchWindows:
    def __init__(self):
        import msvcrt

    def __call__(self):
        import msvcrt
        return msvcrt.getch()


getch = _Getch()

Comments

  1. 1. At 2:19 a.m. on 1 oct 2002, Tobias Polzin said:

    old python versions have to include TERMIOS. If you use an old python version (e.g. 1.5) you have to include "TERMIOS" additionally to "termios" and the "TCSADRAIN" is found in "TERMIOS".

  2. 2. At 2:20 p.m. on 18 may 2003, C Smith said:

    extended for MacOS.

    A few modifications make this work for the mac with Carbon,
    too: a modification to the _Getch to try one more import, a
    modification to the Unix init, and the inclusion of the instructions
    for the MacOS with Carbon support.
    
    
    class _Getch:
        """Gets a single character from standard input.  Does not echo to the
    screen."""
        def __init__(self):
            try:
                self.impl = _GetchWindows()
            except ImportError:
                try:
                self.impl = _GetchUnix()
                except ImportError:
                    self.impl = _GetchMacCarbon()
    
        def __call__(self): return self.impl()
    
    
    class _GetchUnix:
        def __init__(self):
            import tty, sys, termios # import termios now or else you'll get the Unix version on the Mac
    
        def __call__(self):
            import sys, tty, termios
            fd = sys.stdin.fileno()
            old_settings = termios.tcgetattr(fd)
            try:
                tty.setraw(sys.stdin.fileno())
                ch = sys.stdin.read(1)
            finally:
                termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
            return ch
    
    class _GetchWindows:
        def __init__(self):
            import msvcrt
    
        def __call__(self):
            import msvcrt
            return msvcrt.getch()
    
    
    class _GetchMacCarbon:
        """
        A function which returns the current ASCII key that is down;
        if no ASCII key is down, the null string is returned.  The
        page http://www.mactech.com/macintosh-c/chap02-1.html was
        very helpful in figuring out how to do this.
        """
        def __init__(self):
            import Carbon
    
        def __call__(self):
            import Carbon
            if Carbon.Evt.EventAvail(0x0008)[0]==0: # 0x0008 is the keyDownMask
                return ''
            else:
                #
                # The event contains the following info:
                # (what,msg,when,where,mod)=Carbon.Evt.GetNextEvent(0x0008)[1]
                #
                # The message (msg) contains the ASCII char which is
                # extracted with the 0x000000FF charCodeMask; this
                # number is converted to an ASCII character with chr() and
                # returned
                #
                (what,msg,when,where,mod)=Carbon.Evt.GetNextEvent(0x0008)[1]
                return chr(msg <pre>
    A few modifications make this work for the mac with Carbon,
    too: a modification to the _Getch to try one more import, a
    modification to the Unix init, and the inclusion of the instructions
    for the MacOS with Carbon support.
    

    (comment continued...)

  3. 3. At 2:20 p.m. on 18 may 2003, C Smith said:

    (...continued from previous comment)

    class _Getch:
        """Gets a single character from standard input.  Does not echo to the
    screen."""
        def __init__(self):
            try:
                self.impl = _GetchWindows()
            except ImportError:
                try:
                self.impl = _GetchUnix()
                except ImportError:
                    self.impl = _GetchMacCarbon()
    
        def __call__(self): return self.impl()
    
    
    class _GetchUnix:
        def __init__(self):
            import tty, sys, termios # import termios now or else you'll get the Unix version on the Mac
    
        def __call__(self):
            import sys, tty, termios
            fd = sys.stdin.fileno()
            old_settings = termios.tcgetattr(fd)
            try:
                tty.setraw(sys.stdin.fileno())
                ch = sys.stdin.read(1)
            finally:
                termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
            return ch
    
    class _GetchWindows:
        def __init__(self):
            import msvcrt
    
        def __call__(self):
            import msvcrt
            return msvcrt.getch()
    
    
    class _GetchMacCarbon:
        """
        A function which returns the current ASCII key that is down;
        if no ASCII key is down, the null string is returned.  The
        page http://www.mactech.com/macintosh-c/chap02-1.html was
        very helpful in figuring out how to do this.
        """
        def __init__(self):
            import Carbon
    
        def __call__(self):
            import Carbon
            if Carbon.Evt.EventAvail(0x0008)[0]==0: # 0x0008 is the keyDownMask
                return ''
            else:
                #
                # The event contains the following info:
                # (what,msg,when,where,mod)=Carbon.Evt.GetNextEvent(0x0008)[1]
                #
                # The message (msg) contains the ASCII char which is
                # extracted with the 0x000000FF charCodeMask; this
                # number is converted to an ASCII character with chr() and
                # returned
                #
                (what,msg,when,where,mod)=Carbon.Evt.GetNextEvent(0x0008)[1]
                return chr(msg
    

    </pre>

  4. 4. At 2:45 p.m. on 19 may 2003, C Smith said:

    erratum for MacOS modification.

    Sorry about the repeated text.  I see that the ampersand
    inside the &ltpre&gt; tag is what caused it...and it also caused
    the last line to appear incorrectly.  The last line should
    read
    
    return chr(msg &amp; 0x000000FF)
    
  5. 5. At 9:59 p.m. on 4 may 2005, C Smith said:

    updated for OS X. The following code (along with the _GetUnix and _GetWindows above) gives the correct getch behavior whether imported in the pythonIDE or in a Terminal script on the Mac. The order of trial imports is changed in _Getch because when in the IDE, the Unix import was succeeding. A single line in the _GetMacCarbon was added to see if the Carbon module has the Evt method. When the import succeeds when in the Terminal, the Carbon library there does not have the Evt method and so the import fails and the _GetchUnix() line is executed.

    I also found that the curses snippet at < http://www.pythonapocrypha.com/Chapter22/Chapter22.shtml > works in the Terminal but will cause the pythonIDE to quit without warning if you run it there.

    ###
    
    class _Getch:
        """Gets a single character from standard input.  Does not echo to the
    screen."""
        def __init__(self):
            try:
                self.impl = _GetchWindows()
            except ImportError:
                try:
                    self.impl = _GetchMacCarbon()
                except AttributeError:
                    self.impl = _GetchUnix()
    
        def __call__(self): return self.impl()
    
    class _GetchMacCarbon:
        """
        A function which returns the current ASCII key that is down;
        if no ASCII key is down, the null string is returned.  The
        page http://www.mactech.com/macintosh-c/chap02-1.html was
        very helpful in figuring out how to do this.
        """
        def __init__(self):
            import Carbon
            Carbon.Evt #see if it has this (in Unix, it doesn't)
    
        def __call__(self):
            import Carbon
            if Carbon.Evt.EventAvail(0x0008)[0]==0: # 0x0008 is the keyDownMask
                return ''
            else:
                #
                # The event contains the following info:
                # (what,msg,when,where,mod)=Carbon.Evt.GetNextEvent(0x0008)[1]
                #
                # The message (msg) contains the ASCII char which is
                # extracted with the 0x000000FF charCodeMask; this
                # number is converted to an ASCII character with chr() and
                # returned
                #
                (what,msg,when,where,mod)=Carbon.Evt.GetNextEvent(0x0008)[1]
                return chr(msg &amp; 0x000000FF)
    
    if __name__ == '__main__': # a little test
       print 'Press a key'
       inkey = _Getch()
       import sys
       for i in xrange(sys.maxint):
          k=inkey()
          if k&lt;&gt;'':break
       print 'you pressed ',k
    ###
    
  6. 6. At 6:41 p.m. on 15 mar 2006, Cullen Newsom said:

    getch with IDLE. I was unable to make this work in IDLE on OSX. I guess because of what IDLE seems to do to stdio. I always got "AttributeError".

  7. 7. At 5:14 p.m. on 29 mar 2008, qubodup qubodup said:

    Carbon less invasive? Hi, I tried the code on linux and got " ImportError: No module named Carbon"

    You should wrap the carbon loading in a "try:" I suppose (I'm not experienced enough to post an example, as I eg. don't know what to write into "except ImportError:" if I want nothing to happen.

  8. 8. At 7:40 p.m. on 21 jun 2009, galli.87 said:

    This should do the trick:

    class _Getch:
        """
        Gets a single character from standard input.  Does not echo to
        the screen.
        """
        def __init__(self):
            try:
                self.impl = _GetchWindows()
            except ImportError:
                try:
                    self.impl = _GetchMacCarbon()
                except(AttributeError, ImportError):
                    self.impl = _GetchUnix()
    
        def __call__(self): return self.impl()
    

Sign in to comment