问题:Python从用户读取单个字符
有没有一种方法可以从用户输入中读取单个字符?例如,他们在终端上按一个键,然后将其返回(类似getch())。我知道Windows中有一个功能,但是我想要跨平台的功能。
回答 0
以下是指向该站点的链接,该站点说明了如何在Windows,Linux和OSX中读取单个字符:http : //code.activestate.com/recipes/134892/
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()回答 1
sys.stdin.read(1)基本上将从STDIN读取1个字节。
如果必须使用不等待的方法,则\n可以按照先前答案中的建议使用此代码:
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()回答 2
在两个答案中逐字引用的ActiveState 配方经过了精心设计。可以归结为:
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()回答 3
还值得尝试的是readchar库,该库部分基于其他答案中提到的ActiveState配方。
安装:
pip install readchar用法:
import readchar
print("Reading a char:")
print(repr(readchar.readchar()))
print("Reading a key:")
print(repr(readchar.readkey()))在Windows和Linux上使用Python 2.7进行了测试。
在Windows上,只有映射到字母或ASCII控制代码键的支持(Backspace,Enter,Esc,Tab,Ctrl+ 字母)。在GNU / Linux(取决于具体终端上,也许?),你也可以得到Insert,Delete,Pg Up,Pg Dn,Home,End和键…但随后,有分离的这些特殊键问题。F nEsc
警告:像这里的大多数(全部?)答案一样,信号键(如Ctrl+ C,Ctrl+ D和Ctrl+)Z被捕获并返回(分别为'\x03','\x04'和'\x1a');您的程序可能很难终止。
回答 4
替代方法:
import os
import sys    
import termios
import fcntl
def getch():
  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 1:            
      try:
        c = sys.stdin.read(1)
        break
      except IOError: pass
  finally:
    termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
    fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
  return c从这篇博客文章中。
回答 5
如果按+ 或+ ,则此代码基于此处,将正确引发KeyboardInterrupt和EOFError 。CtrlCCtrlD
应该可以在Windows和Linux上使用。可从原始来源获得OS X版本。
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): 
        char = self.impl()
        if char == '\x03':
            raise KeyboardInterrupt
        elif char == '\x04':
            raise EOFError
        return char
class _GetchUnix:
    def __init__(self):
        import tty
        import sys
    def __call__(self):
        import sys
        import tty
        import 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()回答 6
(当前)排名靠前的答案(带有ActiveState代码)过于复杂。当仅一个函数就足够时,我看不出使用类的理由。以下是两个实现相同功能但具有更易读代码的实现。
这两种实现:
- 在Python 2或Python 3中工作正常
- 在Windows,OSX和Linux上工作
- 只读取一个字节(即,他们不等待换行符)
- 不依赖任何外部库
- 是自包含的(函数定义之外没有代码)
版本1:可读又简单
def getChar():
    try:
        # for Windows-based systems
        import msvcrt # If successful, we are on Windows
        return msvcrt.getch()
    except ImportError:
        # for POSIX-based systems (with termios & tty support)
        import tty, sys, termios  # raises ImportError if unsupported
        fd = sys.stdin.fileno()
        oldSettings = termios.tcgetattr(fd)
        try:
            tty.setcbreak(fd)
            answer = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, oldSettings)
        return answer版本2:避免重复导入和异常处理:
[编辑]我错过了ActiveState代码的一项优势。如果您打算多次读取字符,则该代码避免了在类似Unix的系统上重复执行Windows导入和ImportError异常处理的(可忽略的)成本。尽管您可能应该更关注代码的可读性而不是可以忽略的优化,但这是一种替代方法(它与Louis的答案类似,但是getChar()是自包含的),其功能与ActiveState代码相同,并且更具可读性:
def getChar():
    # figure out which function to use once, and store it in _func
    if "_func" not in getChar.__dict__:
        try:
            # for Windows-based systems
            import msvcrt # If successful, we are on Windows
            getChar._func=msvcrt.getch
        except ImportError:
            # for POSIX-based systems (with termios & tty support)
            import tty, sys, termios # raises ImportError if unsupported
            def _ttyRead():
                fd = sys.stdin.fileno()
                oldSettings = termios.tcgetattr(fd)
                try:
                    tty.setcbreak(fd)
                    answer = sys.stdin.read(1)
                finally:
                    termios.tcsetattr(fd, termios.TCSADRAIN, oldSettings)
                return answer
            getChar._func=_ttyRead
    return getChar._func()上面的两个getChar()版本的示例代码:
