사용자 입력에서 하나의 단일 문자를 읽는 방법이 있습니까? 예를 들어 터미널에서 하나의 키를 누르면 반환됩니다 (정렬 한 정렬 getch()
). Windows에 기능이 있다는 것을 알고 있지만 플랫폼 간 무언가를 원합니다.
사용자 입력에서 하나의 단일 문자를 읽는 방법이 있습니까? 예를 들어 터미널에서 하나의 키를 누르면 반환됩니다 (정렬 한 정렬 getch()
). Windows에 기능이 있다는 것을 알고 있지만 플랫폼 간 무언가를 원합니다.
답변:
다음은 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()
ImportError
예외가 if-statement와 같은 방식 으로 사용되는 것을 좋아하지 않는다 . 왜 OS를 확인하기 위해 platform.system ()을 호출하지 않습니까?
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()
두 가지 답변으로 구두 인용 된 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()
0
.
readchar 라이브러리도 시도해 볼 가치가 있습니다.이 라이브러리는 부분적으로 다른 답변에서 언급 한 ActiveState 레시피를 기반으로합니다.
설치:
pip install readchar
용법:
import readchar
print("Reading a char:")
print(repr(readchar.readchar()))
print("Reading a key:")
print(repr(readchar.readkey()))
Python 2.7을 사용하여 Windows 및 Linux에서 테스트되었습니다.
Windows에서 문자 또는 ASCII 제어 코드에 매핑에만 키가 지원됩니다 ( Backspace, Enter, Esc, Tab, Ctrl+ 문자 ). GNU / 리눅스 (아마, 정확한 터미널에 따라?) 당신은 또한 얻을 Insert, Delete, Pg Up, Pg Dn, Home, End및 키 ...하지만 다음에,에서 이러한 특수 키를 분리 문제가있다 .F nEsc
주의 사항 : 대부분의 (모두?) 답변과 마찬가지로 Ctrl+ C, Ctrl+ D및 Ctrl+ 와 같은 신호 키 Z는 잡히고 반환됩니다 ( 각각 '\x03'
, '\x04'
및 '\x1a'
). 프로그램이 중단되기 어려울 수 있습니다.
다른 방법 :
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
에서 이 블로그 게시물 .
| os.O_NONBLOCK
. 그렇지 않으면 루프에 넣을 수 있습니다 (루프에서 비트가 회전하지 않도록 잠자기하는 것이 좋습니다).
while True
then 을 사용하는 것이 좋습니다 while 1
.
here에 기반한이 코드 는 Ctrl+ C또는 Ctrl+ D를 누르면 KeyboardInterrupt 및 EOFError를 올바르게 발생 시킵니다.
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()
(현재) 최상위 답변 (ActiveState 코드 포함)은 지나치게 복잡합니다. 단순한 함수로 충분할 때 클래스를 사용해야 할 이유가 없습니다. 아래는 동일하지만 더 읽기 쉬운 코드로 달성하는 두 가지 구현입니다.
이 두 가지 구현 모두 :
버전 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 예외 처리를 반복하는 데 드는 비용을 무시할 수 있습니다. 무시할 수있는 최적화보다 코드 가독성에 더 관심이 있어야하지만 ActiveState 코드와 동일하게 작동하고 더 읽기 쉬운 대안이 있습니다 (Louis의 대답과 비슷하지만 getChar ()는 독립적입니다).
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))
컨텍스트 관리자의 사용 사례 일 수 있습니다. Windows OS에 대한 수당을 제외하고 다음은 제 제안입니다.
#!/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()
self
하고 를 반환 __enter__
하는 read
메서드를 가질 sys.stdin.read(1)
수 있으며 한 컨텍스트에서 여러 문자를 읽을 수 있습니다.
이것을 사용하십시오 : 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()
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)
여기 에 대한 답변 은 유익한 것이었지만 스레드를 안전하게 교차하는 플랫폼 방식으로 키 누름을 비동기식으로 가져오고 별도의 이벤트에서 키 누름을 해제하는 방법을 원했습니다. 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()
back 의 동작을 원하면 호출 후 누른 키만 반환한다는 점을 제외하고는와 get(lossy=True)
같습니다 . 따라서 위의 예 에서 사용자가 'c'를 누를 때까지 차단 한 다음 다시 호출하면 다른 키를 누를 때까지 차단됩니다.get()
get()
get()
getAsync()
조금 다릅니다. 많은 처리를 수행하는 작업을 위해 설계되었으며 때로는 되돌아 와서 어떤 키를 눌렀는지 확인합니다. 따라서 가장 오래된 키부터 가장 최근에 누른 키 순으로 getAsync()
마지막 호출 이후에 누른 모든 키 목록을 반환합니다 getAsync()
. 또한 차단되지 않습니다. 즉, 마지막 호출 이후에 키를 누르지 getAsync()
않으면 빈 []
값이 반환됩니다.
실제로 키를 캡처를 시작하려면 호출 할 필요가 keys.startCapture()
당신과 함께 keys
객체 위했다. startCapture
비 차단이며, 단순히 키 누름을 기록하는 하나의 스레드와 해당 키 누름을 처리하는 다른 스레드를 시작합니다. 키 누름을 기록하는 스레드가 어떤 키도 놓치지 않도록하기 위해 두 개의 스레드가 있습니다.
키 캡처를 중지하려면 전화를 걸어 keys.stopCapture()
키 캡처를 중지합니다. 그러나 키 캡처는 차단 작업이므로 스레드 캡처 키는을 호출 한 후 키를 하나 더 캡처 할 수 있습니다 stopCapture()
.
이를 방지하기 위해 startCapture(functionName, args)
키가 'c'인지 확인한 다음 종료하는 것과 같은 기능을 수행하는 함수에 선택적 매개 변수를 전달할 수 있습니다 . 예를 들어, 여기서 잠을 자면 키가 빠질 수 있으므로이 기능은 거의 수행하지 않는 것이 중요합니다.
그러나이 stopCapture()
기능에서 get()
호출되면 더 이상 캡처하지 않고 키 캡처가 즉시 중지 되며, 아직 키를 누르지 않은 경우 없음으로 모든 호출이 즉시 반환됩니다.
또한, 이후 get()
와 getAsync()
(당신이 그들을 검색 할 때까지), 당신이 호출 할 수 있습니다 이전의 모든 키를 누르면 저장 clearGetList()
하고 clearAsyncList()
이전에 누른 키를 잊어.
그 주 get()
, getAsync()
키를 누르면 그렇다면, 이벤트는 독립적 인 : 1. 하나의 호출 get()
즉에 손실로, 기다리고, 그 키를 반환합니다. 다른 대기 통화 (있는 경우)는 계속 대기합니다. 2. 해당 키는 get 키 대기열에 저장되므로 get()
손실이 발생하지 않으면 get()
아직 반환되지 않은 가장 오래된 키를 반환합니다 . 3. 모든 이벤트는 해당 키를 입력으로 사용하여 시작됩니다. 4. 해당 키는 getAsync()
키 목록에 저장되며 다음 번 호출시 lis twill이 반환되고 빈 목록으로 설정됩니다.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"
그것은 내가 한 간단한 테스트에서 나에게 잘 작동하지만, 내가 놓친 것이 있으면 행복하게도 다른 사람들의 피드백을받을 것입니다.
나는 이것을 여기에 게시했다 .
다른 답변 중 하나의 주석은 cbreak 모드를 언급했습니다. 유닉스 구현에 중요합니다. 일반적으로 ^ C ( KeyboardError
)를 getchar에 의해 소비 하지 않기 때문에 (터미널을 원시 모드로 설정 할 때와 마찬가지로) 대부분의 다른 답변).
또 다른 중요한 세부 사항은 1 바이트가 아닌 하나의 문자 를 읽으려는 경우 단일 문자가 UTF-8 (Python 3+)로 구성된 최대 바이트 수이므로 입력 스트림에서 4 바이트를 읽어야한다는 것입니다 ). 단일 바이트 만 읽으면 키패드 화살표와 같은 멀티 바이트 문자에 예기치 않은 결과가 발생합니다.
유닉스에 대한 변경된 구현은 다음과 같습니다.
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)
파이 게임으로 이것을 시도하십시오 :
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, "."
pygame.error: video system not initialized
ActiveState의 레시피에는 "posix"시스템에 대한 약간의 버그가 포함 된 것으로 보입니다 Ctrl-C
(Mac을 사용하고 있습니다). 스크립트에 다음 코드를 넣으면
while(True):
print(getch())
로 스크립트를 종료 할 수 없으며 Ctrl-C
탈출하려면 터미널을 종료 해야합니다.
다음 줄이 원인이라고 생각하며 너무 잔인합니다.
tty.setraw(sys.stdin.fileno())
그 외에도 패키지 tty
는 실제로 필요하지 않으며 termios
처리하기에 충분합니다.
아래는 입력 할 때 char를 에코하는 Ctrl-C
추가 getche
기능 과 함께 나를 위해 작동하는 향상된 코드입니다 ( 중단됩니다) .
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)
참고 문헌 :
curses
파이썬 의 패키지는 터미널에서 문자 입력을위한 "원시"모드로 들어가는 데 몇 가지 문장만으로도 사용할 수 있습니다. Curses의 주된 용도는 출력을 위해 화면을 인계하는 것입니다. 이 코드 스 니펫은 print()
대신 사용할 수있는 명령문을 사용하지만 출력에 첨부 된 줄 끝을 curs가 변경하는 방법을 알고 있어야합니다.
#!/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!')
복잡한 작업을 수행하는 경우 curses를 사용하여 키를 읽습니다. 그러나 많은 경우 표준 라이브러리를 사용하고 화살표 키를 읽을 수있는 간단한 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
pip 패키지에 의존하지 않고 python3에 대한 내 솔루션.
# 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())
나는 이것이 가장 우아한 해결책이라고 생각합니다.
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!")
받아 들인 대답이 저에게 잘 수행되지 않았습니다 (키를 누르고 아무 일도 일어나지 않으면 다른 키를 누르면 작동합니다).
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)
대화식 모드에서 실행 하십시오.
여기에 대답 : Enter 키를 누르지 않고 파이썬에서 raw_input
이 코드를 사용하십시오
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
msvcrt.getch
과를msvcrt.getwch
가 제안.