2016-02-13 363 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和其他終端命令? –