实时捕获和处理按键(例如按键事件)
Real-time capture and processing of keypresses (e.g. keypress event)
注意:我想在不使用任何外部包的情况下执行此操作,例如 PyGame 等
我正在尝试在单个按键到达时捕获它们并针对特定字符执行操作,无论我只是想 "re-echo" 字符,还是根本不显示它并执行其他操作。
我找到了一个跨平台(虽然不确定 OS X)getch() 实现,因为我不想像 input() 那样阅读整行:
# 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
[尝试 1]
我首先尝试了一个简单的 while-true 循环来轮询 getch,但是如果我打字太快,字符就会丢失。减少睡眠时间会使情况变得更糟。调试语句仅在按下回车键时打印,时间和位置都不一致。 (看起来可能有一些线路缓冲正在进行?)取出循环和睡眠让它工作一次但完美。
#!/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()
[尝试 2]
我得到了一个使用线程方法 () 的示例,但它 "locks up" 并且不接受任何输入(包括 Ctrl-C
等)..
#!/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()
有没有人有任何建议或指导?或者更重要的是,有人可以解释 为什么 两次尝试都不起作用吗?谢谢。
首先,我真的不认为您需要多线程。例如,如果您想要执行一些任务,例如在屏幕上绘图或执行此操作时捕获按键等任务,则需要它。
让我们考虑这样一种情况,您只想捕获按键并在每次按键后执行一些操作:如果 x 被按下,则退出,否则只打印字符。在这种情况下,你所需要的只是简单的 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)
注意没有睡眠()。我假设您认为 getch() 不会等待用户输入,所以您设置了 1s 睡眠时间。但是,您的 getch() 等待一个条目然后 returns 它。在这种情况下,全局变量并不是很有用,所以你还不如在循环中调用 process(getch())。
print(key, end="", flush=True)
=> 额外的参数将确保按下的键保持在一行上,而不是每次打印时都附加换行符。
另一种情况,您希望同时执行不同的东西,应该使用线程。
考虑这段代码:
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)
这将创建全局变量 n
并在主线程中每秒递增 10 次。同时,key_capturing
方法监听按下的键并执行与前面示例相同的操作+当您在键盘上按下 n 时,全局变量的当前值 n
将被打印出来。
结束语:正如@zondo 指出的那样,您确实错过了 getch() 定义中的大括号。 return msvcrt.getch
最有可能是 return msvcrt.getch()
注意:我想在不使用任何外部包的情况下执行此操作,例如 PyGame 等
我正在尝试在单个按键到达时捕获它们并针对特定字符执行操作,无论我只是想 "re-echo" 字符,还是根本不显示它并执行其他操作。
我找到了一个跨平台(虽然不确定 OS X)getch() 实现,因为我不想像 input() 那样阅读整行:
# 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
[尝试 1] 我首先尝试了一个简单的 while-true 循环来轮询 getch,但是如果我打字太快,字符就会丢失。减少睡眠时间会使情况变得更糟。调试语句仅在按下回车键时打印,时间和位置都不一致。 (看起来可能有一些线路缓冲正在进行?)取出循环和睡眠让它工作一次但完美。
#!/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()
[尝试 2]
我得到了一个使用线程方法 () 的示例,但它 "locks up" 并且不接受任何输入(包括 Ctrl-C
等)..
#!/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()
有没有人有任何建议或指导?或者更重要的是,有人可以解释 为什么 两次尝试都不起作用吗?谢谢。
首先,我真的不认为您需要多线程。例如,如果您想要执行一些任务,例如在屏幕上绘图或执行此操作时捕获按键等任务,则需要它。
让我们考虑这样一种情况,您只想捕获按键并在每次按键后执行一些操作:如果 x 被按下,则退出,否则只打印字符。在这种情况下,你所需要的只是简单的 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)
注意没有睡眠()。我假设您认为 getch() 不会等待用户输入,所以您设置了 1s 睡眠时间。但是,您的 getch() 等待一个条目然后 returns 它。在这种情况下,全局变量并不是很有用,所以你还不如在循环中调用 process(getch())。
print(key, end="", flush=True)
=> 额外的参数将确保按下的键保持在一行上,而不是每次打印时都附加换行符。
另一种情况,您希望同时执行不同的东西,应该使用线程。
考虑这段代码:
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)
这将创建全局变量 n
并在主线程中每秒递增 10 次。同时,key_capturing
方法监听按下的键并执行与前面示例相同的操作+当您在键盘上按下 n 时,全局变量的当前值 n
将被打印出来。
结束语:正如@zondo 指出的那样,您确实错过了 getch() 定义中的大括号。 return msvcrt.getch
最有可能是 return msvcrt.getch()