from __future__ import print_function # put at top of file if using Python 2
# Example of a prompt for one character of input
promptStr   = "Please give me a character:"
responseStr = "Thank you for giving me a '{}'."
print(promptStr, end="\n> ")
answer = getChar()
print("\n")
print(responseStr.format(answer))回答 7
这可能是上下文管理器的用例。撇开Windows操作系统的津贴,这是我的建议:
#!/usr/bin/env python3
# file: 'readchar.py'
"""
Implementation of a way to get a single character of input
without waiting for the user to hit <Enter>.
(OS is Linux, Ubuntu 14.04)
"""
import tty, sys, termios
class ReadChar():
    def __enter__(self):
        self.fd = sys.stdin.fileno()
        self.old_settings = termios.tcgetattr(self.fd)
        tty.setraw(sys.stdin.fileno())
        return sys.stdin.read(1)
    def __exit__(self, type, value, traceback):
        termios.tcsetattr(self.fd, termios.TCSADRAIN, self.old_settings)
def test():
    while True:
        with ReadChar() as rc:
            char = rc
        if ord(char) <= 32:
            print("You entered character with ordinal {}."\
                        .format(ord(char)))
        else:
            print("You entered character '{}'."\
                        .format(char))
        if char in "^C^D":
            sys.exit()
if __name__ == "__main__":
    test()回答 8
尝试使用以下方法:http : //home.wlu.edu/~levys/software/kbhit.py 它是非阻塞的(这意味着您可以进行while循环并检测按键而无需停止它)和跨平台。
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 != []使用此示例:
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()回答 9
这是NON-BLOCKING,它读取一个密钥并将其存储在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在您的程序中
keypress = Keypress()
while something:
   do something
   if keypress == 'c':
        break
   elif keypress == 'i': 
       print('info')
   else:
       print("i dont understand %s" % keypress)回答 10
此处的答案是有益的,但是我还想一种方法来异步获取按键并在单独的事件中触发按键,所有这些操作都是以线程安全的,跨平台的方式进行的。PyGame对我来说也太肿。因此,我做了以下工作(在Python 2.7中,但我怀疑它很容易移植),我想在这里分享一下,以防对其他人有用。我将其存储在名为keyPress.py的文件中。
class _Getch:
    """Gets a single character from standard input.  Does not echo to the
screen. From http://code.activestate.com/recipes/134892/"""
    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()
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
        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 & 0x000000FF)
import threading
# From  https://stackoverflow.com/a/2022629/2924421
class Event(list):
    def __call__(self, *args, **kwargs):
        for f in self:
            f(*args, **kwargs)
    def __repr__(self):
        return "Event(%s)" % list.__repr__(self)            
def getKey():
    inkey = _Getch()
    import sys
    for i in xrange(sys.maxint):
        k=inkey()
        if k<>'':break
    return k
class KeyCallbackFunction():
    callbackParam = None
    actualFunction = None
    def __init__(self, actualFunction, callbackParam):
        self.actualFunction = actualFunction
        self.callbackParam = callbackParam
    def doCallback(self, inputKey):
        if not self.actualFunction is None:
            if self.callbackParam is None:
                callbackFunctionThread = threading.Thread(target=self.actualFunction, args=(inputKey,))
            else:
                callbackFunctionThread = threading.Thread(target=self.actualFunction, args=(inputKey,self.callbackParam))
            callbackFunctionThread.daemon = True
            callbackFunctionThread.start()
