\x1B[6n
是一个标准(据我所知)的 ANSI 转义码,用于查询用户光标的位置。如果发送到 stdout,则终端应该将 \x1B[{line};{column}R
写入 stdin。如果实现了这个结果,就可以假定支持 ANSI 转义码。主要问题在于检测此回复。
Windows
msvcrt.getch
可以用于从 stdin 中检索字符,而无需等待按下回车键。这与 msvcrt.kbhit
结合使用,后者检测是否正在等待读取 stdin,可产生本文“带注释的代码”部分中找到的代码。
Unix/with termios
警告:我(不明智地)没有测试过这个特定的 tty/select/termios 代码,但我知道类似的代码过去曾经有效。
getch
和 kbhit
可以使用 tty.setraw
和 select.select
进行复制。因此,我们可以定义这些函数如下:
from termios import TCSADRAIN, tcgetattr, tcsetattr
from select import select
from tty import setraw
from sys import stdin
def getch() -> bytes:
fd = stdin.fileno()
old_settings = tcgetattr(fd)
try:
setraw(fd)
return stdin.read(1).encode()
finally:
tcsetattr(fd, TCSADRAIN, old_settings)
def kbhit() -> bool:
return bool(select([stdin], [], [], 0)[0])
这可以与下面的代码一起使用。
带注释的代码
from sys import stdin, stdout
def isansitty() -> bool:
"""
The response to \x1B[6n should be \x1B[{line};{column}R according to
https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797. If this
doesn't work, then it is unlikely ANSI escape codes are supported.
"""
while kbhit():
getch()
stdout.write("\x1B[6n")
stdout.flush()
stdin.flush()
if kbhit():
if ord(getch()) == 27 and kbhit():
if getch() == b"[":
while kbhit():
getch()
return stdout.isatty()
return False
完整代码(无注释)
如果您需要,这里是原始代码。
from sys import stdin, stdout
from platform import system
if system() == "Windows":
from msvcrt import getch, kbhit
else:
from termios import TCSADRAIN, tcgetattr, tcsetattr
from select import select
from tty import setraw
from sys import stdin
def getch() -> bytes:
fd = stdin.fileno()
old_settings = tcgetattr(fd)
try:
setraw(fd)
return stdin.read(1).encode()
finally:
tcsetattr(fd, TCSADRAIN, old_settings)
def kbhit() -> bool:
return bool(select([stdin], [], [], 0)[0])
def isansitty() -> bool:
"""
Checks if stdout supports ANSI escape codes and is a tty.
"""
while kbhit():
getch()
stdout.write("\x1b[6n")
stdout.flush()
stdin.flush()
if kbhit():
if ord(getch()) == 27 and kbhit():
if getch() == b"[":
while kbhit():
getch()
return stdout.isatty()
return False
来源
没有特定的顺序: