Tho*_*mas 5 python terminal ansi-escape terminal-emulator
在终端仿真器中似乎有一些行与行的概念,我想知道更多.
下面的Python脚本显示三行'a'并等待,然后有三行'b'.
import sys, struct, fcntl, termios
write = sys.stdout.write
def clear_screen(): write('\x1b[2J')
def move_cursor(row, col): write('\x1b['+str(row)+';'+str(col)+'H')
def current_width(): #taken from blessings so this example doesn't have dependencies
return struct.unpack('hhhh', fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, '\000' * 8))[1]
clear_screen()
for c in 'ab':
#clear_screen between loops changes this behavior
width = current_width()
move_cursor(5, 1)
write(c*width+'\n')
move_cursor(6, 1)
write(c*width+'\n')
move_cursor(7, 1)
write(c*width+'\n')
sys.stdout.flush()
try: input() # pause and wait for ENTER in python 2 and 3
except: pass
Run Code Online (Sandbox Code Playgroud)

如果在此休息期间将终端窗口宽度缩小一个字符,您会看到

这似乎很合理 - 每条线都是单独包裹的.当我们再次点击进入打印bs时,

一切都按预期工作.我已经使用了绝对游标定位,并写入了我之前写过的相同行 - 这当然不会覆盖所有的a,因为其中很多都在其他行上.
但是,当我们将窗口缩小一个字符时,包装的工作方式不同:

为什么第二排和第三排b包装在一起,为什么最后一行a与b的第一行合并?为什么在上面的顶部可见行中有一个暗示 - 我们看到两个a是因为它们仍然链接了两行 - 当然如果我们再次移动窗口,那一行将继续以相同的方式包裹.即使对于我们替换了整行的行也似乎正在发生这种情况.
事实证明,以前包装过的行现在链接到相应的父行; 一旦我们大量扩展终端,它们就属于同一条逻辑线更为明显:

