【问题标题】:Reliable overprinting of output from PythonPython 输出的可靠叠印
【发布时间】:2016-02-06 19:33:06
【问题描述】:

我有一些输出想要重写,并且有一个简单的 Python 函数可以很好地完成这个任务(在 OS X 上的终端中),但我不确定我是否可以依赖它:

import sys
import time

def print_over(s):
    print(s, end='\r')
    print("\033[F" * (s.count('\n')+1))
    sys.stdout.flush() 
    time.sleep(0.2)

我知道在某些情况下这当然行不通,但想知道

  • 我可以期望它在多大程度上工作(例如,是否只是 OS X 的一些怪癖允许这样做),
  • 如何描述它将在哪里起作用和在哪里不起作用(例如,是否有一些 POSIX 或其他标准可以保证它会起作用),以及
  • 我是否可以从我的代码中检测到它是否可以工作。

【问题讨论】:

  • 它不能在 Windows 上运行。
  • @MarkRansom:有什么东西可以让类似的东西在 Windows 上运行吗?
  • @ZN13:这也“向上滚动”吗?
  • @raxacoricofallapatorius 试试看import sys, time for progress in range(0,101): sys.stdout.write("Download progress: %d%% \r" % (progress) ) sys.stdout.flush() time.sleep(0.2) print ("") 编辑:评论格式很棒。

标签: python python-2.7 output ansi-escape


【解决方案1】:

忽略关于可移植到 Microsoft Console API(OP 可以探索)的讨论,只关注“ANSI-escapes”工作的地方:

这一行特别有趣,因为它是唯一使用的转义序列:

print("\033[F" * (s.count('\n')+1))

对应于ECMA-48控件CPL,如XTerm Control Sequences

CSI Ps F  Cursor Preceding Line Ps Times (default = 1) (CPL).
  • OP 询问“我可以期望它在多大范围内发挥作用”。这取决于。它在 xterm 中实现(并且 Terminal.app 实现了其中的相当一部分),但不是 VT100 或 VT220 的一部分(参见http://vt100.net 的文档)。它在1996 中添加到xterm。所以认为它仅限于模仿 xterm 的程序。

  • POSIX 对此话题无话可说。 X/Open Curses(不是 POSIX 的一部分)很接近——但 CPL 不对应于任何 terminfo 功能。 ECMA-48 是相关的,但不能保证在任何给定终端中实现 ECMA-48 中列出的任何功能。相反,它列举了可能性并规定了它们的语法。同样不能保证 xterm 的任何给定功能都可以在另一个程序中找到(例如参见 Comparing versions, by counting controls)。

  • 原则上,可以尝试使用光标位置报告(CPR 控制序列)来查看使用CPL后光标的位置,但即使在某些“xterm 模拟器”上也是不可靠的。

顺便说一句,CPL 控制序列接受重复参数,因此可以重写打印语句以使用它(而不是重复控制序列)。

如果你想要更便携一点,使用 CUU(光标向上)控件可以与 VT100 配合使用,并且(如 CPL)可以用重复计数参数化。那是"\033[A":

CSI Ps A  Cursor Up Ps Times (default = 1) (CUU).

【讨论】:

    【解决方案2】:

    为什么不使用curses?它在 Linux、OSX 中原生运行,现在还有一个 Windows implementation(据报道 here)。

    以下示例在大多数平台上都是可靠的:

    from curses import wrapper
    import time
    
    def print_over(scr, s):
        scr.clear()
        scr.addstr(5, 0, s)
        scr.refresh()
    
    def main(scr):
        for i in range(10, 110, 10):
            print_over(scr,'Progress: %d %%'%i)
            time.sleep(1)
    
    wrapper(main)
    

    编辑:

    这是另一个不清除整个屏幕的示例:

    from curses import tparm, tigetstr, setupterm
    import time
    
    def tput(cmd, *args):
        print (tparm(tigetstr(cmd), *args), end='') # Emulates Unix tput
    
    class window():
        def __init__(s, x, y, w, h):
            s.x, s.y, s.w, s.h = x, y, w, h # store window coordinates and size
    
        def __enter__(s):
            tput('sc') # saves current cursor position
            s.clear()  # clears the window
            return s
    
        def __exit__(s, exc_type, exc_val, exc_tb):
            tput('rc') # restores cursor position
    
        def clear(s, fill=' '):
            for i in range(s.h):
                tput('cup', s.y+i, s.x) # moves cursor to the leftmost column
                print (fill*s.w, end='')
    
        def print_over(s, msg, x, y):
            tput('cup', s.y+y, s.x+x)
            print (msg, end='')
    
    setupterm()
    with window(x=5, y=10, w=80, h=5) as w:
        for i in range(10, 110, 10):
            w.clear()
            w.print_over('Progress: %d %%'%i, 5, 2)
            time.sleep(1)
    

    这里还有一个只覆盖最后一行:

    from curses import tparm, tigetstr, setupterm
    import time
    
    def tput(cmd, *args):
        print (tparm(tigetstr(cmd), *args), end='') # Emulates Unix tput
    
    setupterm()
    for i in range(10, 110, 10):
        tput('el') # clear to end of line
        print (' Progress: %d %%'%i, end='\r')
        time.sleep(1)
    

    基本上,原则是始终使用cursestput commands 以避免任何显式转义字符。

    请注意,您可能需要刷新标准输出或仅使用 python -u 启动脚本。

    【讨论】:

    • 如果他使用诅咒,它会使用整个屏幕。有些人希望命令行应用程序只使用几行代码。
    • @ThomasDickey:正确,这就是我要找的。​​span>
    猜你喜欢
    • 1970-01-01
    • 2021-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多