来自 Python 的可靠套印输出
Reliable overprinting of output from Python
我有一些输出我想重写并有一个简单的 Python 函数可以为我完成这个很好(在 OS X 的终端中)但我不确定我是否可以依赖一般情况下:
import sys
import time
def print_over(s):
print(s, end='\r')
print("3[F" * (s.count('\n')+1))
sys.stdout.flush()
time.sleep(0.2)
我知道在某些情况下这当然行不通,但想知道
- 我期望它的工作范围有多广(例如,是否只是 OS X 的一些怪癖允许这样做),
- 如何描述它将在哪些地方起作用,哪些地方不起作用(例如,是否有一些 POSIX 或其他保证它会起作用的标准),以及
- 我能否从我的代码中检测到它是否有效。
忽略关于 Microsoft Console API 可移植性的讨论(OP 可以探索),只关注 "ANSI-escapes" 工作的地方:
这一行特别有趣,因为它是唯一使用的转义序列:
print("3[F" * (s.count('\n')+1))
对应的是ECMA-48 control CPL
, e.g., as in XTerm Control Sequences:
CSI Ps F Cursor Preceding Line Ps Times (default = 1) (CPL).
OP 问 "how widely I can expect it to work"。这取决于。它是在 xterm 中实现的(并且 Terminal.app 实现了其中的相当一部分),但不是 VT100 或 VT220 的一部分(请参阅 http://vt100.net). It was added to xterm in 1996 上的文档。因此认为它仅限于模仿 xterm 的程序。
POSIX has nothing to say on the topic. X/Open Curses (not part of POSIX) is close—but CPL does not correspond to any of the terminfo capabilities. ECMA-48 is relevant, but there is no guarantee that any feature listed in ECMA-48 is implemented in any given terminal. Rather, it enumerates possibilities and prescribes their syntax. There is no guarantee likewise that any given feature of xterm is found in another program (see for example Comparing versions, by counting controls).
原则上,可以尝试使用光标位置报告(CPR
控制序列)来查看光标在使用[后的位置=40=]CPL
,但即使这样在某些 "xterm emulators".
上也不可靠
顺便说一句,CPL
控制序列接受一个重复参数,因此可以重写打印语句以使用它(而不是重复控制序列).
如果你想更便携一点,使用 CUU
(光标向上)控制适用于 VT100,并且(如 CPL
) 可以用重复计数参数化。即"3[A"
:
CSI Ps A Cursor Up Ps Times (default = 1) (CUU).
为什么不使用 curses? It works natively in Linux, OSX and now there is also a Windows implementation (as reported 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)
基本上,原则是始终使用 curses
和 tput commands 以避免任何显式转义字符。
请注意,您可能需要刷新标准输出或仅使用 python -u
启动脚本。
我有一些输出我想重写并有一个简单的 Python 函数可以为我完成这个很好(在 OS X 的终端中)但我不确定我是否可以依赖一般情况下:
import sys
import time
def print_over(s):
print(s, end='\r')
print("3[F" * (s.count('\n')+1))
sys.stdout.flush()
time.sleep(0.2)
我知道在某些情况下这当然行不通,但想知道
- 我期望它的工作范围有多广(例如,是否只是 OS X 的一些怪癖允许这样做),
- 如何描述它将在哪些地方起作用,哪些地方不起作用(例如,是否有一些 POSIX 或其他保证它会起作用的标准),以及
- 我能否从我的代码中检测到它是否有效。
忽略关于 Microsoft Console API 可移植性的讨论(OP 可以探索),只关注 "ANSI-escapes" 工作的地方:
这一行特别有趣,因为它是唯一使用的转义序列:
print("3[F" * (s.count('\n')+1))
对应的是ECMA-48 control CPL
, e.g., as in XTerm Control Sequences:
CSI Ps F Cursor Preceding Line Ps Times (default = 1) (CPL).
OP 问 "how widely I can expect it to work"。这取决于。它是在 xterm 中实现的(并且 Terminal.app 实现了其中的相当一部分),但不是 VT100 或 VT220 的一部分(请参阅 http://vt100.net). It was added to xterm in 1996 上的文档。因此认为它仅限于模仿 xterm 的程序。
POSIX has nothing to say on the topic. X/Open Curses (not part of POSIX) is close—but CPL does not correspond to any of the terminfo capabilities. ECMA-48 is relevant, but there is no guarantee that any feature listed in ECMA-48 is implemented in any given terminal. Rather, it enumerates possibilities and prescribes their syntax. There is no guarantee likewise that any given feature of xterm is found in another program (see for example Comparing versions, by counting controls).
原则上,可以尝试使用光标位置报告(
CPR
控制序列)来查看光标在使用[后的位置=40=]CPL
,但即使这样在某些 "xterm emulators". 上也不可靠
顺便说一句,CPL
控制序列接受一个重复参数,因此可以重写打印语句以使用它(而不是重复控制序列).
如果你想更便携一点,使用 CUU
(光标向上)控制适用于 VT100,并且(如 CPL
) 可以用重复计数参数化。即"3[A"
:
CSI Ps A Cursor Up Ps Times (default = 1) (CUU).
为什么不使用 curses? It works natively in Linux, OSX and now there is also a Windows implementation (as reported 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)
基本上,原则是始终使用 curses
和 tput commands 以避免任何显式转义字符。
请注意,您可能需要刷新标准输出或仅使用 python -u
启动脚本。