Python read a single character from the user
![Multi tool use Multi tool use](http://sgv.ssvwv.com/sg/ssvwvcomimagb.png)
Multi tool use
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()
). I know there's a function in Windows for it, but I'd like something that is cross-platform.
python input
add a comment |
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()
). I know there's a function in Windows for it, but I'd like something that is cross-platform.
python input
On windows I ran into the same problem as in this question. The solution is to replace themsvcrt.getch
withmsvcrt.getwch
, as suggested there.
– A. Roy
Sep 8 '18 at 18:53
add a comment |
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()
). I know there's a function in Windows for it, but I'd like something that is cross-platform.
python input
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()
). I know there's a function in Windows for it, but I'd like something that is cross-platform.
python input
python input
edited May 10 '15 at 14:56
![](https://i.stack.imgur.com/yjo7W.jpg?s=32&g=1)
![](https://i.stack.imgur.com/yjo7W.jpg?s=32&g=1)
Masked Man
1
1
asked Feb 4 '09 at 7:08
Evan FosmarkEvan Fosmark
55.7k2894114
55.7k2894114
On windows I ran into the same problem as in this question. The solution is to replace themsvcrt.getch
withmsvcrt.getwch
, as suggested there.
– A. Roy
Sep 8 '18 at 18:53
add a comment |
On windows I ran into the same problem as in this question. The solution is to replace themsvcrt.getch
withmsvcrt.getwch
, as suggested there.
– A. Roy
Sep 8 '18 at 18:53
On windows I ran into the same problem as in this question. The solution is to replace the
msvcrt.getch
with msvcrt.getwch
, as suggested there.– A. Roy
Sep 8 '18 at 18:53
On windows I ran into the same problem as in this question. The solution is to replace the
msvcrt.getch
with msvcrt.getwch
, as suggested there.– A. Roy
Sep 8 '18 at 18:53
add a comment |
20 Answers
20
active
oldest
votes
Here's a link to a site that says how you can read a single character in Windows, Linux and 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()
14
code seems short enough that you could just include it, but +1 for finding a good (cross-platform) answer so quickly.
– John Mulder
Feb 4 '09 at 7:24
4
Does it handle non-latin (e.g., cyrillic) letters well? I am having a problem with that and can't figure out, if it is my mistake, or not.
– Phlya
Mar 29 '13 at 18:01
4
I don't like how theImportError
exception is used like some kind of if-statement; why not call platform.system() to check the OS?
– Seismoid
Sep 21 '14 at 20:19
9
@Seismoid: Asking for forgiveness is generally considered better, see stackoverflow.com/questions/12265451/…
– dirkjot
Apr 21 '15 at 11:18
3
Doesn't work on OS X: "old_settings = termios.tcgetattr(fd)" "termios.error: (25, 'Inappropriate ioctl for device')"
– Sarge Borsch
Sep 13 '15 at 18:35
|
show 11 more comments
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/)
24
I find it odd that sys.stdin.read(1) waits for a n, lol. Thanks for the submission, though.
– Evan Fosmark
Feb 4 '09 at 8:00
3
One character or one byte? That's not the same.
– chryss
Feb 4 '09 at 15:08
4
@Evan, that's because python is in line buffered mode by default
– John La Rooy
Oct 13 '09 at 11:09
4
Note that this code blocks you from using ^C or ^D!
– Tim R.
Jun 26 '12 at 1:29
2
@EvanFosmark: it's not necessarily that sys.stdin.read(1) waits for n, it's that the terminal program deciding when to send other characters to your program doesn't write them until it sees 'n' - how else would you be able to press backspace and correct what you're typing? (the serious answer to that is - teach the python program to implement the line control, keep a buffer, process backspaces, but that's a different world you may not want to buy into when just "reading a character", and could make your line handling different from all the other programs on your system.)
– Tony Delroy
Feb 21 '14 at 6:49
|
show 2 more comments
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()
add a comment |
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.
3
Works with Python 3 on Linux as well. Much better than getch, because readchar allows printing to stdout while waiting for key (via threads or asyncio).
– wrobell
Jun 5 '15 at 20:54
1
Tested on Kubuntu Linux, this works perfectly and is very Pythonic. Thank you!
– dotancohen
Aug 20 '15 at 13:30
Tested on Win10 + Python 3.5: ERROR:root:'in <string>' requires string as left operand, not bytes Traceback (most recent call last): File "..main.py", line 184, in wrapper result = func(*args, **kwargs) File "C:GitHubPython-Demodemoday_hello.py", line 41, in readch_eg print(readchar.readchar()) File "C:UsersipcjsAppDataLocalProgramsPythonPython35libsite-packagesreadcharreadchar_windows.py", line 14, in readchar while ch in 'x00xe0': TypeError: 'in <string>' requires string as left operand, not bytes
– ipcjs
Nov 5 '16 at 20:46
@ipcjs please report that bug to the maintainers
– Melih Yıldız'
Jul 5 '17 at 8:37
this is the best answer. adding a dependency to VS C++ library just for this functionality is crazy.
– FistOfFury
Dec 26 '17 at 14:54
add a comment |
An alternative method:
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
From this blog post.
Does not seem to work for me - returns empty string immediately upon calling. On Linux with Python 3.6.
– Marein
Apr 6 '17 at 17:40
1
@Marein If you want it to block (wait for input), remove the| os.O_NONBLOCK
. Otherwise, you can put it in a loop (good idea to sleep for a bit in the loop to keep from spinning).
– Chris Gregg
Feb 4 at 16:55
add a comment |
I think it gets extremely clunky at this point, and debugging on the different platforms is a big mess.
You'd be better off using something like pyglet, pygame, cocos2d - if you are doing something more elaborate than this and will need visuals, OR curses if you are going to work with the terminal.
Curses is standard: http://docs.python.org/library/curses.html
10
I strongly disagree with this. To use curses you have to initscr() the terminal which clears the screen. This is unwanted behavior if the user only wants to use curses to get userinput without n.
– dman
Jun 26 '13 at 20:22
add a comment |
This code, based off here, will correctly raise KeyboardInterrupt and EOFError if Ctrl+C or Ctrl+D are pressed.
Should work on Windows and Linux. An OS X version is available from the original source.
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()
This code waits for n.
– ChaimG
Nov 15 '16 at 14:48
add a comment |
The (currently) top-ranked answer (with the ActiveState code) is overly complicated. I don't see a reason to use classes when a mere function should suffice. Below are two implementations that accomplish the same thing but with more readable code.
Both of these implementations:
- work just fine in Python 2 or Python 3
- work on Windows, OSX, and Linux
- read just one byte (i.e., they don't wait for a newline)
- don't depend on any external libraries
- are self-contained (no code outside of the function definition)
Version 1: readable and simple
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
Version 2: avoid repeated imports and exception handling:
[EDIT] I missed one advantage of the ActiveState code. If you plan to read characters multiple times, that code avoids the (negligible) cost of repeating the Windows import and the ImportError exception handling on Unix-like systems. While you probably should be more concerned about code readability than that negligible optimization, here is an alternative (it is similar to Louis's answer, but getChar() is self-contained) that functions the same as the ActiveState code and is more readable:
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()
Example code that exercises either of the getChar() versions above:
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))
1
I ran into an issue with tty.setraw() when printing messages while simultaneously waiting for a key (multi-threaded). Long story short, I found that using tty.setcbreak() lets you get a single character without breaking all the other normal stuff. Long story in this answer
– TheDavidFactor
May 21 '16 at 3:50
Nice, I was hoping there would be a cleaner way of doing this
– Phylliida
May 5 '17 at 18:00
add a comment |
This might be a use case for a context manager. Leaving aside allowances for Windows OS, here's my suggestion:
#!/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()
You could also returnself
in__enter__
and have aread
method that returnssys.stdin.read(1)
, then you could read multiple characters in one context.
– L3viathan
Apr 12 '17 at 6:46
add a comment |
The answers here were informative, however I also wanted a way to get key presses asynchronously and fire off key presses in separate events, all in a thread-safe, cross-platform way. PyGame was also too bloated for me. So I made the following (in Python 2.7 but I suspect it's easily portable), which I figured I'd share here in case it was useful for anyone else. I stored this in a file named 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()
The idea is that you can either simply call keyPress.getKey()
, which will read a key from the keyboard, then return it.
If you want something more than that, I made a KeyCapture
object. You can create one via something like keys = keyPress.KeyCapture()
.
Then there are three things you can do:
addEvent(functionName)
takes in any function that takes in one parameter. Then every time a key is pressed, this function will be called with that key's string as it's input. These are ran in a separate thread, so you can block all you want in them and it won't mess up the functionality of the KeyCapturer nor delay the other events.
get()
returns a key in the same blocking way as before. It is now needed here because the keys are being captured via the KeyCapture
object now, so keyPress.getKey()
would conflict with that behavior and both of them would miss some keys since only one key can be captured at a time. Also, say the user presses 'a', then 'b', you call get()
, the the user presses 'c'. That get()
call will immediately return 'a', then if you call it again it will return 'b', then 'c'. If you call it again it will block until another key is pressed. This ensures that you don't miss any keys, in a blocking way if desired. So in this way it's a little different than keyPress.getKey()
from before
If you want the behavior of getKey()
back, get(lossy=True)
is like get()
, except that it only returns keys pressed after the call to get()
. So in the above example, get()
would block until the user presses 'c', and then if you call it again it will block until another key is pressed.
getAsync()
is a little different. It's designed for something that does a lot of processing, then occasionally comes back and checks which keys were pressed. Thus getAsync()
returns a list of all the keys pressed since the last call to getAsync()
, in order from oldest key pressed to most recent key pressed. It also doesn't block, meaning that if no keys have been pressed since the last call to getAsync()
, an empty will be returned.
To actually start capturing keys, you need to call keys.startCapture()
with your keys
object made above. startCapture
is non-blocking, and simply starts one thread that just records the key presses, and another thread to process those key presses. There are two threads to ensure that the thread that records key presses doesn't miss any keys.
If you want to stop capturing keys, you can call keys.stopCapture()
and it will stop capturing keys. However, since capturing a key is a blocking operation, the thread capturing keys might capture one more key after calling stopCapture()
.
To prevent this, you can pass in an optional parameter(s) into startCapture(functionName, args)
of a function that just does something like checks if a key equals 'c' and then exits. It's important that this function does very little before, for example, a sleep here will cause us to miss keys.
However, if stopCapture()
is called in this function, key captures will be stopped immediately, without trying to capture any more, and that all get()
calls will be returned immediately, with None if no keys have been pressed yet.
Also, since get()
and getAsync()
store all the previous keys pressed (until you retrieve them), you can call clearGetList()
and clearAsyncList()
to forget the keys previously pressed.
Note that get()
, getAsync()
and events are independent, so if a key is pressed:
1. One call to get()
that is waiting, with lossy on, will return
that key. The other waiting calls (if any) will continue waiting.
2. That key 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. That key will be stored in the list of getAsync()
keys, where that lis twill be returned and set to empty list on the next call to getAsync()
If all this is too much, here is an example use case:
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"
It is working well for me from the simple test I made, but I will happily take others feedback as well if there is something I missed.
I posted this here as well.
add a comment |
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)
3
does this work for command line applications?
– ThorSummoner
Jan 23 '15 at 7:36
Yes @ThorSummoner
– Davoud Taghawi-Nejad
Mar 27 '15 at 3:12
1
@ThorSummoner: This code has a number of problems — so no, it won't work for command line applications.
– martineau
Aug 31 '15 at 0:29
It runs for a command line application, given that the windows manager is running.
– Davoud Taghawi-Nejad
Aug 31 '15 at 17:36
1
So basically it doesn't work for command line applications...
– Mehdi
Nov 17 '15 at 13:06
|
show 1 more comment
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
add a comment |
Try this with 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, "."
That is a neat idea, but it doesn't work on the command line:pygame.error: video system not initialized
– dirkjot
Apr 21 '15 at 11:15
add a comment |
A comment in one of the other answers mentioned cbreak mode, which is important for Unix implementations because you generally don't want ^C (KeyboardError
) to be consumed by getchar (as it will when you set the terminal to raw mode, as done by most other answers).
Another important detail is that if you're looking to read one character and not one byte, you should read 4 bytes from the input stream, as that's the maximum number of bytes a single character will consist of in UTF-8 (Python 3+). Reading only a single byte will produce unexpected results for multi-byte characters such as keypad arrows.
Here's my changed implementation for 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)
add a comment |
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 %sr' % 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!')
add a comment |
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
add a comment |
The build-in raw_input should help.
for i in range(3):
print ("So much work to do!")
k = raw_input("Press any key to continue...")
print ("Ok, back to work.")
6
raw_input is waiting for enter key
– vac
Dec 16 '15 at 22:03
add a comment |
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("rn")
tty.tcdrain(sys.stdin.fileno())
add a comment |
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!")
add a comment |
If I'm doing something complicated I'll use curses to read keys. But a lot of times I just want a simple Python 3 script that uses the standard library and can read arrow keys, so I do this:
import sys, termios, tty
key_Enter = 13
key_Esc = 27
key_Up = '33[A'
key_Dn = '33[B'
key_Rt = '33[C'
key_Lt = '33[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 = '33' + ch[1:]
termios.tcsetattr(fdInput, termios.TCSADRAIN, termAttr)
return ch
add a comment |
protected by eyllanesc Jun 23 '18 at 22:25
Thank you for your interest in this question.
Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).
Would you like to answer one of these unanswered questions instead?
20 Answers
20
active
oldest
votes
20 Answers
20
active
oldest
votes
active
oldest
votes
active
oldest
votes
Here's a link to a site that says how you can read a single character in Windows, Linux and 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()
14
code seems short enough that you could just include it, but +1 for finding a good (cross-platform) answer so quickly.
– John Mulder
Feb 4 '09 at 7:24
4
Does it handle non-latin (e.g., cyrillic) letters well? I am having a problem with that and can't figure out, if it is my mistake, or not.
– Phlya
Mar 29 '13 at 18:01
4
I don't like how theImportError
exception is used like some kind of if-statement; why not call platform.system() to check the OS?
– Seismoid
Sep 21 '14 at 20:19
9
@Seismoid: Asking for forgiveness is generally considered better, see stackoverflow.com/questions/12265451/…
– dirkjot
Apr 21 '15 at 11:18
3
Doesn't work on OS X: "old_settings = termios.tcgetattr(fd)" "termios.error: (25, 'Inappropriate ioctl for device')"
– Sarge Borsch
Sep 13 '15 at 18:35
|
show 11 more comments
Here's a link to a site that says how you can read a single character in Windows, Linux and 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()
14
code seems short enough that you could just include it, but +1 for finding a good (cross-platform) answer so quickly.
– John Mulder
Feb 4 '09 at 7:24
4
Does it handle non-latin (e.g., cyrillic) letters well? I am having a problem with that and can't figure out, if it is my mistake, or not.
– Phlya
Mar 29 '13 at 18:01
4
I don't like how theImportError
exception is used like some kind of if-statement; why not call platform.system() to check the OS?
– Seismoid
Sep 21 '14 at 20:19
9
@Seismoid: Asking for forgiveness is generally considered better, see stackoverflow.com/questions/12265451/…
– dirkjot
Apr 21 '15 at 11:18
3
Doesn't work on OS X: "old_settings = termios.tcgetattr(fd)" "termios.error: (25, 'Inappropriate ioctl for device')"
– Sarge Borsch
Sep 13 '15 at 18:35
|
show 11 more comments
Here's a link to a site that says how you can read a single character in Windows, Linux and 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()
Here's a link to a site that says how you can read a single character in Windows, Linux and 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()
edited Jan 2 '16 at 2:34
EddieOffermann
757
757
answered Feb 4 '09 at 7:11
tehvantehvan
7,52742330
7,52742330
14
code seems short enough that you could just include it, but +1 for finding a good (cross-platform) answer so quickly.
– John Mulder
Feb 4 '09 at 7:24
4
Does it handle non-latin (e.g., cyrillic) letters well? I am having a problem with that and can't figure out, if it is my mistake, or not.
– Phlya
Mar 29 '13 at 18:01
4
I don't like how theImportError
exception is used like some kind of if-statement; why not call platform.system() to check the OS?
– Seismoid
Sep 21 '14 at 20:19
9
@Seismoid: Asking for forgiveness is generally considered better, see stackoverflow.com/questions/12265451/…
– dirkjot
Apr 21 '15 at 11:18
3
Doesn't work on OS X: "old_settings = termios.tcgetattr(fd)" "termios.error: (25, 'Inappropriate ioctl for device')"
– Sarge Borsch
Sep 13 '15 at 18:35
|
show 11 more comments
14
code seems short enough that you could just include it, but +1 for finding a good (cross-platform) answer so quickly.
– John Mulder
Feb 4 '09 at 7:24
4
Does it handle non-latin (e.g., cyrillic) letters well? I am having a problem with that and can't figure out, if it is my mistake, or not.
– Phlya
Mar 29 '13 at 18:01
4
I don't like how theImportError
exception is used like some kind of if-statement; why not call platform.system() to check the OS?
– Seismoid
Sep 21 '14 at 20:19
9
@Seismoid: Asking for forgiveness is generally considered better, see stackoverflow.com/questions/12265451/…
– dirkjot
Apr 21 '15 at 11:18
3
Doesn't work on OS X: "old_settings = termios.tcgetattr(fd)" "termios.error: (25, 'Inappropriate ioctl for device')"
– Sarge Borsch
Sep 13 '15 at 18:35
14
14
code seems short enough that you could just include it, but +1 for finding a good (cross-platform) answer so quickly.
– John Mulder
Feb 4 '09 at 7:24
code seems short enough that you could just include it, but +1 for finding a good (cross-platform) answer so quickly.
– John Mulder
Feb 4 '09 at 7:24
4
4
Does it handle non-latin (e.g., cyrillic) letters well? I am having a problem with that and can't figure out, if it is my mistake, or not.
– Phlya
Mar 29 '13 at 18:01
Does it handle non-latin (e.g., cyrillic) letters well? I am having a problem with that and can't figure out, if it is my mistake, or not.
– Phlya
Mar 29 '13 at 18:01
4
4
I don't like how the
ImportError
exception is used like some kind of if-statement; why not call platform.system() to check the OS?– Seismoid
Sep 21 '14 at 20:19
I don't like how the
ImportError
exception is used like some kind of if-statement; why not call platform.system() to check the OS?– Seismoid
Sep 21 '14 at 20:19
9
9
@Seismoid: Asking for forgiveness is generally considered better, see stackoverflow.com/questions/12265451/…
– dirkjot
Apr 21 '15 at 11:18
@Seismoid: Asking for forgiveness is generally considered better, see stackoverflow.com/questions/12265451/…
– dirkjot
Apr 21 '15 at 11:18
3
3
Doesn't work on OS X: "old_settings = termios.tcgetattr(fd)" "termios.error: (25, 'Inappropriate ioctl for device')"
– Sarge Borsch
Sep 13 '15 at 18:35
Doesn't work on OS X: "old_settings = termios.tcgetattr(fd)" "termios.error: (25, 'Inappropriate ioctl for device')"
– Sarge Borsch
Sep 13 '15 at 18:35
|
show 11 more comments
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/)
24
I find it odd that sys.stdin.read(1) waits for a n, lol. Thanks for the submission, though.
– Evan Fosmark
Feb 4 '09 at 8:00
3
One character or one byte? That's not the same.
– chryss
Feb 4 '09 at 15:08
4
@Evan, that's because python is in line buffered mode by default
– John La Rooy
Oct 13 '09 at 11:09
4
Note that this code blocks you from using ^C or ^D!
– Tim R.
Jun 26 '12 at 1:29
2
@EvanFosmark: it's not necessarily that sys.stdin.read(1) waits for n, it's that the terminal program deciding when to send other characters to your program doesn't write them until it sees 'n' - how else would you be able to press backspace and correct what you're typing? (the serious answer to that is - teach the python program to implement the line control, keep a buffer, process backspaces, but that's a different world you may not want to buy into when just "reading a character", and could make your line handling different from all the other programs on your system.)
– Tony Delroy
Feb 21 '14 at 6:49
|
show 2 more comments
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/)
24
I find it odd that sys.stdin.read(1) waits for a n, lol. Thanks for the submission, though.
– Evan Fosmark
Feb 4 '09 at 8:00
3
One character or one byte? That's not the same.
– chryss
Feb 4 '09 at 15:08
4
@Evan, that's because python is in line buffered mode by default
– John La Rooy
Oct 13 '09 at 11:09
4
Note that this code blocks you from using ^C or ^D!
– Tim R.
Jun 26 '12 at 1:29
2
@EvanFosmark: it's not necessarily that sys.stdin.read(1) waits for n, it's that the terminal program deciding when to send other characters to your program doesn't write them until it sees 'n' - how else would you be able to press backspace and correct what you're typing? (the serious answer to that is - teach the python program to implement the line control, keep a buffer, process backspaces, but that's a different world you may not want to buy into when just "reading a character", and could make your line handling different from all the other programs on your system.)
– Tony Delroy
Feb 21 '14 at 6:49
|
show 2 more comments
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/)
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/)
edited Oct 18 '12 at 13:16
![](https://i.stack.imgur.com/17gYz.png?s=32&g=1)
![](https://i.stack.imgur.com/17gYz.png?s=32&g=1)
mgilson
211k39411528
211k39411528
answered Feb 4 '09 at 7:30
![](https://i.stack.imgur.com/omB1R.jpg?s=32&g=1)
![](https://i.stack.imgur.com/omB1R.jpg?s=32&g=1)
Yuval AdamYuval Adam
111k75264362
111k75264362
24
I find it odd that sys.stdin.read(1) waits for a n, lol. Thanks for the submission, though.
– Evan Fosmark
Feb 4 '09 at 8:00
3
One character or one byte? That's not the same.
– chryss
Feb 4 '09 at 15:08
4
@Evan, that's because python is in line buffered mode by default
– John La Rooy
Oct 13 '09 at 11:09
4
Note that this code blocks you from using ^C or ^D!
– Tim R.
Jun 26 '12 at 1:29
2
@EvanFosmark: it's not necessarily that sys.stdin.read(1) waits for n, it's that the terminal program deciding when to send other characters to your program doesn't write them until it sees 'n' - how else would you be able to press backspace and correct what you're typing? (the serious answer to that is - teach the python program to implement the line control, keep a buffer, process backspaces, but that's a different world you may not want to buy into when just "reading a character", and could make your line handling different from all the other programs on your system.)
– Tony Delroy
Feb 21 '14 at 6:49
|
show 2 more comments
24
I find it odd that sys.stdin.read(1) waits for a n, lol. Thanks for the submission, though.
– Evan Fosmark
Feb 4 '09 at 8:00
3
One character or one byte? That's not the same.
– chryss
Feb 4 '09 at 15:08
4
@Evan, that's because python is in line buffered mode by default
– John La Rooy
Oct 13 '09 at 11:09
4
Note that this code blocks you from using ^C or ^D!
– Tim R.
Jun 26 '12 at 1:29
2
@EvanFosmark: it's not necessarily that sys.stdin.read(1) waits for n, it's that the terminal program deciding when to send other characters to your program doesn't write them until it sees 'n' - how else would you be able to press backspace and correct what you're typing? (the serious answer to that is - teach the python program to implement the line control, keep a buffer, process backspaces, but that's a different world you may not want to buy into when just "reading a character", and could make your line handling different from all the other programs on your system.)
– Tony Delroy
Feb 21 '14 at 6:49
24
24
I find it odd that sys.stdin.read(1) waits for a n, lol. Thanks for the submission, though.
– Evan Fosmark
Feb 4 '09 at 8:00
I find it odd that sys.stdin.read(1) waits for a n, lol. Thanks for the submission, though.
– Evan Fosmark
Feb 4 '09 at 8:00
3
3
One character or one byte? That's not the same.
– chryss
Feb 4 '09 at 15:08
One character or one byte? That's not the same.
– chryss
Feb 4 '09 at 15:08
4
4
@Evan, that's because python is in line buffered mode by default
– John La Rooy
Oct 13 '09 at 11:09
@Evan, that's because python is in line buffered mode by default
– John La Rooy
Oct 13 '09 at 11:09
4
4
Note that this code blocks you from using ^C or ^D!
– Tim R.
Jun 26 '12 at 1:29
Note that this code blocks you from using ^C or ^D!
– Tim R.
Jun 26 '12 at 1:29
2
2
@EvanFosmark: it's not necessarily that sys.stdin.read(1) waits for n, it's that the terminal program deciding when to send other characters to your program doesn't write them until it sees 'n' - how else would you be able to press backspace and correct what you're typing? (the serious answer to that is - teach the python program to implement the line control, keep a buffer, process backspaces, but that's a different world you may not want to buy into when just "reading a character", and could make your line handling different from all the other programs on your system.)
– Tony Delroy
Feb 21 '14 at 6:49
@EvanFosmark: it's not necessarily that sys.stdin.read(1) waits for n, it's that the terminal program deciding when to send other characters to your program doesn't write them until it sees 'n' - how else would you be able to press backspace and correct what you're typing? (the serious answer to that is - teach the python program to implement the line control, keep a buffer, process backspaces, but that's a different world you may not want to buy into when just "reading a character", and could make your line handling different from all the other programs on your system.)
– Tony Delroy
Feb 21 '14 at 6:49
|
show 2 more comments
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()
add a comment |
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()
add a comment |
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()
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()
answered Feb 9 '14 at 13:27
![](https://i.stack.imgur.com/kRxnP.jpg?s=32&g=1)
![](https://i.stack.imgur.com/kRxnP.jpg?s=32&g=1)
LouisLouis
95.1k22184236
95.1k22184236
add a comment |
add a comment |
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.
3
Works with Python 3 on Linux as well. Much better than getch, because readchar allows printing to stdout while waiting for key (via threads or asyncio).
– wrobell
Jun 5 '15 at 20:54
1
Tested on Kubuntu Linux, this works perfectly and is very Pythonic. Thank you!
– dotancohen
Aug 20 '15 at 13:30
Tested on Win10 + Python 3.5: ERROR:root:'in <string>' requires string as left operand, not bytes Traceback (most recent call last): File "..main.py", line 184, in wrapper result = func(*args, **kwargs) File "C:GitHubPython-Demodemoday_hello.py", line 41, in readch_eg print(readchar.readchar()) File "C:UsersipcjsAppDataLocalProgramsPythonPython35libsite-packagesreadcharreadchar_windows.py", line 14, in readchar while ch in 'x00xe0': TypeError: 'in <string>' requires string as left operand, not bytes
– ipcjs
Nov 5 '16 at 20:46
@ipcjs please report that bug to the maintainers
– Melih Yıldız'
Jul 5 '17 at 8:37
this is the best answer. adding a dependency to VS C++ library just for this functionality is crazy.
– FistOfFury
Dec 26 '17 at 14:54
add a comment |
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.
3
Works with Python 3 on Linux as well. Much better than getch, because readchar allows printing to stdout while waiting for key (via threads or asyncio).
– wrobell
Jun 5 '15 at 20:54
1
Tested on Kubuntu Linux, this works perfectly and is very Pythonic. Thank you!
– dotancohen
Aug 20 '15 at 13:30
Tested on Win10 + Python 3.5: ERROR:root:'in <string>' requires string as left operand, not bytes Traceback (most recent call last): File "..main.py", line 184, in wrapper result = func(*args, **kwargs) File "C:GitHubPython-Demodemoday_hello.py", line 41, in readch_eg print(readchar.readchar()) File "C:UsersipcjsAppDataLocalProgramsPythonPython35libsite-packagesreadcharreadchar_windows.py", line 14, in readchar while ch in 'x00xe0': TypeError: 'in <string>' requires string as left operand, not bytes
– ipcjs
Nov 5 '16 at 20:46
@ipcjs please report that bug to the maintainers
– Melih Yıldız'
Jul 5 '17 at 8:37
this is the best answer. adding a dependency to VS C++ library just for this functionality is crazy.
– FistOfFury
Dec 26 '17 at 14:54
add a comment |
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.
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.
answered Aug 16 '14 at 18:47
Søren LøvborgSøren Løvborg
6,06223435
6,06223435
3
Works with Python 3 on Linux as well. Much better than getch, because readchar allows printing to stdout while waiting for key (via threads or asyncio).
– wrobell
Jun 5 '15 at 20:54
1
Tested on Kubuntu Linux, this works perfectly and is very Pythonic. Thank you!
– dotancohen
Aug 20 '15 at 13:30
Tested on Win10 + Python 3.5: ERROR:root:'in <string>' requires string as left operand, not bytes Traceback (most recent call last): File "..main.py", line 184, in wrapper result = func(*args, **kwargs) File "C:GitHubPython-Demodemoday_hello.py", line 41, in readch_eg print(readchar.readchar()) File "C:UsersipcjsAppDataLocalProgramsPythonPython35libsite-packagesreadcharreadchar_windows.py", line 14, in readchar while ch in 'x00xe0': TypeError: 'in <string>' requires string as left operand, not bytes
– ipcjs
Nov 5 '16 at 20:46
@ipcjs please report that bug to the maintainers
– Melih Yıldız'
Jul 5 '17 at 8:37
this is the best answer. adding a dependency to VS C++ library just for this functionality is crazy.
– FistOfFury
Dec 26 '17 at 14:54
add a comment |
3
Works with Python 3 on Linux as well. Much better than getch, because readchar allows printing to stdout while waiting for key (via threads or asyncio).
– wrobell
Jun 5 '15 at 20:54
1
Tested on Kubuntu Linux, this works perfectly and is very Pythonic. Thank you!
– dotancohen
Aug 20 '15 at 13:30
Tested on Win10 + Python 3.5: ERROR:root:'in <string>' requires string as left operand, not bytes Traceback (most recent call last): File "..main.py", line 184, in wrapper result = func(*args, **kwargs) File "C:GitHubPython-Demodemoday_hello.py", line 41, in readch_eg print(readchar.readchar()) File "C:UsersipcjsAppDataLocalProgramsPythonPython35libsite-packagesreadcharreadchar_windows.py", line 14, in readchar while ch in 'x00xe0': TypeError: 'in <string>' requires string as left operand, not bytes
– ipcjs
Nov 5 '16 at 20:46
@ipcjs please report that bug to the maintainers
– Melih Yıldız'
Jul 5 '17 at 8:37
this is the best answer. adding a dependency to VS C++ library just for this functionality is crazy.
– FistOfFury
Dec 26 '17 at 14:54
3
3
Works with Python 3 on Linux as well. Much better than getch, because readchar allows printing to stdout while waiting for key (via threads or asyncio).
– wrobell
Jun 5 '15 at 20:54
Works with Python 3 on Linux as well. Much better than getch, because readchar allows printing to stdout while waiting for key (via threads or asyncio).
– wrobell
Jun 5 '15 at 20:54
1
1
Tested on Kubuntu Linux, this works perfectly and is very Pythonic. Thank you!
– dotancohen
Aug 20 '15 at 13:30
Tested on Kubuntu Linux, this works perfectly and is very Pythonic. Thank you!
– dotancohen
Aug 20 '15 at 13:30
Tested on Win10 + Python 3.5: ERROR:root:'in <string>' requires string as left operand, not bytes Traceback (most recent call last): File "..main.py", line 184, in wrapper result = func(*args, **kwargs) File "C:GitHubPython-Demodemoday_hello.py", line 41, in readch_eg print(readchar.readchar()) File "C:UsersipcjsAppDataLocalProgramsPythonPython35libsite-packagesreadcharreadchar_windows.py", line 14, in readchar while ch in 'x00xe0': TypeError: 'in <string>' requires string as left operand, not bytes
– ipcjs
Nov 5 '16 at 20:46
Tested on Win10 + Python 3.5: ERROR:root:'in <string>' requires string as left operand, not bytes Traceback (most recent call last): File "..main.py", line 184, in wrapper result = func(*args, **kwargs) File "C:GitHubPython-Demodemoday_hello.py", line 41, in readch_eg print(readchar.readchar()) File "C:UsersipcjsAppDataLocalProgramsPythonPython35libsite-packagesreadcharreadchar_windows.py", line 14, in readchar while ch in 'x00xe0': TypeError: 'in <string>' requires string as left operand, not bytes
– ipcjs
Nov 5 '16 at 20:46
@ipcjs please report that bug to the maintainers
– Melih Yıldız'
Jul 5 '17 at 8:37
@ipcjs please report that bug to the maintainers
– Melih Yıldız'
Jul 5 '17 at 8:37
this is the best answer. adding a dependency to VS C++ library just for this functionality is crazy.
– FistOfFury
Dec 26 '17 at 14:54
this is the best answer. adding a dependency to VS C++ library just for this functionality is crazy.
– FistOfFury
Dec 26 '17 at 14:54
add a comment |
An alternative method:
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
From this blog post.
Does not seem to work for me - returns empty string immediately upon calling. On Linux with Python 3.6.
– Marein
Apr 6 '17 at 17:40
1
@Marein If you want it to block (wait for input), remove the| os.O_NONBLOCK
. Otherwise, you can put it in a loop (good idea to sleep for a bit in the loop to keep from spinning).
– Chris Gregg
Feb 4 at 16:55
add a comment |
An alternative method:
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
From this blog post.
Does not seem to work for me - returns empty string immediately upon calling. On Linux with Python 3.6.
– Marein
Apr 6 '17 at 17:40
1
@Marein If you want it to block (wait for input), remove the| os.O_NONBLOCK
. Otherwise, you can put it in a loop (good idea to sleep for a bit in the loop to keep from spinning).
– Chris Gregg
Feb 4 at 16:55
add a comment |
An alternative method:
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
From this blog post.
An alternative method:
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
From this blog post.
edited May 22 '12 at 0:13
jdi
70k14121161
70k14121161
answered Aug 31 '11 at 15:30
TylerTyler
24k1177100
24k1177100
Does not seem to work for me - returns empty string immediately upon calling. On Linux with Python 3.6.
– Marein
Apr 6 '17 at 17:40
1
@Marein If you want it to block (wait for input), remove the| os.O_NONBLOCK
. Otherwise, you can put it in a loop (good idea to sleep for a bit in the loop to keep from spinning).
– Chris Gregg
Feb 4 at 16:55
add a comment |
Does not seem to work for me - returns empty string immediately upon calling. On Linux with Python 3.6.
– Marein
Apr 6 '17 at 17:40
1
@Marein If you want it to block (wait for input), remove the| os.O_NONBLOCK
. Otherwise, you can put it in a loop (good idea to sleep for a bit in the loop to keep from spinning).
– Chris Gregg
Feb 4 at 16:55
Does not seem to work for me - returns empty string immediately upon calling. On Linux with Python 3.6.
– Marein
Apr 6 '17 at 17:40
Does not seem to work for me - returns empty string immediately upon calling. On Linux with Python 3.6.
– Marein
Apr 6 '17 at 17:40
1
1
@Marein If you want it to block (wait for input), remove the
| os.O_NONBLOCK
. Otherwise, you can put it in a loop (good idea to sleep for a bit in the loop to keep from spinning).– Chris Gregg
Feb 4 at 16:55
@Marein If you want it to block (wait for input), remove the
| os.O_NONBLOCK
. Otherwise, you can put it in a loop (good idea to sleep for a bit in the loop to keep from spinning).– Chris Gregg
Feb 4 at 16:55
add a comment |
I think it gets extremely clunky at this point, and debugging on the different platforms is a big mess.
You'd be better off using something like pyglet, pygame, cocos2d - if you are doing something more elaborate than this and will need visuals, OR curses if you are going to work with the terminal.
Curses is standard: http://docs.python.org/library/curses.html
10
I strongly disagree with this. To use curses you have to initscr() the terminal which clears the screen. This is unwanted behavior if the user only wants to use curses to get userinput without n.
– dman
Jun 26 '13 at 20:22
add a comment |
I think it gets extremely clunky at this point, and debugging on the different platforms is a big mess.
You'd be better off using something like pyglet, pygame, cocos2d - if you are doing something more elaborate than this and will need visuals, OR curses if you are going to work with the terminal.
Curses is standard: http://docs.python.org/library/curses.html
10
I strongly disagree with this. To use curses you have to initscr() the terminal which clears the screen. This is unwanted behavior if the user only wants to use curses to get userinput without n.
– dman
Jun 26 '13 at 20:22
add a comment |
I think it gets extremely clunky at this point, and debugging on the different platforms is a big mess.
You'd be better off using something like pyglet, pygame, cocos2d - if you are doing something more elaborate than this and will need visuals, OR curses if you are going to work with the terminal.
Curses is standard: http://docs.python.org/library/curses.html
I think it gets extremely clunky at this point, and debugging on the different platforms is a big mess.
You'd be better off using something like pyglet, pygame, cocos2d - if you are doing something more elaborate than this and will need visuals, OR curses if you are going to work with the terminal.
Curses is standard: http://docs.python.org/library/curses.html
answered Feb 4 '09 at 11:04
nachiknachik
4813811
4813811
10
I strongly disagree with this. To use curses you have to initscr() the terminal which clears the screen. This is unwanted behavior if the user only wants to use curses to get userinput without n.
– dman
Jun 26 '13 at 20:22
add a comment |
10
I strongly disagree with this. To use curses you have to initscr() the terminal which clears the screen. This is unwanted behavior if the user only wants to use curses to get userinput without n.
– dman
Jun 26 '13 at 20:22
10
10
I strongly disagree with this. To use curses you have to initscr() the terminal which clears the screen. This is unwanted behavior if the user only wants to use curses to get userinput without n.
– dman
Jun 26 '13 at 20:22
I strongly disagree with this. To use curses you have to initscr() the terminal which clears the screen. This is unwanted behavior if the user only wants to use curses to get userinput without n.
– dman
Jun 26 '13 at 20:22
add a comment |
This code, based off here, will correctly raise KeyboardInterrupt and EOFError if Ctrl+C or Ctrl+D are pressed.
Should work on Windows and Linux. An OS X version is available from the original source.
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()
This code waits for n.
– ChaimG
Nov 15 '16 at 14:48
add a comment |
This code, based off here, will correctly raise KeyboardInterrupt and EOFError if Ctrl+C or Ctrl+D are pressed.
Should work on Windows and Linux. An OS X version is available from the original source.
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()
This code waits for n.
– ChaimG
Nov 15 '16 at 14:48
add a comment |
This code, based off here, will correctly raise KeyboardInterrupt and EOFError if Ctrl+C or Ctrl+D are pressed.
Should work on Windows and Linux. An OS X version is available from the original source.
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()
This code, based off here, will correctly raise KeyboardInterrupt and EOFError if Ctrl+C or Ctrl+D are pressed.
Should work on Windows and Linux. An OS X version is available from the original source.
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()
answered Jan 1 '14 at 5:13
kirikiri
1,07411437
1,07411437
This code waits for n.
– ChaimG
Nov 15 '16 at 14:48
add a comment |
This code waits for n.
– ChaimG
Nov 15 '16 at 14:48
This code waits for n.
– ChaimG
Nov 15 '16 at 14:48
This code waits for n.
– ChaimG
Nov 15 '16 at 14:48
add a comment |
The (currently) top-ranked answer (with the ActiveState code) is overly complicated. I don't see a reason to use classes when a mere function should suffice. Below are two implementations that accomplish the same thing but with more readable code.
Both of these implementations:
- work just fine in Python 2 or Python 3
- work on Windows, OSX, and Linux
- read just one byte (i.e., they don't wait for a newline)
- don't depend on any external libraries
- are self-contained (no code outside of the function definition)
Version 1: readable and simple
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
Version 2: avoid repeated imports and exception handling:
[EDIT] I missed one advantage of the ActiveState code. If you plan to read characters multiple times, that code avoids the (negligible) cost of repeating the Windows import and the ImportError exception handling on Unix-like systems. While you probably should be more concerned about code readability than that negligible optimization, here is an alternative (it is similar to Louis's answer, but getChar() is self-contained) that functions the same as the ActiveState code and is more readable:
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()
Example code that exercises either of the getChar() versions above:
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))
1
I ran into an issue with tty.setraw() when printing messages while simultaneously waiting for a key (multi-threaded). Long story short, I found that using tty.setcbreak() lets you get a single character without breaking all the other normal stuff. Long story in this answer
– TheDavidFactor
May 21 '16 at 3:50
Nice, I was hoping there would be a cleaner way of doing this
– Phylliida
May 5 '17 at 18:00
add a comment |
The (currently) top-ranked answer (with the ActiveState code) is overly complicated. I don't see a reason to use classes when a mere function should suffice. Below are two implementations that accomplish the same thing but with more readable code.
Both of these implementations:
- work just fine in Python 2 or Python 3
- work on Windows, OSX, and Linux
- read just one byte (i.e., they don't wait for a newline)
- don't depend on any external libraries
- are self-contained (no code outside of the function definition)
Version 1: readable and simple
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
Version 2: avoid repeated imports and exception handling:
[EDIT] I missed one advantage of the ActiveState code. If you plan to read characters multiple times, that code avoids the (negligible) cost of repeating the Windows import and the ImportError exception handling on Unix-like systems. While you probably should be more concerned about code readability than that negligible optimization, here is an alternative (it is similar to Louis's answer, but getChar() is self-contained) that functions the same as the ActiveState code and is more readable:
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()
Example code that exercises either of the getChar() versions above:
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))
1
I ran into an issue with tty.setraw() when printing messages while simultaneously waiting for a key (multi-threaded). Long story short, I found that using tty.setcbreak() lets you get a single character without breaking all the other normal stuff. Long story in this answer
– TheDavidFactor
May 21 '16 at 3:50
Nice, I was hoping there would be a cleaner way of doing this
– Phylliida
May 5 '17 at 18:00
add a comment |
The (currently) top-ranked answer (with the ActiveState code) is overly complicated. I don't see a reason to use classes when a mere function should suffice. Below are two implementations that accomplish the same thing but with more readable code.
Both of these implementations:
- work just fine in Python 2 or Python 3
- work on Windows, OSX, and Linux
- read just one byte (i.e., they don't wait for a newline)
- don't depend on any external libraries
- are self-contained (no code outside of the function definition)
Version 1: readable and simple
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
Version 2: avoid repeated imports and exception handling:
[EDIT] I missed one advantage of the ActiveState code. If you plan to read characters multiple times, that code avoids the (negligible) cost of repeating the Windows import and the ImportError exception handling on Unix-like systems. While you probably should be more concerned about code readability than that negligible optimization, here is an alternative (it is similar to Louis's answer, but getChar() is self-contained) that functions the same as the ActiveState code and is more readable:
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()
Example code that exercises either of the getChar() versions above:
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))
The (currently) top-ranked answer (with the ActiveState code) is overly complicated. I don't see a reason to use classes when a mere function should suffice. Below are two implementations that accomplish the same thing but with more readable code.
Both of these implementations:
- work just fine in Python 2 or Python 3
- work on Windows, OSX, and Linux
- read just one byte (i.e., they don't wait for a newline)
- don't depend on any external libraries
- are self-contained (no code outside of the function definition)
Version 1: readable and simple
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
Version 2: avoid repeated imports and exception handling:
[EDIT] I missed one advantage of the ActiveState code. If you plan to read characters multiple times, that code avoids the (negligible) cost of repeating the Windows import and the ImportError exception handling on Unix-like systems. While you probably should be more concerned about code readability than that negligible optimization, here is an alternative (it is similar to Louis's answer, but getChar() is self-contained) that functions the same as the ActiveState code and is more readable:
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()
Example code that exercises either of the getChar() versions above:
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))
edited Aug 24 '17 at 7:03
lucid_dreamer
17329
17329
answered May 2 '16 at 2:49
Matthew Strax-HaberMatthew Strax-Haber
7114
7114
1
I ran into an issue with tty.setraw() when printing messages while simultaneously waiting for a key (multi-threaded). Long story short, I found that using tty.setcbreak() lets you get a single character without breaking all the other normal stuff. Long story in this answer
– TheDavidFactor
May 21 '16 at 3:50
Nice, I was hoping there would be a cleaner way of doing this
– Phylliida
May 5 '17 at 18:00
add a comment |
1
I ran into an issue with tty.setraw() when printing messages while simultaneously waiting for a key (multi-threaded). Long story short, I found that using tty.setcbreak() lets you get a single character without breaking all the other normal stuff. Long story in this answer
– TheDavidFactor
May 21 '16 at 3:50
Nice, I was hoping there would be a cleaner way of doing this
– Phylliida
May 5 '17 at 18:00
1
1
I ran into an issue with tty.setraw() when printing messages while simultaneously waiting for a key (multi-threaded). Long story short, I found that using tty.setcbreak() lets you get a single character without breaking all the other normal stuff. Long story in this answer
– TheDavidFactor
May 21 '16 at 3:50
I ran into an issue with tty.setraw() when printing messages while simultaneously waiting for a key (multi-threaded). Long story short, I found that using tty.setcbreak() lets you get a single character without breaking all the other normal stuff. Long story in this answer
– TheDavidFactor
May 21 '16 at 3:50
Nice, I was hoping there would be a cleaner way of doing this
– Phylliida
May 5 '17 at 18:00
Nice, I was hoping there would be a cleaner way of doing this
– Phylliida
May 5 '17 at 18:00
add a comment |
This might be a use case for a context manager. Leaving aside allowances for Windows OS, here's my suggestion:
#!/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()
You could also returnself
in__enter__
and have aread
method that returnssys.stdin.read(1)
, then you could read multiple characters in one context.
– L3viathan
Apr 12 '17 at 6:46
add a comment |
This might be a use case for a context manager. Leaving aside allowances for Windows OS, here's my suggestion:
#!/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()
You could also returnself
in__enter__
and have aread
method that returnssys.stdin.read(1)
, then you could read multiple characters in one context.
– L3viathan
Apr 12 '17 at 6:46
add a comment |
This might be a use case for a context manager. Leaving aside allowances for Windows OS, here's my suggestion:
#!/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()
This might be a use case for a context manager. Leaving aside allowances for Windows OS, here's my suggestion:
#!/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()
edited Sep 28 '14 at 20:24
ThP
1,87121423
1,87121423
answered Sep 28 '14 at 20:07
Alex KleiderAlex Kleider
411
411
You could also returnself
in__enter__
and have aread
method that returnssys.stdin.read(1)
, then you could read multiple characters in one context.
– L3viathan
Apr 12 '17 at 6:46
add a comment |
You could also returnself
in__enter__
and have aread
method that returnssys.stdin.read(1)
, then you could read multiple characters in one context.
– L3viathan
Apr 12 '17 at 6:46
You could also return
self
in __enter__
and have a read
method that returns sys.stdin.read(1)
, then you could read multiple characters in one context.– L3viathan
Apr 12 '17 at 6:46
You could also return
self
in __enter__
and have a read
method that returns sys.stdin.read(1)
, then you could read multiple characters in one context.– L3viathan
Apr 12 '17 at 6:46
add a comment |
The answers here were informative, however I also wanted a way to get key presses asynchronously and fire off key presses in separate events, all in a thread-safe, cross-platform way. PyGame was also too bloated for me. So I made the following (in Python 2.7 but I suspect it's easily portable), which I figured I'd share here in case it was useful for anyone else. I stored this in a file named 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()
The idea is that you can either simply call keyPress.getKey()
, which will read a key from the keyboard, then return it.
If you want something more than that, I made a KeyCapture
object. You can create one via something like keys = keyPress.KeyCapture()
.
Then there are three things you can do:
addEvent(functionName)
takes in any function that takes in one parameter. Then every time a key is pressed, this function will be called with that key's string as it's input. These are ran in a separate thread, so you can block all you want in them and it won't mess up the functionality of the KeyCapturer nor delay the other events.
get()
returns a key in the same blocking way as before. It is now needed here because the keys are being captured via the KeyCapture
object now, so keyPress.getKey()
would conflict with that behavior and both of them would miss some keys since only one key can be captured at a time. Also, say the user presses 'a', then 'b', you call get()
, the the user presses 'c'. That get()
call will immediately return 'a', then if you call it again it will return 'b', then 'c'. If you call it again it will block until another key is pressed. This ensures that you don't miss any keys, in a blocking way if desired. So in this way it's a little different than keyPress.getKey()
from before
If you want the behavior of getKey()
back, get(lossy=True)
is like get()
, except that it only returns keys pressed after the call to get()
. So in the above example, get()
would block until the user presses 'c', and then if you call it again it will block until another key is pressed.
getAsync()
is a little different. It's designed for something that does a lot of processing, then occasionally comes back and checks which keys were pressed. Thus getAsync()
returns a list of all the keys pressed since the last call to getAsync()
, in order from oldest key pressed to most recent key pressed. It also doesn't block, meaning that if no keys have been pressed since the last call to getAsync()
, an empty will be returned.
To actually start capturing keys, you need to call keys.startCapture()
with your keys
object made above. startCapture
is non-blocking, and simply starts one thread that just records the key presses, and another thread to process those key presses. There are two threads to ensure that the thread that records key presses doesn't miss any keys.
If you want to stop capturing keys, you can call keys.stopCapture()
and it will stop capturing keys. However, since capturing a key is a blocking operation, the thread capturing keys might capture one more key after calling stopCapture()
.
To prevent this, you can pass in an optional parameter(s) into startCapture(functionName, args)
of a function that just does something like checks if a key equals 'c' and then exits. It's important that this function does very little before, for example, a sleep here will cause us to miss keys.
However, if stopCapture()
is called in this function, key captures will be stopped immediately, without trying to capture any more, and that all get()
calls will be returned immediately, with None if no keys have been pressed yet.
Also, since get()
and getAsync()
store all the previous keys pressed (until you retrieve them), you can call clearGetList()
and clearAsyncList()
to forget the keys previously pressed.
Note that get()
, getAsync()
and events are independent, so if a key is pressed:
1. One call to get()
that is waiting, with lossy on, will return
that key. The other waiting calls (if any) will continue waiting.
2. That key 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. That key will be stored in the list of getAsync()
keys, where that lis twill be returned and set to empty list on the next call to getAsync()
If all this is too much, here is an example use case:
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"
It is working well for me from the simple test I made, but I will happily take others feedback as well if there is something I missed.
I posted this here as well.
add a comment |
The answers here were informative, however I also wanted a way to get key presses asynchronously and fire off key presses in separate events, all in a thread-safe, cross-platform way. PyGame was also too bloated for me. So I made the following (in Python 2.7 but I suspect it's easily portable), which I figured I'd share here in case it was useful for anyone else. I stored this in a file named 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()
The idea is that you can either simply call keyPress.getKey()
, which will read a key from the keyboard, then return it.
If you want something more than that, I made a KeyCapture
object. You can create one via something like keys = keyPress.KeyCapture()
.
Then there are three things you can do:
addEvent(functionName)
takes in any function that takes in one parameter. Then every time a key is pressed, this function will be called with that key's string as it's input. These are ran in a separate thread, so you can block all you want in them and it won't mess up the functionality of the KeyCapturer nor delay the other events.
get()
returns a key in the same blocking way as before. It is now needed here because the keys are being captured via the KeyCapture
object now, so keyPress.getKey()
would conflict with that behavior and both of them would miss some keys since only one key can be captured at a time. Also, say the user presses 'a', then 'b', you call get()
, the the user presses 'c'. That get()
call will immediately return 'a', then if you call it again it will return 'b', then 'c'. If you call it again it will block until another key is pressed. This ensures that you don't miss any keys, in a blocking way if desired. So in this way it's a little different than keyPress.getKey()
from before
If you want the behavior of getKey()
back, get(lossy=True)
is like get()
, except that it only returns keys pressed after the call to get()
. So in the above example, get()
would block until the user presses 'c', and then if you call it again it will block until another key is pressed.
getAsync()
is a little different. It's designed for something that does a lot of processing, then occasionally comes back and checks which keys were pressed. Thus getAsync()
returns a list of all the keys pressed since the last call to getAsync()
, in order from oldest key pressed to most recent key pressed. It also doesn't block, meaning that if no keys have been pressed since the last call to getAsync()
, an empty will be returned.
To actually start capturing keys, you need to call keys.startCapture()
with your keys
object made above. startCapture
is non-blocking, and simply starts one thread that just records the key presses, and another thread to process those key presses. There are two threads to ensure that the thread that records key presses doesn't miss any keys.
If you want to stop capturing keys, you can call keys.stopCapture()
and it will stop capturing keys. However, since capturing a key is a blocking operation, the thread capturing keys might capture one more key after calling stopCapture()
.
To prevent this, you can pass in an optional parameter(s) into startCapture(functionName, args)
of a function that just does something like checks if a key equals 'c' and then exits. It's important that this function does very little before, for example, a sleep here will cause us to miss keys.
However, if stopCapture()
is called in this function, key captures will be stopped immediately, without trying to capture any more, and that all get()
calls will be returned immediately, with None if no keys have been pressed yet.
Also, since get()
and getAsync()
store all the previous keys pressed (until you retrieve them), you can call clearGetList()
and clearAsyncList()
to forget the keys previously pressed.
Note that get()
, getAsync()
and events are independent, so if a key is pressed:
1. One call to get()
that is waiting, with lossy on, will return
that key. The other waiting calls (if any) will continue waiting.
2. That key 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. That key will be stored in the list of getAsync()
keys, where that lis twill be returned and set to empty list on the next call to getAsync()
If all this is too much, here is an example use case:
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"
It is working well for me from the simple test I made, but I will happily take others feedback as well if there is something I missed.
I posted this here as well.
add a comment |
The answers here were informative, however I also wanted a way to get key presses asynchronously and fire off key presses in separate events, all in a thread-safe, cross-platform way. PyGame was also too bloated for me. So I made the following (in Python 2.7 but I suspect it's easily portable), which I figured I'd share here in case it was useful for anyone else. I stored this in a file named 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()
The idea is that you can either simply call keyPress.getKey()
, which will read a key from the keyboard, then return it.
If you want something more than that, I made a KeyCapture
object. You can create one via something like keys = keyPress.KeyCapture()
.
Then there are three things you can do:
addEvent(functionName)
takes in any function that takes in one parameter. Then every time a key is pressed, this function will be called with that key's string as it's input. These are ran in a separate thread, so you can block all you want in them and it won't mess up the functionality of the KeyCapturer nor delay the other events.
get()
returns a key in the same blocking way as before. It is now needed here because the keys are being captured via the KeyCapture
object now, so keyPress.getKey()
would conflict with that behavior and both of them would miss some keys since only one key can be captured at a time. Also, say the user presses 'a', then 'b', you call get()
, the the user presses 'c'. That get()
call will immediately return 'a', then if you call it again it will return 'b', then 'c'. If you call it again it will block until another key is pressed. This ensures that you don't miss any keys, in a blocking way if desired. So in this way it's a little different than keyPress.getKey()
from before
If you want the behavior of getKey()
back, get(lossy=True)
is like get()
, except that it only returns keys pressed after the call to get()
. So in the above example, get()
would block until the user presses 'c', and then if you call it again it will block until another key is pressed.
getAsync()
is a little different. It's designed for something that does a lot of processing, then occasionally comes back and checks which keys were pressed. Thus getAsync()
returns a list of all the keys pressed since the last call to getAsync()
, in order from oldest key pressed to most recent key pressed. It also doesn't block, meaning that if no keys have been pressed since the last call to getAsync()
, an empty will be returned.
To actually start capturing keys, you need to call keys.startCapture()
with your keys
object made above. startCapture
is non-blocking, and simply starts one thread that just records the key presses, and another thread to process those key presses. There are two threads to ensure that the thread that records key presses doesn't miss any keys.
If you want to stop capturing keys, you can call keys.stopCapture()
and it will stop capturing keys. However, since capturing a key is a blocking operation, the thread capturing keys might capture one more key after calling stopCapture()
.
To prevent this, you can pass in an optional parameter(s) into startCapture(functionName, args)
of a function that just does something like checks if a key equals 'c' and then exits. It's important that this function does very little before, for example, a sleep here will cause us to miss keys.
However, if stopCapture()
is called in this function, key captures will be stopped immediately, without trying to capture any more, and that all get()
calls will be returned immediately, with None if no keys have been pressed yet.
Also, since get()
and getAsync()
store all the previous keys pressed (until you retrieve them), you can call clearGetList()
and clearAsyncList()
to forget the keys previously pressed.
Note that get()
, getAsync()
and events are independent, so if a key is pressed:
1. One call to get()
that is waiting, with lossy on, will return
that key. The other waiting calls (if any) will continue waiting.
2. That key 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. That key will be stored in the list of getAsync()
keys, where that lis twill be returned and set to empty list on the next call to getAsync()
If all this is too much, here is an example use case:
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"
It is working well for me from the simple test I made, but I will happily take others feedback as well if there is something I missed.
I posted this here as well.
The answers here were informative, however I also wanted a way to get key presses asynchronously and fire off key presses in separate events, all in a thread-safe, cross-platform way. PyGame was also too bloated for me. So I made the following (in Python 2.7 but I suspect it's easily portable), which I figured I'd share here in case it was useful for anyone else. I stored this in a file named 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()
The idea is that you can either simply call keyPress.getKey()
, which will read a key from the keyboard, then return it.
If you want something more than that, I made a KeyCapture
object. You can create one via something like keys = keyPress.KeyCapture()
.
Then there are three things you can do:
addEvent(functionName)
takes in any function that takes in one parameter. Then every time a key is pressed, this function will be called with that key's string as it's input. These are ran in a separate thread, so you can block all you want in them and it won't mess up the functionality of the KeyCapturer nor delay the other events.
get()
returns a key in the same blocking way as before. It is now needed here because the keys are being captured via the KeyCapture
object now, so keyPress.getKey()
would conflict with that behavior and both of them would miss some keys since only one key can be captured at a time. Also, say the user presses 'a', then 'b', you call get()
, the the user presses 'c'. That get()
call will immediately return 'a', then if you call it again it will return 'b', then 'c'. If you call it again it will block until another key is pressed. This ensures that you don't miss any keys, in a blocking way if desired. So in this way it's a little different than keyPress.getKey()
from before
If you want the behavior of getKey()
back, get(lossy=True)
is like get()
, except that it only returns keys pressed after the call to get()
. So in the above example, get()
would block until the user presses 'c', and then if you call it again it will block until another key is pressed.
getAsync()
is a little different. It's designed for something that does a lot of processing, then occasionally comes back and checks which keys were pressed. Thus getAsync()
returns a list of all the keys pressed since the last call to getAsync()
, in order from oldest key pressed to most recent key pressed. It also doesn't block, meaning that if no keys have been pressed since the last call to getAsync()
, an empty will be returned.
To actually start capturing keys, you need to call keys.startCapture()
with your keys
object made above. startCapture
is non-blocking, and simply starts one thread that just records the key presses, and another thread to process those key presses. There are two threads to ensure that the thread that records key presses doesn't miss any keys.
If you want to stop capturing keys, you can call keys.stopCapture()
and it will stop capturing keys. However, since capturing a key is a blocking operation, the thread capturing keys might capture one more key after calling stopCapture()
.
To prevent this, you can pass in an optional parameter(s) into startCapture(functionName, args)
of a function that just does something like checks if a key equals 'c' and then exits. It's important that this function does very little before, for example, a sleep here will cause us to miss keys.
However, if stopCapture()
is called in this function, key captures will be stopped immediately, without trying to capture any more, and that all get()
calls will be returned immediately, with None if no keys have been pressed yet.
Also, since get()
and getAsync()
store all the previous keys pressed (until you retrieve them), you can call clearGetList()
and clearAsyncList()
to forget the keys previously pressed.
Note that get()
, getAsync()
and events are independent, so if a key is pressed:
1. One call to get()
that is waiting, with lossy on, will return
that key. The other waiting calls (if any) will continue waiting.
2. That key 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. That key will be stored in the list of getAsync()
keys, where that lis twill be returned and set to empty list on the next call to getAsync()
If all this is too much, here is an example use case:
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"
It is working well for me from the simple test I made, but I will happily take others feedback as well if there is something I missed.
I posted this here as well.
edited May 23 '17 at 12:10
Community♦
11
11
answered Jul 31 '15 at 15:17
![](https://i.stack.imgur.com/AcGoY.jpg?s=32&g=1)
![](https://i.stack.imgur.com/AcGoY.jpg?s=32&g=1)
PhylliidaPhylliida
2,2541224
2,2541224
add a comment |
add a comment |
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)
3
does this work for command line applications?
– ThorSummoner
Jan 23 '15 at 7:36
Yes @ThorSummoner
– Davoud Taghawi-Nejad
Mar 27 '15 at 3:12
1
@ThorSummoner: This code has a number of problems — so no, it won't work for command line applications.
– martineau
Aug 31 '15 at 0:29
It runs for a command line application, given that the windows manager is running.
– Davoud Taghawi-Nejad
Aug 31 '15 at 17:36
1
So basically it doesn't work for command line applications...
– Mehdi
Nov 17 '15 at 13:06
|
show 1 more comment
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)
3
does this work for command line applications?
– ThorSummoner
Jan 23 '15 at 7:36
Yes @ThorSummoner
– Davoud Taghawi-Nejad
Mar 27 '15 at 3:12
1
@ThorSummoner: This code has a number of problems — so no, it won't work for command line applications.
– martineau
Aug 31 '15 at 0:29
It runs for a command line application, given that the windows manager is running.
– Davoud Taghawi-Nejad
Aug 31 '15 at 17:36
1
So basically it doesn't work for command line applications...
– Mehdi
Nov 17 '15 at 13:06
|
show 1 more comment
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)
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)
edited Jun 22 '14 at 21:03
answered Jun 22 '14 at 20:40
Davoud Taghawi-NejadDavoud Taghawi-Nejad
9,04894365
9,04894365
3
does this work for command line applications?
– ThorSummoner
Jan 23 '15 at 7:36
Yes @ThorSummoner
– Davoud Taghawi-Nejad
Mar 27 '15 at 3:12
1
@ThorSummoner: This code has a number of problems — so no, it won't work for command line applications.
– martineau
Aug 31 '15 at 0:29
It runs for a command line application, given that the windows manager is running.
– Davoud Taghawi-Nejad
Aug 31 '15 at 17:36
1
So basically it doesn't work for command line applications...
– Mehdi
Nov 17 '15 at 13:06
|
show 1 more comment
3
does this work for command line applications?
– ThorSummoner
Jan 23 '15 at 7:36
Yes @ThorSummoner
– Davoud Taghawi-Nejad
Mar 27 '15 at 3:12
1
@ThorSummoner: This code has a number of problems — so no, it won't work for command line applications.
– martineau
Aug 31 '15 at 0:29
It runs for a command line application, given that the windows manager is running.
– Davoud Taghawi-Nejad
Aug 31 '15 at 17:36
1
So basically it doesn't work for command line applications...
– Mehdi
Nov 17 '15 at 13:06
3
3
does this work for command line applications?
– ThorSummoner
Jan 23 '15 at 7:36
does this work for command line applications?
– ThorSummoner
Jan 23 '15 at 7:36
Yes @ThorSummoner
– Davoud Taghawi-Nejad
Mar 27 '15 at 3:12
Yes @ThorSummoner
– Davoud Taghawi-Nejad
Mar 27 '15 at 3:12
1
1
@ThorSummoner: This code has a number of problems — so no, it won't work for command line applications.
– martineau
Aug 31 '15 at 0:29
@ThorSummoner: This code has a number of problems — so no, it won't work for command line applications.
– martineau
Aug 31 '15 at 0:29
It runs for a command line application, given that the windows manager is running.
– Davoud Taghawi-Nejad
Aug 31 '15 at 17:36
It runs for a command line application, given that the windows manager is running.
– Davoud Taghawi-Nejad
Aug 31 '15 at 17:36
1
1
So basically it doesn't work for command line applications...
– Mehdi
Nov 17 '15 at 13:06
So basically it doesn't work for command line applications...
– Mehdi
Nov 17 '15 at 13:06
|
show 1 more comment
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
add a comment |
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
add a comment |
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
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
edited Apr 28 '17 at 20:40
NinjaFart
1,38611215
1,38611215
answered Jul 21 '15 at 21:42
jdev6jdev6
312
312
add a comment |
add a comment |
Try this with 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, "."
That is a neat idea, but it doesn't work on the command line:pygame.error: video system not initialized
– dirkjot
Apr 21 '15 at 11:15
add a comment |
Try this with 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, "."
That is a neat idea, but it doesn't work on the command line:pygame.error: video system not initialized
– dirkjot
Apr 21 '15 at 11:15
add a comment |
Try this with 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, "."
Try this with 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, "."
edited Apr 3 '16 at 14:08
![](https://i.stack.imgur.com/moDhz.jpg?s=32&g=1)
![](https://i.stack.imgur.com/moDhz.jpg?s=32&g=1)
Hackaholic
12.4k12541
12.4k12541
answered Feb 21 '15 at 0:36
PyGuyPyGuy
291
291
That is a neat idea, but it doesn't work on the command line:pygame.error: video system not initialized
– dirkjot
Apr 21 '15 at 11:15
add a comment |
That is a neat idea, but it doesn't work on the command line:pygame.error: video system not initialized
– dirkjot
Apr 21 '15 at 11:15
That is a neat idea, but it doesn't work on the command line:
pygame.error: video system not initialized
– dirkjot
Apr 21 '15 at 11:15
That is a neat idea, but it doesn't work on the command line:
pygame.error: video system not initialized
– dirkjot
Apr 21 '15 at 11:15
add a comment |
A comment in one of the other answers mentioned cbreak mode, which is important for Unix implementations because you generally don't want ^C (KeyboardError
) to be consumed by getchar (as it will when you set the terminal to raw mode, as done by most other answers).
Another important detail is that if you're looking to read one character and not one byte, you should read 4 bytes from the input stream, as that's the maximum number of bytes a single character will consist of in UTF-8 (Python 3+). Reading only a single byte will produce unexpected results for multi-byte characters such as keypad arrows.
Here's my changed implementation for 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)
add a comment |
A comment in one of the other answers mentioned cbreak mode, which is important for Unix implementations because you generally don't want ^C (KeyboardError
) to be consumed by getchar (as it will when you set the terminal to raw mode, as done by most other answers).
Another important detail is that if you're looking to read one character and not one byte, you should read 4 bytes from the input stream, as that's the maximum number of bytes a single character will consist of in UTF-8 (Python 3+). Reading only a single byte will produce unexpected results for multi-byte characters such as keypad arrows.
Here's my changed implementation for 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)
add a comment |
A comment in one of the other answers mentioned cbreak mode, which is important for Unix implementations because you generally don't want ^C (KeyboardError
) to be consumed by getchar (as it will when you set the terminal to raw mode, as done by most other answers).
Another important detail is that if you're looking to read one character and not one byte, you should read 4 bytes from the input stream, as that's the maximum number of bytes a single character will consist of in UTF-8 (Python 3+). Reading only a single byte will produce unexpected results for multi-byte characters such as keypad arrows.
Here's my changed implementation for 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)
A comment in one of the other answers mentioned cbreak mode, which is important for Unix implementations because you generally don't want ^C (KeyboardError
) to be consumed by getchar (as it will when you set the terminal to raw mode, as done by most other answers).
Another important detail is that if you're looking to read one character and not one byte, you should read 4 bytes from the input stream, as that's the maximum number of bytes a single character will consist of in UTF-8 (Python 3+). Reading only a single byte will produce unexpected results for multi-byte characters such as keypad arrows.
Here's my changed implementation for 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)
answered Jul 23 '17 at 15:17
NoahNoah
759415
759415
add a comment |
add a comment |
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 %sr' % 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!')
add a comment |
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 %sr' % 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!')
add a comment |
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 %sr' % 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!')
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 %sr' % 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!')
answered Dec 29 '14 at 17:38
John MarkJohn Mark
1417
1417
add a comment |
add a comment |
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
add a comment |
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
add a comment |
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
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
edited Jan 7 '18 at 12:07
answered Jan 7 '18 at 10:09
ibicibic
312510
312510
add a comment |
add a comment |
The build-in raw_input should help.
for i in range(3):
print ("So much work to do!")
k = raw_input("Press any key to continue...")
print ("Ok, back to work.")
6
raw_input is waiting for enter key
– vac
Dec 16 '15 at 22:03
add a comment |
The build-in raw_input should help.
for i in range(3):
print ("So much work to do!")
k = raw_input("Press any key to continue...")
print ("Ok, back to work.")
6
raw_input is waiting for enter key
– vac
Dec 16 '15 at 22:03
add a comment |
The build-in raw_input should help.
for i in range(3):
print ("So much work to do!")
k = raw_input("Press any key to continue...")
print ("Ok, back to work.")
The build-in raw_input should help.
for i in range(3):
print ("So much work to do!")
k = raw_input("Press any key to continue...")
print ("Ok, back to work.")
answered Dec 15 '15 at 20:51
MabookaMabooka
171
171
6
raw_input is waiting for enter key
– vac
Dec 16 '15 at 22:03
add a comment |
6
raw_input is waiting for enter key
– vac
Dec 16 '15 at 22:03
6
6
raw_input is waiting for enter key
– vac
Dec 16 '15 at 22:03
raw_input is waiting for enter key
– vac
Dec 16 '15 at 22:03
add a comment |
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("rn")
tty.tcdrain(sys.stdin.fileno())
add a comment |
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("rn")
tty.tcdrain(sys.stdin.fileno())
add a comment |
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("rn")
tty.tcdrain(sys.stdin.fileno())
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("rn")
tty.tcdrain(sys.stdin.fileno())
answered Jan 28 '16 at 1:24
xroxro
1
1
add a comment |
add a comment |
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!")
add a comment |
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!")
add a comment |
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!")
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!")
answered Nov 29 '17 at 9:14
theAlsetheAlse
2,89054490
2,89054490
add a comment |
add a comment |
If I'm doing something complicated I'll use curses to read keys. But a lot of times I just want a simple Python 3 script that uses the standard library and can read arrow keys, so I do this:
import sys, termios, tty
key_Enter = 13
key_Esc = 27
key_Up = '33[A'
key_Dn = '33[B'
key_Rt = '33[C'
key_Lt = '33[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 = '33' + ch[1:]
termios.tcsetattr(fdInput, termios.TCSADRAIN, termAttr)
return ch
add a comment |
If I'm doing something complicated I'll use curses to read keys. But a lot of times I just want a simple Python 3 script that uses the standard library and can read arrow keys, so I do this:
import sys, termios, tty
key_Enter = 13
key_Esc = 27
key_Up = '33[A'
key_Dn = '33[B'
key_Rt = '33[C'
key_Lt = '33[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 = '33' + ch[1:]
termios.tcsetattr(fdInput, termios.TCSADRAIN, termAttr)
return ch
add a comment |
If I'm doing something complicated I'll use curses to read keys. But a lot of times I just want a simple Python 3 script that uses the standard library and can read arrow keys, so I do this:
import sys, termios, tty
key_Enter = 13
key_Esc = 27
key_Up = '33[A'
key_Dn = '33[B'
key_Rt = '33[C'
key_Lt = '33[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 = '33' + ch[1:]
termios.tcsetattr(fdInput, termios.TCSADRAIN, termAttr)
return ch
If I'm doing something complicated I'll use curses to read keys. But a lot of times I just want a simple Python 3 script that uses the standard library and can read arrow keys, so I do this:
import sys, termios, tty
key_Enter = 13
key_Esc = 27
key_Up = '33[A'
key_Dn = '33[B'
key_Rt = '33[C'
key_Lt = '33[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 = '33' + ch[1:]
termios.tcsetattr(fdInput, termios.TCSADRAIN, termAttr)
return ch
edited Sep 24 '18 at 5:20
answered Sep 23 '18 at 0:07
![](https://i.stack.imgur.com/PNalc.png?s=32&g=1)
![](https://i.stack.imgur.com/PNalc.png?s=32&g=1)
qelqel
47944
47944
add a comment |
add a comment |
protected by eyllanesc Jun 23 '18 at 22:25
Thank you for your interest in this question.
Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).
Would you like to answer one of these unanswered questions instead?
K1cEZTsaBJL2d0qK19UZThuV96qO yxPoXwQRA5a MvtVyeofYckM THwR,hGsqZI0
On windows I ran into the same problem as in this question. The solution is to replace the
msvcrt.getch
withmsvcrt.getwch
, as suggested there.– A. Roy
Sep 8 '18 at 18:53