Leggere l'input da raw_input () senza avere il prompt sovrascritto da altri thread in Python

StackOverflow https://stackoverflow.com/questions/2082387

Domanda

Sto cercando di lasciare che i comandi di input dell'utente su una console utilizzano raw_input (), funziona bene. Il problema è che ho thread di sfondo che occasionalmente output di log-informazione sullo schermo e quando fanno incasinano il prompt di input (poiché l'output vanno ovunque il cursore sia al momento).

Questo è un piccolo programma Python che illustra ciò che intendo.

#!/usr/bin/env python
import threading
import time

def message_loop():
    while True:
        time.sleep(1)
        print "Hello World"

thread = threading.Thread(target = message_loop)
thread.start()

while True:
    input = raw_input("Prompt> ")
    print "You typed", input

Questo è un esempio di come potrebbe apparire quando lo eseguo:

Prompt> Hello World
Hello World
Hello World
Hello World
test
You typed test
Prompt> Hello World
Hello World
Hello World
hellHello World
o
You typed hello
Prompt> Hello World
Hello World
Hello World
Hello World

Quello che voglio è che il prompt si muova insieme all'uscita dal thread. Così:

Hello World
Hello World
Prompt> test
You typed test
Hello World
Hello World
Hello World
Hello World
Hello World
Prompt> hello
You typed hello
Hello World
Hello World
Hello World
Hello World
Prompt> 

Qualche idea su come raggiungere questo obiettivo senza ricorrere a brutti hack? :)

È stato utile?

Soluzione

Di recente ho riscontrato questo problema e vorrei lasciare questa soluzione qui per riferimento futuro. Queste soluzioni cancellano il testo in sospeso raw_input (readline) dal terminale, stampano il nuovo testo, quindi ristampano al terminale ciò che era nel buffer raw_input.

Questo primo programma è piuttosto semplice, ma funziona correttamente solo quando c'è solo 1 riga di testo in attesa di raw_input:

#!/usr/bin/python

import time,readline,thread,sys

def noisy_thread():
    while True:
        time.sleep(3)
        sys.stdout.write('\r'+' '*(len(readline.get_line_buffer())+2)+'\r')
        print 'Interrupting text!'
        sys.stdout.write('> ' + readline.get_line_buffer())
        sys.stdout.flush()

thread.start_new_thread(noisy_thread, ())
while True:
    s = raw_input('> ')

Produzione:

$ ./threads_input.py
Interrupting text!
Interrupting text!
Interrupting text!
> WELL, PRINCE, Genoa and Lucca are now no more than private estates of the Bo
Interrupting text!
> WELL, PRINCE, Genoa and Lucca are now no more than private estates of the Bo
naparte family. No, I warn you, that if you do not tell me we are at war,

Il secondo gestisce correttamente 2 o più linee tamponate, ma ha più dipendenze dei moduli (standard) e richiede un po 'di hackery terminale:

#!/usr/bin/python

import time,readline,thread
import sys,struct,fcntl,termios

def blank_current_readline():
    # Next line said to be reasonably portable for various Unixes
    (rows,cols) = struct.unpack('hh', fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ,'1234'))

    text_len = len(readline.get_line_buffer())+2

    # ANSI escape sequences (All VT100 except ESC[0G)
    sys.stdout.write('\x1b[2K')                         # Clear current line
    sys.stdout.write('\x1b[1A\x1b[2K'*(text_len/cols))  # Move cursor up and clear line
    sys.stdout.write('\x1b[0G')                         # Move to start of line


def noisy_thread():
    while True:
        time.sleep(3)
        blank_current_readline()
        print 'Interrupting text!'
        sys.stdout.write('> ' + readline.get_line_buffer())
        sys.stdout.flush()          # Needed or text doesn't show until a key is pressed


if __name__ == '__main__':
    thread.start_new_thread(noisy_thread, ())
    while True:
        s = raw_input('> ')

Produzione. Le righe di lettura precedenti sono state cancellate correttamente:

$ ./threads_input2.py
Interrupting text!
Interrupting text!
Interrupting text!
Interrupting text!
> WELL, PRINCE, Genoa and Lucca are now no more than private estates of the Bo
naparte family. No, I warn you, that if you do not tell me we are at war,

Fonti utili:

Come ottenere la larghezza della finestra della console Linux in Python

Apt Like Column Output - Python Library(Questo esempio di codice mostra come ottenere la larghezza del terminale per UNIX o Windows)

http://en.wikipedia.org/wiki/ansi_escape_code

Altri suggerimenti

Penso che tu abbia bisogno di qualcosa che ti consenta di stampare dinamicamente/eliminare/sovrascrivere il testo dalla finestra del terminale, ad esempio come l'Unix watch o top I comandi funzionano.

Penso che nel tuo caso stampare "prompt>" ma poi quando ottieni un "ciao mondo" sovrascrivi "prompt>" con "hello world", quindi stampa "prompt>" sulla riga qui sotto. Non credo che tu possa farlo con una stampa di uscita regolare al terminale.

Potresti essere in grado di fare quello che vuoi usare Python maledizioni biblioteca. Non l'ho mai usato, quindi non posso dirti come risolvere il tuo problema (o se il modulo sarà anche in grado di risolvere il tuo problema), ma penso che valga la pena dare un'occhiata. Una ricerca di "Tutorial di Python Curses" ha fornito a Documento tutorial PDF che sembra utile.

È necessario aggiornare STDOUT da un singolo thread, non da più thread ... altrimenti non hai alcun controllo su I/O interlacciati.

Ti consigliamo di creare un singolo thread per la scrittura di output.

È possibile utilizzare una coda nel thread e avere tutti gli altri thread scrivere le loro informazioni di registrazione di output.

Non penso che sia possibile. Come dovrebbe comportarsi comunque? Nulla viene visualizzato fino a quando l'utente preme Inserisci? Se è così, l'output arriverà solo quando l'utente emette un comando (o qualunque cosa il tuo sistema si aspetti) e ciò non sembra desiderabile.

Massure i thread dovrebbero essere usciti su un altro file.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top