从 raw_input() 读取输入而没有被 Python 中的其他线程覆盖的提示

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2082387/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 23:43:31  来源:igfitidea点击:

Reading input from raw_input() without having the prompt overwritten by other threads in Python

pythonmultithreadingraw-input

提问by Jim

I'm trying to let the user input commands at a console using raw_input(), this works fine. The problem is I have background threads that occasionally output log-information to the screen and when they do they mess up the input prompt (since the output go wherever the cursor happens to be at the moment).

我试图让用户在控制台上使用 raw_input() 输入命令,这很好用。问题是我有后台线程,它们偶尔会向屏幕输出日志信息,当它们这样做时,它们会弄乱输入提示(因为此时输出会到达光标所在的任何位置)。

This is a small Python program that illustrate what i mean.

这是一个小型 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

This is an example of what it could look like when I run it:

这是我运行它时它的外观示例:

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

What I want is for the prompt to move along with the output from the thread. Like so:

我想要的是提示随着线程的输出移动。像这样:

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> 

Any ideas on how to achieve this without resorting to ugly hacks? :)

关于如何在不诉诸丑陋黑客的情况下实现这一目标的任何想法?:)

回答by jwmullally

I recently encountered this problem, and would like to leave this solution here for future reference. These solutions clear the pending raw_input (readline) text from the terminal, print the new text, then reprint to the terminal what was in the raw_input buffer.

我最近遇到了这个问题,想把这个解决方案留在这里以备日后参考。这些解决方案从终端清除未决的 raw_input(readline)文本,打印新文本,然后将 raw_input 缓冲区中的内容重新打印到终端。

This first program is pretty simple, but only works correctly when there is only 1 line of text waiting for 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('> ')

Output:

输出:

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

The second correctly handles 2 or more buffered lines, but has more (standard) module dependencies and requires a wee bit of terminal hackery:

第二个正确处理 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('> ')

Output. Previous readline lines cleared properly:

输出。以前的 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,


Useful sources:

有用的来源:

How to get Linux console window width in Python

如何在 Python 中获取 Linux 控制台窗口宽度

apt like column output - python library(This code sample shows how to get terminal width for either Unix or Windows)

apt like column output - python library(此代码示例显示了如何获取 Unix 或 Windows 的终端宽度)

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

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

回答by Roman

I think you need something that lets you dynamically print/delete/overwrite text from the terminal window e.g. how the UNIX watchor topcommands work.

我认为您需要一些可以让您从终端窗口动态打印/删除/覆盖文本的东西,例如 UNIXwatchtop命令的工作方式。

I think in your case you would print "Prompt>" but then when you get a "Hello World" you overwrite "Prompt>" with "Hello World", and then print "Prompt>" on the line below. I don't think you can do that with regular output printing to the terminal.

我认为在你的情况下你会打印“Prompt>”但是当你得到一个“Hello World”时你用“Hello World”覆盖“Prompt>”,然后在下面的行上打印“Prompt>”。我认为您无法通过将常规输出打印到终端来做到这一点。

You might be able to do what you want using Python's curseslibrary. I have never used it so I can't tell you how to solve your problem (or if the module will even be able to solve your problem), but I think it is worth taking a look into. A search for "python curses tutorial" provided a PDF tutorial documentwhich seems helpful.

您也许可以使用 Python 的curses库做您想做的事。我从未使用过它,所以我无法告诉您如何解决您的问题(或者该模块是否能够解决您的问题),但我认为值得研究一下。搜索“python curses tutorial”提供了一个看起来很有帮助的PDF 教程文档

回答by Corey Goldberg

you need to update stdout from a single thread, not from multiple threads... or else you have no control over interleaved i/o.

您需要从单个线程而不是多个线程更新标准输出......否则您无法控制交错的 i/o。

you will want to create a single thread for output writing.

您将需要创建一个用于输出写入的线程。

you can use a Queue in the thread and have all other threads write their output logging info to it.. then read from this Queue and write to stdout at appropriate times along with your prompt message.

您可以在线程中使用 Queue 并让所有其他线程将其输出日志信息写入其中。

回答by zneak

I don't think it's possible. How should that behave anyways? Nothing shows up until the user presses Enter? If that's so, output would only come when the user issues a command (or whatever your system expects), and that doesn't sound desirable.

我不认为这是可能的。无论如何应该如何表现?在用户按下 Enter 之前什么都没有显示?如果是这样,输出只会在用户发出命令(或您的系统期望的任何内容)时出现,这听起来并不理想。

Methinks your threads should output to another file.

我认为您的线程应该输出到另一个文件。