问题
Note: I want to do this without using any external packages, like PyGame, etc.
I am attempting to capture individual keypresses as they arrive and perform an action for specific characters, whether I simply want to "re-echo" the character, or not display it at all and do something else.
I have found a cross-platform (though not sure about OS X) getch() implementation because I do not want to read a whole line like input() does:
# http://code.activestate.com/recipes/134892/
def 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.
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
[Attempt 1] I first tried a simple while-true loop to poll getch, but if I type too fast, characters go missing. Reducing the sleep time makes it worse. The debug statements only print on press of the enter key and not consistently in time nor position. (It appears there might be some line buffering going on?) Taking out the loop and sleep lets it work once but perfectly.
#!/usr/bin/env python3
import sys
import tty
import time
def main():
while True:
time.sleep(1)
sys.stdout.write(" DEBUG:Before ")
sys.stdout.write(getch())
sys.stdout.write(" DEBUG:After ")
if __name__ == "__main__":
main()
[Attempt 2]
I got an example for using a threaded approach (https://stackoverflow.com/a/14043979/2752206) but it "locks up" and won't accept any input (including Ctrl-C
, and etc)..
#!/usr/bin/env python3
import sys
import tty
import time
import threading
key = 'Z'
def main():
threading.Thread(target=getchThread).start()
while True:
time.sleep(1)
sys.stdout.write(" DEBUG:Before ")
sys.stdout.write(key)
sys.stdout.write(" DEBUG:After ")
def getchThread():
global key
lock = threading.Lock()
while True:
with lock:
key = getch()
if __name__ == "__main__":
main()
Does anyone have any advice or guidance? Or more importantly, can someone explain why the two attempts do not work? Thanks.
回答1:
First off, I don't really thing you need multithreading. You'd need that if you, for example, wanted to do some tasks like drawing on screen or whatever and capturing keys while you do this.
Let's consider a case where you only want to capture keys and after each keypress execute some action: Exit, if x was pressed, otherwise just print the character. All you need for this case is simple while loop
def process(key):
if key == 'x':
exit('exitting')
else:
print(key, end="", flush=True)
if __name__ == "__main__":
while True:
key = getch()
process(key)
Notice absence of sleep(). I am assuming you thought getch() won't wait for user input so you set 1s sleep time. However, your getch() waits for one entry and then returns it. In this case, global variable is not really useful, so you might as well just call process(getch()) inside the loop.
print(key, end="", flush=True)
=> the extra arguments will ensure pressed keys stay on one line, not appending newline character every time you print something.
The other case, where you'd want to execute different stuff simultaneously, should use threading.
Consider this code:
n = 0
quit = False
def process(key):
if key == 'x':
global quit
quit = True
exit('exitting')
elif key == 'n':
global n
print(n)
else:
print(key, end="", flush=True)
def key_capturing():
while True:
process(getch())
if __name__ == "__main__":
threading.Thread(target=key_capturing).start()
while not quit:
n += 1
time.sleep(0.1)
This will create global variable n
and increment it 10 times a second in main thread. Simultaneously, key_capturing
method listens to keys pressed and does the same thing as in previous example + when you press n on your keyboard, current value of the global variable n
will be printed.
Closing note: as @zondo noted, you really missed braces in the getch() definition. return msvcrt.getch
should most likely be return msvcrt.getch()
来源:https://stackoverflow.com/questions/35383970/real-time-capture-and-processing-of-keypresses-e-g-keypress-event