class KeyCapture():
    gotKeyLock = threading.Lock()
    gotKeys = []
    gotKeyEvent = threading.Event()
    keyBlockingSetKeyLock = threading.Lock()
    addingEventsLock = threading.Lock()
    keyReceiveEvents = Event()
    keysGotLock = threading.Lock()
    keysGot = []
    keyBlockingKeyLockLossy = threading.Lock()
    keyBlockingKeyLossy = None
    keyBlockingEventLossy = threading.Event()
    keysBlockingGotLock = threading.Lock()
    keysBlockingGot = []
    keyBlockingGotEvent = threading.Event()
    wantToStopLock = threading.Lock()
    wantToStop = False
    stoppedLock = threading.Lock()
    stopped = True
    isRunningEvent = False
    getKeyThread = None
    keyFunction = None
    keyArgs = None
    # Begin capturing keys. A seperate thread is launched that
    # captures key presses, and then these can be received via get,
    # getAsync, and adding an event via addEvent. Note that this
    # will prevent the system to accept keys as normal (say, if
    # you are in a python shell) because it overrides that key
    # capturing behavior.
    # If you start capture when it's already been started, a
    # InterruptedError("Keys are still being captured")
    # will be thrown
    # Note that get(), getAsync() and events are independent, so if a key is pressed:
    #
    # 1: Any calls to get() that are waiting, with lossy on, will return
    #    that key
    # 2: It will be stored in the queue of get keys, so that get() with lossy
    #    off will return the oldest key pressed not returned by get() yet.
    # 3: All events will be fired with that key as their input
    # 4: It will be stored in the list of getAsync() keys, where that list
    #    will be returned and set to empty list on the next call to getAsync().
    # get() call with it, aand add it to the getAsync() list.
    def startCapture(self, keyFunction=None, args=None):
        # Make sure we aren't already capturing keys
        self.stoppedLock.acquire()
        if not self.stopped:
            self.stoppedLock.release()
            raise InterruptedError("Keys are still being captured")
            return
        self.stopped = False
        self.stoppedLock.release()
        # If we have captured before, we need to allow the get() calls to actually
        # wait for key presses now by clearing the event
        if self.keyBlockingEventLossy.is_set():
            self.keyBlockingEventLossy.clear()
        # Have one function that we call every time a key is captured, intended for stopping capture
        # as desired
        self.keyFunction = keyFunction
        self.keyArgs = args
        # Begin capturing keys (in a seperate thread)
        self.getKeyThread = threading.Thread(target=self._threadProcessKeyPresses)
        self.getKeyThread.daemon = True
        self.getKeyThread.start()
        # Process key captures (in a seperate thread)
        self.getKeyThread = threading.Thread(target=self._threadStoreKeyPresses)
        self.getKeyThread.daemon = True
        self.getKeyThread.start()
    def capturing(self):
        self.stoppedLock.acquire()
        isCapturing = not self.stopped
        self.stoppedLock.release()
        return isCapturing
    # Stops the thread that is capturing keys on the first opporunity
    # has to do so. It usually can't stop immediately because getting a key
    # is a blocking process, so this will probably stop capturing after the
    # next key is pressed.
    #
    # However, Sometimes if you call stopCapture it will stop before starting capturing the
    # next key, due to multithreading race conditions. So if you want to stop capturing
    # reliably, call stopCapture in a function added via addEvent. Then you are
    # guaranteed that capturing will stop immediately after the rest of the callback
    # functions are called (before starting to capture the next key).
    def stopCapture(self):
        self.wantToStopLock.acquire()
        self.wantToStop = True 
        self.wantToStopLock.release()
    # Takes in a function that will be called every time a key is pressed (with that
    # key passed in as the first paramater in that function)
    def addEvent(self, keyPressEventFunction, args=None):   
        self.addingEventsLock.acquire()
        callbackHolder = KeyCallbackFunction(keyPressEventFunction, args)
        self.keyReceiveEvents.append(callbackHolder.doCallback)
        self.addingEventsLock.release()
    def clearEvents(self):
        self.addingEventsLock.acquire()
        self.keyReceiveEvents = Event()
        self.addingEventsLock.release()
    # Gets a key captured by this KeyCapture, blocking until a key is pressed.
    # There is an optional lossy paramater:
    # If True all keys before this call are ignored, and the next pressed key
    #   will be returned.
    # If False this will return the oldest key captured that hasn't
    #   been returned by get yet. False is the default.
    def get(self, lossy=False):
        if lossy:
            # Wait for the next key to be pressed
            self.keyBlockingEventLossy.wait()
            self.keyBlockingKeyLockLossy.acquire()
            keyReceived = self.keyBlockingKeyLossy
            self.keyBlockingKeyLockLossy.release()
            return keyReceived
        else:
            while True:
                # Wait until a key is pressed
                self.keyBlockingGotEvent.wait()
                # Get the key pressed
                readKey = None
                self.keysBlockingGotLock.acquire()
                # Get a key if it exists
                if len(self.keysBlockingGot) != 0:
                    readKey = self.keysBlockingGot.pop(0)
                # If we got the last one, tell us to wait
                if len(self.keysBlockingGot) == 0:
                    self.keyBlockingGotEvent.clear()
                self.keysBlockingGotLock.release()
                # Process the key (if it actually exists)
                if not readKey is None:
                    return readKey
                # Exit if we are stopping
                self.wantToStopLock.acquire()
                if self.wantToStop:
                    self.wantToStopLock.release()
                    return None
                self.wantToStopLock.release()
    def clearGetList(self):
        self.keysBlockingGotLock.acquire()
        self.keysBlockingGot = []
        self.keysBlockingGotLock.release()
    # Gets a list of all keys pressed since the last call to getAsync, in order
    # from first pressed, second pressed, .., most recent pressed
    def getAsync(self):
        self.keysGotLock.acquire();
        keysPressedList = list(self.keysGot)
        self.keysGot = []
        self.keysGotLock.release()
        return keysPressedList
    def clearAsyncList(self):
        self.keysGotLock.acquire();
        self.keysGot = []
        self.keysGotLock.release();
    def _processKey(self, readKey):
        # Append to list for GetKeyAsync
        self.keysGotLock.acquire()
        self.keysGot.append(readKey)
        self.keysGotLock.release()
        # Call lossy blocking key events
        self.keyBlockingKeyLockLossy.acquire()
        self.keyBlockingKeyLossy = readKey
        self.keyBlockingEventLossy.set()
        self.keyBlockingEventLossy.clear()
        self.keyBlockingKeyLockLossy.release()
        # Call non-lossy blocking key events
        self.keysBlockingGotLock.acquire()
        self.keysBlockingGot.append(readKey)
        if len(self.keysBlockingGot) == 1:
            self.keyBlockingGotEvent.set()
        self.keysBlockingGotLock.release()
        # Call events added by AddEvent
        self.addingEventsLock.acquire()
        self.keyReceiveEvents(readKey)
        self.addingEventsLock.release()
    def _threadProcessKeyPresses(self):
        while True:
            # Wait until a key is pressed
            self.gotKeyEvent.wait()
            # Get the key pressed
            readKey = None
            self.gotKeyLock.acquire()
            # Get a key if it exists
            if len(self.gotKeys) != 0:
                readKey = self.gotKeys.pop(0)
            # If we got the last one, tell us to wait
            if len(self.gotKeys) == 0:
                self.gotKeyEvent.clear()
            self.gotKeyLock.release()
            # Process the key (if it actually exists)
            if not readKey is None:
                self._processKey(readKey)
            # Exit if we are stopping
            self.wantToStopLock.acquire()
            if self.wantToStop:
                self.wantToStopLock.release()
                break
            self.wantToStopLock.release()
    def _threadStoreKeyPresses(self):
        while True:
            # Get a key
            readKey = getKey()
            # Run the potential shut down function
            if not self.keyFunction is None:
                self.keyFunction(readKey, self.keyArgs)
            # Add the key to the list of pressed keys
            self.gotKeyLock.acquire()
            self.gotKeys.append(readKey)
            if len(self.gotKeys) == 1:
                self.gotKeyEvent.set()
            self.gotKeyLock.release()
            # Exit if we are stopping
            self.wantToStopLock.acquire()
            if self.wantToStop:
                self.wantToStopLock.release()
                self.gotKeyEvent.set()
                break
            self.wantToStopLock.release()
        # If we have reached here we stopped capturing
        # All we need to do to clean up is ensure that
        # all the calls to .get() now return None.
        # To ensure no calls are stuck never returning,
        # we will leave the event set so any tasks waiting
        # for it immediately exit. This will be unset upon
        # starting key capturing again.
        self.stoppedLock.acquire()
        # We also need to set this to True so we can start up
        # capturing again.
        self.stopped = True
        self.stopped = True
        self.keyBlockingKeyLockLossy.acquire()
        self.keyBlockingKeyLossy = None
        self.keyBlockingEventLossy.set()
        self.keyBlockingKeyLockLossy.release()
        self.keysBlockingGotLock.acquire()
        self.keyBlockingGotEvent.set()
        self.keysBlockingGotLock.release()
        self.stoppedLock.release()这个想法是,您可以简单地调用keyPress.getKey(),它将从键盘读取一个键,然后将其返回。
