2013-04-13 20 views
9

raw_input() olarak bir arabelleğe girdi okuyan bir işleve ihtiyacım var, ancak tam bir satır döndürene kadar girdi ekleyip engellemek yerine, supress echo ve arabellek her değiştirdiğinde bir geri çağırma çağırın.Python, "filtrelenmiş" satır düzenlemesi, yanlışı olmayan stdin'i yankısız okur

Ben raw_input() olarak, ben özel tuşları farkında olmak istiyorum, çünkü "karakteri okunur" yerine "Değişiklikleri tampon" derler. Örneğin, backspace çalışmalıdır. Ben, örneğin, girişin Büyük harfli yankı simüle etmek için geri arama kullanmak istiyorsa

, kod şu şekilde görünecektir:

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

read_input(callback) 

Bunu nasıl başarabiliriz?

NOT: Ben biter karşılamak için readline ve curses kullanmaya çalışıyorum, ancak her iki Python bağlantıları eksiktir. curses tüm ekranı temizlemeden başlamak yapılır ve herhangi bir giriş başlamadan önce readline tek kanca sunmaktadır edilemez.

cevap

10

Eh, elle kod yazdım. Gelecekte referans olması için bir açıklama bırakacağım.

Koşullar hattı tamponlama

sadece Stdin okuma hattı tamponlama olduğunda ortaya çıkan ilk sorun, devre dışı bırakma

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

. Tek bir karakterin programımıza gerekli yeni bir satır olmadan ulaşmasını istiyoruz ve bu, terminalin çalıştığı varsayılan yol değil.

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

Biz bitince temiz yukarı gerçekleştirmek önemlidir:

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

Bu yönetici aşağıdaki deyim sağlar: Bunun için

, ben tty yapılandırmayı işleyen bir bağlam yöneticisi yazdı veya terminalin bir reset'a ihtiyacı olabilir.

sadece Stdin kodlayan bir okuma ikinci bir sorun, kod çözme Stdin. Ascii unicode karakterleri bize tamamen istenmeyen olan byte by byte'a ulaşacak.

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

Bu boruların üzerinde başarısız olabilir: Düzgün stdin'nin deşifre etmek

, biz Unicode karakterleri için yineleme bir jeneratör yazdı. Emin değilim. Ancak benim kullanım durumum için, doğru kodlamayı seçer ve bir karakter akışı üretir.

Handling özel karakterler

Öncelikle, kontrol olanlardan ayrı yazdırılabilir karakterleri söylemek mümkün olmalıdır:

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

, şimdilik, sadece işlemek istiyorum ← geri tuşu ve CtrlD sekansı

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

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

Bir araya getirme: xinput()

Her şey şu anda hazır. İstediğim işlev için orijinal sözleşme okuma girişi, özel karakterleri ele al, geri arama çağrısı.

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 

← geri silme Hellx bunu Koşu ve yazarak

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

xinput(test) 

dışarı çalışılıyoro Dünya gösterir:

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 
uygulama sadece yansıtır