2013-04-13 15 views
9

Potrzebuję funkcji, która odczyta dane wejściowe do bufora jako raw_input(), ale zamiast echo wprowadzania i blokowania, aż do otrzymania pełnej linii, powinno to być tłumienie echa i wywołaj wywołanie zwrotne za każdym razem, gdy bufor zmieni się na.Python, "przefiltrowana" edycja linii, odczytanie stdin przez char bez echa

Mówię "zmiany bufora" zamiast "odczytuje się znak", ponieważ, jako raw_input(), chciałbym, aby był on świadomy specjalnych kluczy. Backspace powinien działać, na przykład.

Gdybym chciał, na przykład, użyj zwrotnego symulować duże litery echo wejścia, kod wyglądałby następująco:

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

read_input(callback) 

W jaki sposób można to osiągnąć?

UWAGA: Próbowałem użyć readline i curses aby spełnić moje końce, ale oba Wiązania Pythona są niekompletne. curses nie można uruchomić bez czyszczenia całego ekranu, a readline oferuje pojedynczy hak przed rozpoczęciem wprowadzania danych.

Odpowiedz

10

Cóż, napisałem kod ręcznie. Zostawię wyjaśnienie na przyszłość.

Wymagania

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

Wyłączenie bufor linii

Pierwszy problem, który pojawia się, gdy tylko odczytu standardowe wejście jest bufor linii. Chcemy, aby pojedyncze postacie dotarły do ​​naszego programu bez wymaganego znaku nowej linii, a to nie jest domyślny sposób działania terminala.

W tym pisałem menedżera kontekstowego, który obsługuje tty konfigurację:

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

Menadżer umożliwia następujące idiom:

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

ważne jest, aby wykonać czyste, kiedy skończymy lub terminal może potrzebować reset.

dekodowania standardowe wejście

Drugi problem tylko czytać standardowe wejście jest kodowanie. Znaki Unicode w formacie innym niż ascii docierają do nas bajt po bajcie, co jest całkowicie niepożądane.

Aby poprawnie dekodować stdin pisałem generator, który możemy iterację dla znaków Unicode:

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

Może to fail over rur. Nie jestem pewny. Jednak w moim przypadku używa prawidłowego kodowania i generuje strumień znaków.

Handling znaki specjalne

Po pierwsze, powinniśmy być w stanie powiedzieć znaki drukowalne oprócz tych kontroli:

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

Oprócz printables, na razie, tylko chcę obsłużyć ← Backspace i CtrlD sekwencji:

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

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

Składając razem: xinput()

Wszystko jest już gotowe. Oryginalna umowa dla funkcji, którą chciałem, to odczytanie danych wejściowych, obsługa znaków specjalnych, wywołanie oddzwonienia. Realizacja odzwierciedla tylko, że:

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 

próbuje ją

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

xinput(test) 

Running go i wpisując Hellx← Backspaceo Świat pokazuje:

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 
Powiązane problemy