如果您还想要更多,我做了一个KeyCapture对象。您可以通过类似的方式创建一个keys = keyPress.KeyCapture()。
然后,您可以做三件事:
addEvent(functionName)接受具有一个参数的任何函数。然后,每次按下某个键时,将使用该键的字符串作为输入来调用此函数。它们在单独的线程中运行,因此您可以在其中阻塞所有所需的消息,并且不会弄乱KeyCapturer的功能,也不会延迟其他事件。
get()以与以前相同的阻塞方式返回键。现在需要在这里,因为密钥现在是通过KeyCapture对象捕获的,因此keyPress.getKey()会与该行为发生冲突,并且由于一次只能捕获一个密钥,因此它们都会丢失某些密钥。同样,假设用户按“ a”,然后按“ b”,即您叫get(),用户按“ c”。该get()调用将立即返回“ a”,然后再次调用它将返回“ b”,然后返回“ c”。如果再次调用它,它将阻塞直到按下另一个键。这样可以确保您不会遗漏任何按键,如果需要的话,可以采用阻塞方式。因此,这种方式keyPress.getKey()与以前有所不同
如果您想要getKey()返回的行为,get(lossy=True)则类似于get(),只是它仅返回在调用之后按下的键get()。因此,在上面的示例中,get()将阻塞直到用户按下“ c”,然后如果再次调用它,它将阻塞直到按下另一个键。
getAsync()有点不同。它是为需要大量处理的事物而设计的,然后偶尔返回并检查按下了哪些键。因此,按从最旧按键到最新按键的顺序getAsync()返回自上次调用以来所有按键的列表getAsync()。它也不会阻塞,这意味着如果自上次调用以来没有按键被按下getAsync(),[]则将返回一个空   值。
要真正开始捕获键,您需要使用上面制作keys.startCapture()的keys对象进行调用。startCapture是非阻塞的,只需启动一个仅记录按键操作的线程,然后启动另一个线程处理按键操作。有两个线程可确保记录按键的线程不会丢失任何按键。
如果要停止捕获密钥,可以拨打电话keys.stopCapture(),它将停止捕获密钥。但是,由于捕获键是一项阻塞操作,线程捕获键可能在调用后又捕获了一个键stopCapture()。
为防止这种情况,您可以将一个可选参数传入 startCapture(functionName, args)一个函数,该函数仅执行类似的检查键是否等于“ c”然后退出。重要的是此功能在执行之前几乎不起作用,例如,在这里睡觉会导致我们错过按键。
但是,如果stopCapture()在此函数中调用,则键捕获将立即停止,而不再尝试捕获,并且所有get()调用将立即返回,如果尚未按下任何键,则返回None。
另外,由于get()并getAsync()存储了所有先前按下的键(直到检索到它们),因此您可以调用clearGetList()并clearAsyncList()忘记先前按下的键。
请注意get(),getAsync()和事件是独立的,因此如果按下一个键:1 get().对该键的一个呼叫正在等待且有损打开,将返回该键。其他等待的呼叫(如果有)将继续等待。2.该密钥将存储在获取密钥的队列中,以便get()在有损关闭的情况下将返回get()尚未返回的最旧的密钥。3.将使用该键作为输入触发所有事件。4.该键将存储在getAsync()键列表中,该列表将返回并在下一次调用时设置为空列表。getAsync()
如果这一切都太多了,那么这里是一个用例示例:
import keyPress
import time
import threading
def KeyPressed(k, printLock):
    printLock.acquire()
    print "Event: " + k
    printLock.release()
    time.sleep(4)
    printLock.acquire()
    print "Event after delay: " + k
    printLock.release()
