在控制台中重写多行

58 投票
7 回答
59414 浏览
提问于 2025-04-16 22:20

我知道可以用"\r"这个符号来不断地重写终端中显示的最后一行,但我在想有没有办法去修改之前打印的那些行。

我想做的是在一个基于文本的角色扮演游戏中重新打印多行内容,不过我的一个朋友也在问这个问题,他的应用程序有一行是用来显示进度条的,另一行则是描述下载情况。

比如,控制台会打印:

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

然后在程序运行时,适当地更新这两行内容。

7 个回答

8

总的来说,如果你想要控制屏幕显示,就需要使用操作系统底层的库,通常这些库包括:

  • 在Linux或OSX上使用curses(或者是由terminfo/termcap数据库管理的终端控制代码)
  • 在Windows上使用win32控制台API。

@codeape的回答已经给你提供了一些选项,如果你不介意只在一个操作系统上工作,或者愿意在Windows上安装第三方库。

不过,如果你想要一个跨平台的解决方案,并且可以简单地通过pip安装,你可以使用asciimatics。在开发这个包的过程中,我需要解决不同环境之间的差异,以提供一个在Linux、OSX和Windows上都能工作的统一API。

关于进度条,你可以使用BarChart对象,具体可以参考这个演示,使用的代码在这里

19

像这样:

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

在Unix系统上,可以使用curses模块。

在Windows系统上,有几个选择:

这是一个使用curses的简单例子(我对curses完全是小白):

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

撰写回答