How to read a single character from the user?

后端 未结 23 2658
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-11-21 04:28

Is there a way of reading one single character from the user input? For instance, they press one key at the terminal and it is returned (sort of like getch()).

相关标签:
23条回答
  • 2020-11-21 05:19

    The curses package in python can be used to enter "raw" mode for character input from the terminal with just a few statements. Curses' main use is to take over the screen for output, which may not be what you want. This code snippet uses print() statements instead, which are usable, but you must be aware of how curses changes line endings attached to output.

    #!/usr/bin/python3
    # Demo of single char terminal input in raw mode with the curses package.
    import sys, curses
    
    def run_one_char(dummy):
        'Run until a carriage return is entered'
        char = ' '
        print('Welcome to curses', flush=True)
        while ord(char) != 13:
            char = one_char()
    
    def one_char():
        'Read one character from the keyboard'
        print('\r? ', flush= True, end = '')
    
        ## A blocking single char read in raw mode. 
        char = sys.stdin.read(1)
        print('You entered %s\r' % char)
        return char
    
    ## Must init curses before calling any functions
    curses.initscr()
    ## To make sure the terminal returns to its initial settings,
    ## and to set raw mode and guarantee cleanup on exit. 
    curses.wrapper(run_one_char)
    print('Curses be gone!')
    
    0 讨论(0)
  • 2020-11-21 05:22

    The ActiveState recipe quoted verbatim in two answers is over-engineered. It can be boiled down to this:

    def _find_getch():
        try:
            import termios
        except ImportError:
            # Non-POSIX. Return msvcrt's (Windows') getch.
            import msvcrt
            return msvcrt.getch
    
        # POSIX system. Create and return a getch that manipulates the tty.
        import sys, tty
        def _getch():
            fd = sys.stdin.fileno()
            old_settings = termios.tcgetattr(fd)
            try:
                tty.setraw(fd)
                ch = sys.stdin.read(1)
            finally:
                termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
            return ch
    
        return _getch
    
    getch = _find_getch()
    
    0 讨论(0)
  • 2020-11-21 05:24

    This is NON-BLOCKING, reads a key and and stores it in keypress.key.

    import Tkinter as tk
    
    
    class Keypress:
        def __init__(self):
            self.root = tk.Tk()
            self.root.geometry('300x200')
            self.root.bind('<KeyPress>', self.onKeyPress)
    
        def onKeyPress(self, event):
            self.key = event.char
    
        def __eq__(self, other):
            return self.key == other
    
        def __str__(self):
            return self.key
    

    in your programm

    keypress = Keypress()
    
    while something:
       do something
       if keypress == 'c':
            break
       elif keypress == 'i': 
           print('info')
       else:
           print("i dont understand %s" % keypress)
    
    0 讨论(0)
  • 2020-11-21 05:27

    Also worth trying is the readchar library, which is in part based on the ActiveState recipe mentioned in other answers.

    Installation:

    pip install readchar
    

    Usage:

    import readchar
    print("Reading a char:")
    print(repr(readchar.readchar()))
    print("Reading a key:")
    print(repr(readchar.readkey()))
    

    Tested on Windows and Linux with Python 2.7.

    On Windows, only keys which map to letters or ASCII control codes are supported (Backspace, Enter, Esc, Tab, Ctrl+letter). On GNU/Linux (depending on exact terminal, perhaps?) you also get Insert, Delete, Pg Up, Pg Dn, Home, End and F n keys... but then, there's issues separating these special keys from an Esc.

    Caveat: Like with most (all?) answers in here, signal keys like Ctrl+C, Ctrl+D and Ctrl+Z are caught and returned (as '\x03', '\x04' and '\x1a' respectively); your program can be come difficult to abort.

    0 讨论(0)
  • 2020-11-21 05:27

    The ActiveState's recipe seems to contain a little bug for "posix" systems that prevents Ctrl-C from interrupting (I'm using Mac). If I put the following code in my script:

    while(True):
        print(getch())
    

    I will never be able to terminate the script with Ctrl-C, and I have to kill my terminal to escape.

    I believe the following line is the cause, and it's also too brutal:

    tty.setraw(sys.stdin.fileno())
    

    Asides from that, package tty is not really needed, termios is enough to handle it.

    Below is the improved code that works for me (Ctrl-C will interrupt), with the extra getche function that echo the char as you type:

    if sys.platform == 'win32':
        import msvcrt
        getch = msvcrt.getch
        getche = msvcrt.getche
    else:
        import sys
        import termios
        def __gen_ch_getter(echo):
            def __fun():
                fd = sys.stdin.fileno()
                oldattr = termios.tcgetattr(fd)
                newattr = oldattr[:]
                try:
                    if echo:
                        # disable ctrl character printing, otherwise, backspace will be printed as "^?"
                        lflag = ~(termios.ICANON | termios.ECHOCTL)
                    else:
                        lflag = ~(termios.ICANON | termios.ECHO)
                    newattr[3] &= lflag
                    termios.tcsetattr(fd, termios.TCSADRAIN, newattr)
                    ch = sys.stdin.read(1)
                    if echo and ord(ch) == 127: # backspace
                        # emulate backspace erasing
                        # https://stackoverflow.com/a/47962872/404271
                        sys.stdout.write('\b \b')
                finally:
                    termios.tcsetattr(fd, termios.TCSADRAIN, oldattr)
                return ch
            return __fun
        getch = __gen_ch_getter(False)
        getche = __gen_ch_getter(True)
    

    References:

    • https://pypi.python.org/pypi/getch
    0 讨论(0)
提交回复
热议问题