def GetKeyBlocking(keys, printLock):    
    while keys.capturing():
        keyReceived = keys.get()
        time.sleep(1)
        printLock.acquire()
        if not keyReceived is None:
            print "Block " + keyReceived
        else:
            print "Block None"
        printLock.release()
def GetKeyBlockingLossy(keys, printLock):   
    while keys.capturing():
        keyReceived = keys.get(lossy=True)
        time.sleep(1)
        printLock.acquire()
        if not keyReceived is None:
            print "Lossy: " + keyReceived
        else:
            print "Lossy: None"
        printLock.release()
def CheckToClose(k, (keys, printLock)):
    printLock.acquire()
    print "Close: " + k
    printLock.release()
    if k == "c":
        keys.stopCapture()
printLock = threading.Lock()
print "Press a key:"
print "You pressed: " + keyPress.getKey()
print ""
keys = keyPress.KeyCapture()
keys.addEvent(KeyPressed, printLock)
print "Starting capture"
keys.startCapture(CheckToClose, (keys, printLock))
getKeyBlockingThread = threading.Thread(target=GetKeyBlocking, args=(keys, printLock))
getKeyBlockingThread.daemon = True
getKeyBlockingThread.start()
getKeyBlockingThreadLossy = threading.Thread(target=GetKeyBlockingLossy, args=(keys, printLock))
getKeyBlockingThreadLossy.daemon = True
getKeyBlockingThreadLossy.start()
while keys.capturing():
    keysPressed = keys.getAsync()
    printLock.acquire()
    if keysPressed != []:
        print "Async: " + str(keysPressed)
    printLock.release()
    time.sleep(1)
