2016-02-13 365 views
0

注:我想这样做,无需使用任何外部包装,像pygame的,等实时抓拍和按键的处理(例如按键事件)

我试图捕捉单个按键为他们到达并针对特定角色执行操作,无论我是仅仅想“重新回显”角色,还是根本不显示它并执行其他操作。

我已经找到了一个跨平台(虽然关于OS X不知道)的getch()实现,因为我不想念想输入一整行()的作用:

# 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] 我得到一个例子使用螺纹的方法(https://stackoverflow.com/a/14043979/2752206),但它“锁死”,将不接受任何输入(包括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() 

有没有人有任何建议或指导?或者更重要的是,有人可以解释为什么这两次尝试不起作用?谢谢。

+0

您返回函数'msvcrt.getch',但不是被调用的结果。 – zondo

+0

谢谢你的收获。我修改了一段代码,但是我在Linux上。 = P –

回答

1

首先,我没有真正需要多线程的东西。例如,如果您想要执行一些任务,比如在屏幕上绘图或执行任何操作,并在执行此操作时捕获密钥,则需要这样做。

让我们考虑一个情况,你只想捕获按键,并且在每个按键执行某个动作之后:退出,如果x被按下,否则只是打印字符。所有你需要的这种情况很简单,而循环

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()等待一个条目,然后返回它。在这种情况下,全局变量并不是很有用,所以你可能只需在循环中调用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()

+0

谢谢!这是一个很好的解释。 StackExchange的其他“解决方案”使用睡眠和/或线程,但这很简单。我打算在输入时进行绘图,所以也要感谢线程解释。 虽然有一个问题,在线程示例中按x不会退出并停止。 –

+0

@RobAlexander它会停止单独的线程,而不是主线程。你需要发送SIGINT(ctrl + c)按x后杀死主线程 – kecer

+0

谢谢。最后一个问题,我不确定是否需要开始一个新的问题。但任何建议让getch()不能捕获32位以下的ASCII代码,即ctrl和命令代码?或者以某种方式“重新推出”密钥,以便允许ctrl-z和其他终端命令? –