La lecture de l'entrée de Raw_input () sans avoir l'invite écrasé par d'autres threads de Python

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

Question

J'essaie de laisser les commandes d'entrée de l'utilisateur dans une console à l'aide de RAW_Input (), cela fonctionne bien. Le problème est que j'ai des threads d'arrière-plan qui sortent parfois la connexion de connexion à l'écran et lorsqu'ils le font, ils gâchent l'invite d'entrée (car la sortie va partout où le curseur se trouve en ce moment).

Ceci est un petit programme Python qui illustre ce que je veux dire.

#!/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

Ceci est un exemple de ce à quoi il pourrait ressembler lorsque je l'exécute:

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

Ce que je veux, c'est que l'invite se déplace avec la sortie du thread. Ainsi:

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> 

Des idées sur la façon d'y parvenir sans recourir à des hacks laids? :)

Était-ce utile?

La solution

J'ai récemment rencontré ce problème et j'aimerais laisser cette solution ici pour référence future. Ces solutions effacent le texte RAW_input (Readline) en attente du terminal, imprimez le nouveau texte, puis réimprimez au terminal ce qui était dans le tampon Raw_input.

Ce premier programme est assez simple, mais ne fonctionne correctement que lorsqu'il n'y a qu'une seule ligne de texte en attente de 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('> ')

Production:

$ ./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,

Le second gère correctement 2 lignes tamponnées ou plus, mais a plus de dépendances de modules (standard) et nécessite un petit peu de piratage terminal:

#!/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('> ')

Production. Les lignes de lecture précédentes ont été correctement effacées:

$ ./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,

Sources utiles:

Comment obtenir la largeur de la fenêtre de la console Linux en Python

APT comme la sortie de la colonne - Python Library(Cet exemple de code montre comment obtenir la largeur du terminal pour Unix ou Windows)

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

Autres conseils

Je pense que vous avez besoin de quelque chose qui vous permet d'imprimer / supprimer / écraser / écraser dynamiquement la fenêtre du terminal, par exemple comment l'Unix watch ou top Les commandes fonctionnent.

Je pense que dans votre cas, vous imprimeriez "Invite>", mais lorsque vous obtenez une invite "Hello World", vous écrasez "l'invite>" avec "Hello World", puis imprime "Invite>" sur la ligne ci-dessous. Je ne pense pas que vous puissiez le faire avec l'impression de sortie régulière au terminal.

Vous pourrez peut-être faire ce que vous voulez en utilisant Python malédictions bibliothèque. Je ne l'ai jamais utilisé, donc je ne peux pas vous dire comment résoudre votre problème (ou si le module sera même en mesure de résoudre votre problème), mais je pense que cela vaut la peine de jeter un coup d'œil. Une recherche du "tutoriel Python Mindiques" a fourni un Document de tutoriel PDF ce qui semble utile.

Vous devez mettre à jour STDOUT à partir d'un seul thread, pas à partir de plusieurs threads ... sinon vous n'avez aucun contrôle sur les E / S entrelacées.

Vous voudrez créer un seul thread pour l'écriture de sortie.

Vous pouvez utiliser une file d'attente dans le thread et demander à tous les autres threads d'écrire leurs informations de journalisation de sortie. puis lisez cette file d'attente et écrivez à STDOUT aux heures appropriées avec votre message rapide.

Je ne pense pas que ce soit possible. Comment cela devrait-il se comporter de toute façon? Rien n'apparaît jusqu'à ce que l'utilisateur appuie sur Entrée? S'il en est ainsi, la sortie ne viendrait que lorsque l'utilisateur émet une commande (ou ce que votre système attend), et cela ne semble pas souhaitable.

Je pense que vos threads doivent sortir vers un autre fichier.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top