print "done capturing"从我进行的简单测试来看,这对我来说效果很好,但是如果我错过了一些事情,我也会很高兴也接受其他反馈。
我也张贴在这里。
回答 11
在其他答案之一中,有一个注释提到了cbreak模式,这对于Unix实现很重要,因为您通常不希望^ C(KeyboardError)被getchar占用(就像将终端设置为raw模式时那样),其他答案)。
另一个重要的细节是,如果您要读取一个字符而不是一个字节,则应从输入流中读取4个字节,因为这是单个字符在UTF-8中包含的最大字节数(Python 3+ )。仅读取一个字节会为多字节字符(例如键盘箭头)产生意外结果。
这是我为Unix更改的实现:
import contextlib
import os
import sys
import termios
import tty
_MAX_CHARACTER_BYTE_LENGTH = 4
@contextlib.contextmanager
def _tty_reset(file_descriptor):
    """
    A context manager that saves the tty flags of a file descriptor upon
    entering and restores them upon exiting.
    """
    old_settings = termios.tcgetattr(file_descriptor)
    try:
        yield
    finally:
        termios.tcsetattr(file_descriptor, termios.TCSADRAIN, old_settings)
def get_character(file=sys.stdin):
    """
    Read a single character from the given input stream (defaults to sys.stdin).
    """
    file_descriptor = file.fileno()
    with _tty_reset(file_descriptor):
        tty.setcbreak(file_descriptor)
        return os.read(file_descriptor, _MAX_CHARACTER_BYTE_LENGTH)回答 12
尝试使用pygame:
import pygame
pygame.init()             // eliminate error, pygame.error: video system not initialized
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:
    d = "space key"
print "You pressed the", d, "."回答 13
ActiveState的配方似乎包含“ posix”系统的一个小错误,可以防止Ctrl-C中断(我使用的是Mac)。如果我在脚本中输入以下代码:
while(True):
    print(getch())我将永远无法使用来终止脚本Ctrl-C,并且必须杀死终端以逃脱。