实际上,我的问题是如何防止或预测这些行被组合成行.清除整个屏幕消除了行为,但是如果可能的话,仅对需要它的单个行执行此操作会很好,因此我可以按线保持缓存,这显着加快了我的应用程序.清除到行的末尾将该行与其下面的行取消链接,但清除到行的开头不会取消该行与其上方的行的链接.
我很好奇 - 这些线条是什么?我在哪里可以阅读它们?我可以找出哪一行是同一行的一部分吗?
我已经通过terminal.app和iterm观察到了这种行为,并且没有使用tmux.我想,即使没有规范,源代码潜入其中任何一个都会产生答案 - 但我想在某个地方有一个规格!
背景:我想创建一个终端用户界面,可以预测如果用户减小窗口宽度,终端包装的方式.我知道像全屏模式(tput smcup或python -c 'print "\x1b[?1049h"',这是ncurses使用的)这些可以用来防止换行,但不想在这里使用它.
编辑:更清楚地表明我已经理解了脚本的覆盖行为,并想要解释包装行为.
我测试了你的代码并注意到它只发生在你调整窗口大小时.当窗口被单独放置时,它会写出a,并且在按下Enter时会用b覆盖它们(我认为这是预期的行为).
似乎正在发生的事情是,当您在中途调整窗口大小时,行索引会发生变化,因此在下一次迭代中,当调用move_cursor()时,您无法信任相同的坐标.
有趣的是,当您调整窗口大小时,自动换行会在光标向上之前推送文本.我假设这是终端仿真器代码的一部分(因为我们几乎总是希望保持对光标的关注,如果光标位于屏幕的底部,如果自动翻译将其向下推,则调整大小可能会超出窗口的高度).
您会注意到,在按Enter键调整大小后,只能看到两行a(而不是全部3行).这是似乎正在发生的事情:
首先,我们从初始输出开始.(为清晰起见添加了行号)
1
2
3
4
5 aaaaaaaaaaaaaaa\n
6 aaaaaaaaaaaaaaa\n
7 aaaaaaaaaaaaaaa\n
8
Run Code Online (Sandbox Code Playgroud)
请注意,每条线的末尾都有一个换行符号(这就是为什么你的光标出现在最后一行之下,尽管你没有再次移动光标)
当您将窗口缩小一个字符时,会发生以下情况:
1
2 aaaaaaaaaaaaaa
3 a\n
4 aaaaaaaaaaaaaa
5 a\n
6 aaaaaaaaaaaaaa
7 a\n
8
Run Code Online (Sandbox Code Playgroud)
你会注意到"向上推文字"的意思
现在,当您按Enter键并重复循环时,光标将被发送到第5行第1行(由代码指定)并直接放在第二行的最后一行.当它开始写b时,它用b和后续行覆盖第二行的最后一行.
1
2 aaaaaaaaaaaaaa
3 a\n
4 aaaaaaaaaaaaaa
5 bbbbbbbbbbbbbb\n
6 bbbbbbbbbbbbbb
7 bbbbbbbbbbbbbb\n
8
Run Code Online (Sandbox Code Playgroud)
重要的是,这也会覆盖第二行末尾的换行符.这意味着现在没有新行划分a的第二行和b的第一行,所以当你展开窗口时:它们显示为一行.
1
2
3
4
5 aaaaaaaaaaaaaaa\n
6 aaaaaaaaaaaaaabbbbbbbbbbbbbb\n
7 bbbbbbbbbbbbbbbbbbbbbbbbbbbb\n
8
Run Code Online (Sandbox Code Playgroud)
我不完全确定为什么第二行的b也会被放在一起,但它似乎可能与第一个覆盖的a的行现在缺少它自己的新行终止这一事实有关.但是,这只是猜测.
如果你试图通过另一个字符缩小窗口,你得到两个换行符的原因是因为现在你缩小了同一行文本的两半,这意味着一个推动另一个,导致两个字符代替一个在最后.
例如:在我已经显示的这些测试窗口中,宽度从15个字符开始,然后我将其缩小到14并打印出b.还有一行是15个字符长,现在是14个字符串和14个字符串,以14个字符包裹着.同样(由于某种原因)对于b的最后两行是正确的(它们是一行28个字符,包裹在14处).所以当你将窗口缩小一个字符(低至13)时:15 a的第一行现在有两个尾随字符(15 - 13 = 2); 下一行28个字符现在必须适合13个字符宽的窗口(28/13 = 2 R2),同样适用于最后一个b.
0 aaaaaaaaaaaaa
1 aa\n
2 aaaaaaaaaaaaa
3 abbbbbbbbbbbb
4 bb\n
5 bbbbbbbbbbbbb
6 bbbbbbbbbbbbb
7 bb\n
8
Run Code Online (Sandbox Code Playgroud)
当你试图在另一个能够根据需要重新定位文本的程序中运行你的程序时,你会遇到这种困难.如果调整大小,您的索引将变得不可靠.您的终端模拟器正在尝试为您处理重新排列,并在回滚之前在提示(在第8行修复)之前上下推动文本,以确保您始终可以看到您的活动提示.
行和列是终端/终端仿真器定义的内容,由它来决定它们的位置.当给出适当的控制序列时,终端相应地解释它们以便正确显示.
请注意,某些终端的行为有所不同,并且在模拟终端中通常会有一个设置来更改它所模拟的终端类型,这也可能会影响某些转义序列的响应方式.这就是为什么UNIX环境通常有一个设置或环境变量($ TERM),它告诉它与哪个终端通信,以便它知道要发送什么控制序列.
大多数终端使用标准的ANSI兼容控制序列,或基于DEC VT系列硬件终端的系统.
在Preferences-> Settings-> Advanced下的Terminal.app首选项中,您可以在"将终端声明为:"旁边的下拉菜单中实际查看(或更改)窗口模拟的终端类型.
您可以通过存储上一个已知宽度并检查是否存在更改来缓解此问题.在这种情况下,您可以更改光标逻辑以补偿更改.
或者,您可以考虑使用为相对光标移动而设计的转义序列(而不是绝对值),以避免在调整大小后意外覆盖前一行.还可以仅使用转义序列来保存和恢复特定的光标位置.
Esc[<value>A Up
Esc[<value>B Down
Esc[<value>C Forward
Esc[<value>D Backward
Esc[s Save Current Position
Esc[u Restore Last Saved Position
Esc[K Erase from cursor position to end of line
Run Code Online (Sandbox Code Playgroud)
但是,您并不能确保所有终端仿真器都以相同的方式处理窗口调整大小(这不是任何终端标准的一部分,AFAIK),或者它将来不会改变.如果您希望创建一个真正的终端模拟器,我建议首先设置您的GUI窗口,以便您可以控制所有调整大小的逻辑.
但是,如果您希望在终端模拟器窗口中运行,并为您正在编写的给定命令行实用程序处理缓解窗口大小调整.我建议查看python的curses库.这是我所知道的所有窗口大小调整程序所使用的功能(vim,yum,irssi),并且可以处理这种更改.虽然我个人没有任何使用它的经验.
它可以通过curses模块用于python .
(并且,如果您计划重新分发您的程序,请考虑使用Python3编写.请为孩子们做:D)
这些链接可能会有所帮助:
我希望有所帮助!
| 归档时间: |
|
| 查看次数: |
1428 次 |
| 最近记录: |