python 文本换行时清除终端中的最后一行

bxjv4tth  于 2023-03-28  发布在  Python
关注(0)|答案(1)|浏览(105)

在我的python程序中,我想在一个很长的循环中打印进度。我想输出特定的信息,比如完成百分比等,但我不想所有这些输出占据整个屏幕。
理想情况下,我想打印一条进度线。

train 53/56...x6 │ loss:1.356 │ miou:0.276 │ rate=3.13 Hz, eta=0:00:01, total=0:00:17, wall=19:48 EST

然后,当打印下一行时,我想简单地覆盖这一行。
目前,我可以通过在打印消息之前简单地打印回车'\r'来做到这一点。这将光标返回到行的开头,然后覆盖该行。这正是我想要的。
问题是,当终端太小,整行都放不下时,该行会换行,回车将我带到换行的开头,而不是该行的绝对开头。
有没有一种方法可以让光标一直回到正确行的开头?

xyhw6mcr

xyhw6mcr1#

您可以使用ANSI escape sequences for cursor movement,最值得注意的是:

  • 定位光标:\033[<L>;<C>H\033[<L>;<C>f将光标放在L行和C列。
  • 将光标向上移动N行:\033[<N>A
  • 将光标向下移动N行:\033[<N>B
  • 将光标向前移动N列:\033[<N>C
  • 将光标向后移动N列:\033[<N>D
  • 保存光标位置:\033[s
  • 恢复光标位置:\033[u

光标位置保存/恢复看起来很适合你的情况,但不幸的是,这两个代码并不被许多终端模拟器所认可。
它们可以在xtermxfce4-terminal中工作(除了在终端/滚动输出的最后一行,如@ThomasDickey在评论中所指出的)。

echo -e "\033[s" {1..100} "\033[u" "Overwrite"

对于其他终端模拟器,您可以尝试使用\033[<N>A将光标上移到所需的行数,然后移动到0列。
如果你知道你的行的长度,你可以计算它在使用bash的情况下(以及如果使用bash的情况下)跨越了多少行(注意COLUMNS环境变量的用法):

line='...'
len=${#line}
rows=$((len / COLUMNS))

然后再往上走:

printf "\033[%dA" "$rows"

在Python中,你可以像这样使用它:

print("\033[s", "123"*100, "\033[u", "Overwrite", sep='')
print("\033[%dA" % 3, "Overwrite", sep='')

或者,用类似curses的东西抽象所有这些。

Python解决方案

基于 Move the cursor up N lines ANSI escape sequence(在大多数终端模拟器中应该可以工作),以及跨Python兼容的code for terminal width detection(在Python3中可以使用shutil.get_terminal_size),这里有一个概念验证演示,适用于滚动输出,适应行长度和更改终端宽度

#!/usr/bin/env python
from __future__ import print_function
import os
import time

cnt = 0
while True:
    with os.popen('stty size', 'r') as stty:
        rows, columns = stty.read().split()

    line = "Run: {}, Columns: {}, Filler: {}".format(cnt, columns, "***"*100)
    print(line)
    print("\033[{}A".format(len(line) // int(columns) + 1), end='')

    time.sleep(1)
    cnt += 1

相关问题