Чтение ввода из raw_input () без перезаписываемой подсказки в других потоках в Python
-
21-09-2019 - |
Вопрос
Я пытаюсь позволить пользователю команды ввода на консоли, используя 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)
Другие советы
Я думаю, что вам нужно что -то, что позволяет динамически печатать/удалить/перезаписать текст из окна терминала, например, как Unix watch
или же top
команды работы.
Я думаю, что в вашем случае вы печатаете «Приглашение>», но потом, когда вы получите подсказку «Hello World», вы перезаписываете »> с« Hello World », а затем Print Primple>» в строке ниже. Я не думаю, что вы можете сделать это с обычной выводом на терминал.
Вы можете сделать то, что хотите, используя Python проклятия библиотека. Я никогда не использовал его, поэтому я не могу сказать вам, как решить вашу проблему (или если модуль даже сможет решить вашу проблему), но я думаю, что это стоит взглянуть. Поиск «Учебник по проклятию Python» предоставил Учебный документ PDF что кажется полезным.
Вам необходимо обновить STDOUT из одного потока, а не из нескольких потоков ... или у вас нет управления по сравнению с чередованным вводом -выводом.
Вы захотите создать один поток для написания вывода.
Вы можете использовать очередь в потоке и попросить все остальные потоки записать в него информацию о выводе. Затем прочитайте из этой очереди и напишите в STDOUT в подходящее время вместе с вашим запросом.
Я не думаю, что это возможно. Как это должно вести себя в любом случае? Ничто не отображается, пока пользователь не нажимает ввод? Если это так, вывод будет прийти только тогда, когда пользователь выпустит команду (или что -то, что ожидает ваша система), и это не звучит желательно.
Methinks ваши потоки должны выводиться в другой файл.