Чтение ввода из raw_input () без перезаписываемой подсказки в других потоках в Python

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

Вопрос

Я пытаюсь позволить пользователю команды ввода на консоли, используя RAW_INPUT (), это работает нормально. Проблема в том, что у меня есть фоновые потоки, которые иногда выводят вход в систему на экране, и когда они это делают, они испортят подсказку ввода (поскольку вывод идет, куда угодно, курсор оказывается в данный момент).

Это небольшая программа Python, которая иллюстрирует то, что я имею в виду.

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

Это пример того, как он может выглядеть, когда я запускаю его:

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

Я хочу, чтобы подсказка перешла вместе с выходом из потока. Вот так:

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> 

Есть идеи о том, как достичь этого, не прибегая к уродливым хакам? :)

Это было полезно?

Решение

Я недавно столкнулся с этой проблемой и хотел бы оставить это решение для будущего. Эти решения очищают ожидающий текст RAW_INPUT (Line Line) из терминала, распечатайте новый текст, затем перепечатайте на терминал, который был в буфере RAW_INPUT.

Эта первая программа довольно проста, но работает правильно, только когда есть только 1 строка текста, ожидая 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('> ')

Выход:

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

Второй правильно обрабатывает 2 или более буферных линий, но имеет больше (стандартных) зависимостей модуля и требует немного хакерства терминала:

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

Выход. Предыдущие строки чтения правильно очищены:

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

Полезные источники:

Как получить ширину окна консоли Linux в Python

Apt как вывод столбца - библиотека Python(Этот образец кода показывает, как получить ширину терминала для UNIX или Windows)

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

Другие советы

Я думаю, что вам нужно что -то, что позволяет динамически печатать/удалить/перезаписать текст из окна терминала, например, как Unix watch или же top команды работы.

Я думаю, что в вашем случае вы печатаете «Приглашение>», но потом, когда вы получите подсказку «Hello World», вы перезаписываете »> с« Hello World », а затем Print Primple>» в строке ниже. Я не думаю, что вы можете сделать это с обычной выводом на терминал.

Вы можете сделать то, что хотите, используя Python проклятия библиотека. Я никогда не использовал его, поэтому я не могу сказать вам, как решить вашу проблему (или если модуль даже сможет решить вашу проблему), но я думаю, что это стоит взглянуть. Поиск «Учебник по проклятию Python» предоставил Учебный документ PDF что кажется полезным.

Вам необходимо обновить STDOUT из одного потока, а не из нескольких потоков ... или у вас нет управления по сравнению с чередованным вводом -выводом.

Вы захотите создать один поток для написания вывода.

Вы можете использовать очередь в потоке и попросить все остальные потоки записать в него информацию о выводе. Затем прочитайте из этой очереди и напишите в STDOUT в подходящее время вместе с вашим запросом.

Я не думаю, что это возможно. Как это должно вести себя в любом случае? Ничто не отображается, пока пользователь не нажимает ввод? Если это так, вывод будет прийти только тогда, когда пользователь выпустит команду (или что -то, что ожидает ваша система), и это не звучит желательно.

Methinks ваши потоки должны выводиться в другой файл.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top