2013-04-13 28 views
9

我需要一個將輸入讀入緩衝區的函數,因爲raw_input()會,但不是回顯輸入和阻塞,直到返回完整的行,它應該超壓回聲並在每次緩衝區更改時調用回調Python,「過濾」行編輯,通過char讀取stdin無回聲

我說「緩衝區更改」而不是「字符被讀取」,因爲,作爲raw_input(),我希望它知道特殊鍵。例如,退格應該可以工作。

如果我想,例如,使用回調來模擬輸入大寫的回聲,代碼應該是這樣的:

def callback(text): 
    print '\r' + text.upper() 

read_input(callback) 

我怎樣才能做到這一點?

注: 我一直在嘗試使用readlinecurses以滿足我的目的,但兩者Python綁定是不完整的。 curses不能在不清除整個屏幕的情況下啓動,並且readline在任何輸入開始之前提供單個掛鉤。

回答

10

那麼,我手寫了代碼。我會留下解釋以備將來參考。

要求
import sys, tty, termios, codecs, unicodedata 
from contextlib import contextmanager 

禁用行緩衝

是指當簡單地讀取標準輸入是行緩衝的第一個問題。我們希望單個字符在沒有必要的換行符的情況下到達我們的程序,這不是終端操作的默認方式。

對於這一點,我寫了一個處理tty配置的上下文管理器:

@contextmanager 
def cbreak(): 
    old_attrs = termios.tcgetattr(sys.stdin) 
    tty.setcbreak(sys.stdin) 
    try: 
     yield 
    finally: 
     termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_attrs) 

該管理器使下列成語:

with cbreak(): 
    single_char_no_newline = sys.stdin.read(1) 

它進行清理時,我們就大功告成了很重要,或者終端可能需要reset

解碼標準輸入

的第二個問題,只需讀取標準輸入編碼。非ASCII字符會逐字節地到達我們,這是完全不希望的。

要正確解碼標準輸入,我寫了一臺發電機,我們可以遍歷爲Unicode字符:

def uinput(): 
    reader = codecs.getreader(sys.stdin.encoding)(sys.stdin) 
    with cbreak(): 
     while True: 
      yield reader.read(1) 

這可能會失敗過管道。我不確定。然而,對於我的用例,它會選擇正確的編碼並生成一串字符。

處理特殊字符

首先,我們應該能夠從控制那些分不清打印字符:

def is_printable(c): 
    return not unicodedata.category(c).startswith('C') 
從printables

除此之外,現在,我只想處理←退格Ctrl鍵d序列:

def is_backspace(c): 
    return c in ('\x08','\x7F') 

def is_interrupt(c): 
    return c == '\x04' 

把它放在一起:xinput()

一切都在現在。我想要的功能原始合同是讀取輸入,處理特殊字符,調用回調。實施只是反映了:

def xinput(callback): 
    text = '' 

    for c in uinput(): 
     if is_printable(c): text += c 
     elif is_backspace(c): text = text[:-1] 
     elif is_interrupt(c): break 

     callback(text) 

    return text 

想出來

def test(text): 
    print 'Buffer now holds', text 

xinput(test) 

運行它,然後鍵入Hellx←退格啊,世界顯示:

Buffer now holds H 
Buffer now holds He 
Buffer now holds Hel 
Buffer now holds Hell 
Buffer now holds Hellx 
Buffer now holds Hell 
Buffer now holds Hello 
Buffer now holds Hello 
Buffer now holds Hello w 
Buffer now holds Hello wo 
Buffer now holds Hello wor 
Buffer now holds Hello worl 
Buffer now holds Hello world