我认为以下原因是造成这一点的原因,而且它也太残酷了:
tty.setraw(sys.stdin.fileno())除此之外,tty实际上并不需要打包,termios足以处理它。
下面是对我有用的改进代码(Ctrl-C将被中断),并具有在getche您键入时回显char 的额外功能:
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)参考文献:
回答 14
cursespython中的软件包可用于进入“原始”模式,以从终端输入字符而仅需少量语句。Curses的主要用途是接管屏幕进行输出,这可能不是您想要的。该代码段print()改为使用可用的语句,但您必须了解curses如何更改连接到输出的行尾。
#!/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!')回答 15
如果我正在做复杂的事情,我将使用诅咒来读取键。但是很多时候我只想要一个简单的Python 3脚本,该脚本使用标准库并且可以读取箭头键,所以我这样做:
import sys, termios, tty
key_Enter = 13
key_Esc = 27
key_Up = '\033[A'
key_Dn = '\033[B'
key_Rt = '\033[C'
key_Lt = '\033[D'
fdInput = sys.stdin.fileno()
termAttr = termios.tcgetattr(0)
def getch():
    tty.setraw(fdInput)
    ch = sys.stdin.buffer.raw.read(4).decode(sys.stdin.encoding)
    if len(ch) == 1:
        if ord(ch) < 32 or ord(ch) > 126:
            ch = ord(ch)
    elif ord(ch[0]) == 27:
        ch = '\033' + ch[1:]
    termios.tcsetattr(fdInput, termios.TCSADRAIN, termAttr)
    return ch回答 16
我对python3的解决方案,不依赖于任何pip包。
# 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())回答 17
我相信这是最优雅的解决方案之一。
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然后在代码中使用它:
if getch() == chr(ESC_ASCII_VALUE):
    print("ESC!")回答 18
接受的答案对我而言效果不佳(我按住一个键,什么也不会发生,然后按另一个键便可以使用)。
在了解了curses模块之后,确实看起来是正确的方法。现在它可以通过Windows光标(可通过pip获得)用于Windows ,因此您可以以与平台无关的方式进行编程。这是一个受YouTube上不错的教程启发的示例:
import curses                                                                                                                                       
def getkey(stdscr):
    curses.curs_set(0)
    while True:
        key = stdscr.getch()
        if key != -1:
            break
    return key
if __name__ == "__main__":
    print(curses.wrapper(getkey))使用.py扩展名保存它,或curses.wrapper(getkey)在交互模式下运行。
回答 19
在这里回答:python中的raw_input,而无需按Enter
使用此代码-
from tkinter import Tk, Frame
def __set_key(e, root):
    """
    e - event with attribute 'char', the released key
    """
    global key_pressed
    if e.char:
        key_pressed = e.char
        root.destroy()
def get_key(msg="Press any key ...", time_to_sleep=3):
    """
    msg - set to empty string if you don't want to print anything
    time_to_sleep - default 3 seconds
    """
    global key_pressed
    if msg:
        print(msg)
    key_pressed = None
    root = Tk()
    root.overrideredirect(True)
    frame = Frame(root, width=0, height=0)
    frame.bind("<KeyRelease>", lambda f: __set_key(f, root))
    frame.pack()
    root.focus_set()
    frame.focus_set()
    frame.focus_force()  # doesn't work in a while loop without it
    root.after(time_to_sleep * 1000, func=root.destroy)
    root.mainloop()
    root = None  # just in case
    return key_pressed
def __main():
        c = None
        while not c:
                c = get_key("Choose your weapon ... ", 2)
        print(c)
if __name__ == "__main__":
    __main()参考:https : //github.com/unfor19/mg-tools/blob/master/mgtools/get_key_pressed.py
回答 20
如果您只想注册一个按键,即使用户多次按下该按键或保持按键按下时间更长。为避免获得多个按下的输入,请使用while循环并将其传递。
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)回答 21
如果您只想按住屏幕,以便可以在终端上看到结果,只需编写
input()在代码末尾,它将保留屏幕
回答 22
内置的raw_input应该会有所帮助。
for i in range(3):
    print ("So much work to do!")
k = raw_input("Press any key to continue...")
print ("Ok, back to work.")
