我试图让用户使用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>
关于如何在不诉诸丑陋黑客的情况下实现这一目标的任何想法? :)
答案 0 :(得分:24)
我最近遇到了这个问题,并希望将此解决方案留在此处以供将来参考。 这些解决方案从终端清除待处理的raw_input(readline)文本,打印新文本,然后重新打印到raw_input缓冲区中的终端。
第一个程序非常简单,只有当只有一行文本等待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个或更多缓冲行,但具有更多(标准)模块依赖性并且需要一点点终端hackery:
#!/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('> ')
输出。之前的readline行已正确清除:
$ ./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,
有用的资料来源:
How to get Linux console window width in Python
apt like column output - python library (此代码示例显示如何获取Unix或Windows的终端宽度)
答案 1 :(得分:3)
我认为您需要能够从终端窗口动态打印/删除/覆盖文本的内容,例如UNIX watch
或top
命令的工作原理。
我认为在你的情况下你会打印“提示>”但是当你得到一个“Hello World”时,你会覆盖“提示>”用“Hello World”,然后打印“提示>”在下面的行。我不认为你可以通过常规输出打印到终端。
您可以使用Python的curses库来执行您想要的操作。我从来没有用它,所以我不能告诉你如何解决你的问题(或者模块是否能够解决你的问题),但我认为值得一看。搜索“python curses tutorial”提供了PDF tutorial document,这看起来很有帮助。
答案 2 :(得分:1)
您需要从单个线程更新stdout,而不是从多个线程更新...或者您无法控制交错的I / O.
您需要为输出写入创建单个线程。
您可以在线程中使用Queue并让所有其他线程将其输出日志信息写入其中..然后从此队列中读取并在适当的时间写入stdout以及您的提示消息。
答案 3 :(得分:0)
我不认为这是可能的。这怎么应该表现呢?在用户按Enter键之前没有显示任何内容?如果是这样的话,输出只会在用户发出命令(或系统预期的任何内容)时出现,这听起来并不合适。
将你的线程应该输出到另一个文件。