How to read a single character from the user?

后端 未结 23 2720
爱一瞬间的悲伤
爱一瞬间的悲伤 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:01

    My solution for python3, not depending on any pip packages.

    # precondition: import tty, sys
    def query_yes_no(question, default=True):
        """
        Ask the user a yes/no question.
        Returns immediately upon reading one-char answer.
        Accepts multiple language characters for yes/no.
        """
        if not sys.stdin.isatty():
            return default
        if default:
            prompt = "[Y/n]?"
            other_answers = "n"
        else:
            prompt = "[y/N]?"
            other_answers = "yjosiá"
    
        print(question,prompt,flush= True,end=" ")
        oldttysettings = tty.tcgetattr(sys.stdin.fileno())
        try:
            tty.setraw(sys.stdin.fileno())
            return not sys.stdin.read(1).lower() in other_answers
        except:
            return default
        finally:
            tty.tcsetattr(sys.stdin.fileno(), tty.TCSADRAIN , oldttysettings)
            sys.stdout.write("\r\n")
            tty.tcdrain(sys.stdin.fileno())
    
    0 讨论(0)
  • 2020-11-21 05:02

    I believe that this is one the most elegant solution.

    import os
    
    if os.name == 'nt':
        import msvcrt
        def getch():
            return msvcrt.getch().decode()
    else:
        import sys, tty, termios
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        def getch():
            try:
                tty.setraw(sys.stdin.fileno())
                ch = sys.stdin.read(1)
            finally:
                termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
            return ch
    

    and then use it in the code:

    if getch() == chr(ESC_ASCII_VALUE):
        print("ESC!")
    
    0 讨论(0)
  • 2020-11-21 05:03

    If you want to register only one single key press even if the user pressed it for more than once or kept pressing the key longer. To avoid getting multiple pressed inputs use the while loop and pass it.

    import keyboard
    
    while(True):
      if(keyboard.is_pressed('w')):
          s+=1
          while(keyboard.is_pressed('w')):
            pass
      if(keyboard.is_pressed('s')):
          s-=1
          while(keyboard.is_pressed('s')):
            pass
      print(s)
    
    0 讨论(0)
  • 2020-11-21 05:04

    Try using this: http://home.wlu.edu/~levys/software/kbhit.py It's non-blocking (that means that you can have a while loop and detect a key press without stopping it) and cross-platform.

    import os
    
    # Windows
    if os.name == 'nt':
        import msvcrt
    
    # Posix (Linux, OS X)
    else:
        import sys
        import termios
        import atexit
        from select import select
    
    
    class KBHit:
    
        def __init__(self):
            '''Creates a KBHit object that you can call to do various keyboard things.'''
    
            if os.name == 'nt':
                pass
    
            else:
    
                # Save the terminal settings
                self.fd = sys.stdin.fileno()
                self.new_term = termios.tcgetattr(self.fd)
                self.old_term = termios.tcgetattr(self.fd)
    
                # New terminal setting unbuffered
                self.new_term[3] = (self.new_term[3] & ~termios.ICANON & ~termios.ECHO)
                termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.new_term)
    
                # Support normal-terminal reset at exit
                atexit.register(self.set_normal_term)
    
    
        def set_normal_term(self):
            ''' Resets to normal terminal.  On Windows this is a no-op.
            '''
    
            if os.name == 'nt':
                pass
    
            else:
                termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old_term)
    
    
        def getch(self):
            ''' Returns a keyboard character after kbhit() has been called.
                Should not be called in the same program as getarrow().
            '''
    
            s = ''
    
            if os.name == 'nt':
                return msvcrt.getch().decode('utf-8')
    
            else:
                return sys.stdin.read(1)
    
    
        def getarrow(self):
            ''' Returns an arrow-key code after kbhit() has been called. Codes are
            0 : up
            1 : right
            2 : down
            3 : left
            Should not be called in the same program as getch().
            '''
    
            if os.name == 'nt':
                msvcrt.getch() # skip 0xE0
                c = msvcrt.getch()
                vals = [72, 77, 80, 75]
    
            else:
                c = sys.stdin.read(3)[2]
                vals = [65, 67, 66, 68]
    
            return vals.index(ord(c.decode('utf-8')))
    
    
        def kbhit(self):
            ''' Returns True if keyboard character was hit, False otherwise.
            '''
            if os.name == 'nt':
                return msvcrt.kbhit()
    
            else:
                dr,dw,de = select([sys.stdin], [], [], 0)
                return dr != []
    

    An example to use this:

    import kbhit
    
    kb = kbhit.KBHit()
    
    while(True): 
        print("Key not pressed") #Do something
        if kb.kbhit(): #If a key is pressed:
            k_in = kb.getch() #Detect what key was pressed
            print("You pressed ", k_in, "!") #Do something
    kb.set_normal_term()
    

    Or you could use the getch module from PyPi. But this would block the while loop

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

    if you just want to hold the screen so you can see the result on the terminal just write

    input()
    

    at the end of the code and it will hold the screen

    0 讨论(0)
  • 2020-11-21 05:06
    sys.stdin.read(1)
    

    will basically read 1 byte from STDIN.

    If you must use the method which does not wait for the \n you can use this code as suggested in previous answer:

    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()
    

    (taken from http://code.activestate.com/recipes/134892/)

    0 讨论(0)
提交回复
热议问题