【问题标题】:Rewrite multiple lines in the console在控制台中重写多行
【发布时间】:2011-10-14 00:02:30
【问题描述】:

我知道可以用“\r”一致地重写终端中显示的最后一行,但我无法确定是否有办法返回并编辑控制台中打印的前一行。

我想做的是为基于文本的 RPG 重新打印多行,但是,一位朋友也想知道这一点,因为应用程序有一行专用于进度条,另一行描述下载。

即控制台将打印:

Moving file: NameOfFile.txt  
Total Progress: [########              ] 40%

然后在程序运行时适当地更新(两行)。

【问题讨论】:

  • 这应该在什么平台上?
  • Unix,特别是 Fuduntu,但我希望它可以在大多数平台上工作......不过不需要。
  • stackoverflow.com/questions/3002085/… 那里的答案很好,帮我加载!

标签: python printing console


【解决方案1】:

在 Unix 上,使用 curses 模块。

在 Windows 上,有几个选项:

使用 curses 的简单示例(我是个全诅咒 n00b):

import curses
import time

def report_progress(filename, progress):
    """progress: 0-10"""
    stdscr.addstr(0, 0, "Moving file: {0}".format(filename))
    stdscr.addstr(1, 0, "Total progress: [{1:10}] {0}%".format(progress * 10, "#" * progress))
    stdscr.refresh()

if __name__ == "__main__":
    stdscr = curses.initscr()
    curses.noecho()
    curses.cbreak()

    try:
        for i in range(10):
            report_progress("file_{0}.txt".format(i), i+1)
            time.sleep(0.5)
    finally:
        curses.echo()
        curses.nocbreak()
        curses.endwin()

【讨论】:

  • 谢谢,我在答案中添加了链接。 API 和 Unix 诅咒一样吗?
  • 其实有一个模块可以做你想做的事:[progressbar][code.google.com/p/python-progressbar/]
  • 好吧,但是如何不在屏幕顶部显示状态行,而是在底部(在上一个输出之后的下一行)。我想要一个管道中的多个pv --name 的效果
【解决方案2】:

这里有一个适用于 Python 2/3 的 Python 模块,只需几行代码就可以轻松解决这种情况;D

reprint - A simple module for Python 2/3 to print and refresh multi line output contents in terminal

您可以简单地将 output 实例视为普通的 dictlist(取决于您使用的模式)。当您在output 实例中修改该内容时,终端中的输出将自动刷新:D

根据您的需要,代码如下:

from reprint import output
import time

if __name__ == "__main__":
    with output(output_type='dict') as output_lines:
        for i in range(10):
            output_lines['Moving file'] = "File_{}".format(i)
            for progress in range(100):
                output_lines['Total Progress'] = "[{done}{padding}] {percent}%".format(
                    done = "#" * int(progress/10),
                    padding = " " * (10 - int(progress/10)),
                    percent = progress
                    )
                time.sleep(0.05)

【讨论】:

  • 当我尝试output_lines['one'] = 'abcd' 时,像你一样设置它后,我得到一个零除错误。我相信这里有问题。你对此有什么见解吗?
  • @cat40 我认为这可能使get_terminal_size() 返回值(0,0)。我可以要求更多细节吗?您可以在 Github 问题上发布:D
【解决方案3】:

最终,如果你想操作屏幕,你需要使用底层的操作系统库,通常是:

  • Linux 或 OSX 上的诅咒(或由 terminfo/termcap 数据库跟踪的底层终端控制代码)
  • Windows 上的 win32 控制台 API。

如果您不介意坚持使用一个操作系统或乐于在 Windows 上安装第三方库,@codeape 的回答已经为您提供了众多选择中的一些。

但是,如果您想要一个可以简单地 pip 安装的跨平台解决方案,您可以使用asciimatics。作为开发此软件包的一部分,我必须解决环境之间的差异,以提供适用于 Linux、OSX 和 Windows 的单一 API。

对于进度条,您可以使用 BarChart 对象,如 this demo 中所示,使用 this code

【讨论】:

  • 我有点惊讶,像跨平台控制台操作这样有用的东西没有随 python 一起提供。 (特别是考虑到 Tkinter 的存在)
  • @PythonNut - 我不知道完整的历史,但据我所知,没有人准备为 Windows 编写支持兼容的 API,因此 recommendation 已经使用了各种胶水代码选项提到。
【解决方案4】:

像这样:

#!/usr/bin/env python

import sys
import time
from collections import deque

queue = deque([], 3)
for t in range(20):
    time.sleep(0.5)
    s = "update %d" % t
    for _ in range(len(queue)):
        sys.stdout.write("\x1b[1A\x1b[2K") # move up cursor and delete whole line
    queue.append(s)
    for i in range(len(queue)):
        sys.stdout.write(queue[i] + "\n") # reprint the lines

我在 Jiri 项目中发现了这一点,用 Go 编写。

更好:完成后擦除所有行:

#!/usr/bin/env python

import sys
import time
from collections import deque

queue = deque([], 3)
t = 0
while True:
    time.sleep(0.5)
    if t <= 20:
        s = "update %d" % t
        t += 1
    else:
        s = None
    for _ in range(len(queue)):
        sys.stdout.write("\x1b[1A\x1b[2K") # move up cursor and delete whole line
    if s != None:
        queue.append(s)
    else:
        queue.popleft()
    if len(queue) == 0:
        break
    for i in range(len(queue)):
        sys.stdout.write(queue[i] + "\n") # reprint the lines

【讨论】:

    【解决方案5】:

    回车可用于转到行首,ANSI 代码ESC A ("\033[A") 可以带您上一行。这适用于 Linux。它可以在 Windows 上使用colorama 包启用 ANSI 代码:

    import time
    import sys
    import colorama
    
    colorama.init()
    
    print("Line 1")
    time.sleep(1)
    print("Line 2")
    time.sleep(1)
    print("Line 3 (no eol)", end="")
    sys.stdout.flush()
    time.sleep(1)
    print("\rLine 3 the sequel")
    time.sleep(1)
    print("\033[ALine 3 the second sequel")
    time.sleep(1)
    print("\033[A\033[A\033[ALine 1 the sequel")
    time.sleep(1)
    print()  # skip two lines so that lines 2 and 3 don't get overwritten by the next console prompt
    print()
    

    输出:

    > python3 multiline.py
    Line 1 the sequel
    Line 2
    Line 3 the second sequel
    >
    

    在底层,colorama 可能使用SetConsoleMode 启用Console Virtual Terminal Sequences

    (也在这里发布:https://stackoverflow.com/a/64360937/461834

    【讨论】:

      【解决方案6】:

      我用“magic_char”找到了简单的解决方案。

      magic_char = '\033[F'
      multi_line = 'First\nSecond\nThird'
      ret_depth = magic_char * multi_line.count('\n')
      print('{}{}'.format(ret_depth, multi_line), end='', flush = True)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-06-10
        • 1970-01-01
        • 1970-01-01
        • 2014-01-29
        • 2023-03-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多