从 raw_input() 读取输入,而不会让 Python 中的其他线程覆盖提示符

发布于 2024-08-18 00:15:33 字数 1017 浏览 5 评论 0原文

我试图让用户使用 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> 

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

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).

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

#!/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? :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

赴月观长安 2024-08-25 00:15:33

我最近遇到了这个问题,想把这个解决方案留在这里以供将来参考。
这些解决方案从终端清除待处理的 raw_input(readline)文本,打印新文本,然后将 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,

有用的来源:

How to get Linux console window width在Python中

apt像列输出 - python库
(此代码示例展示了如何获取 Unix 或 Windows 的终端宽度)

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

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.

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

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

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

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

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

千秋岁 2024-08-25 00:15:33

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

我认为在你的情况下你会打印“Prompt>”但是当你收到“Hello World”时,你会覆盖“Prompt>”与“Hello World”,然后打印“Prompt>”在下面一行。我认为你不能通过定期输出打印到终端来做到这一点。

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

I think you need something that lets you dynamically print/delete/overwrite text from the terminal window e.g. how the UNIX watch or top commands work.

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.

You might be able to do what you want using Python's curses library. 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 document which seems helpful.

终遇你 2024-08-25 00:15:33

你需要从单个线程更新 stdout,而不是从多个线程......否则你无法控制交错 I/O。

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

您可以在线程中使用队列,并让所有其他线程将其输出日志记录信息写入其中。然后从此队列中读取数据,并在适当的时间连同提示消息一起写入标准输出。

you need to update stdout from a single thread, not from multiple threads... or else you have no control over interleaved 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.

一片旧的回忆 2024-08-25 00:15:33

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

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

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.

Methinks your threads should output to another file.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文