'problem with escape sequence on linux for getch python
I have a pretty big problem, I'm making an _input function in python, and for that I need a getch function, so I started coding this, but I have a big problem: the escape sequences on linux, their size is variable! So I would like to know how to detect these sequences properly (and quickly).
import os
import sys
if os.name == "nt":
import msvcrt
else:
import tty
import termios
_special_key_tn = {
"H" : "A" , #top
"P" : "B" , #down
"M" : "C" , #right
"K" : "D" , #left
"S" : "?" , #suppr
"R" : "_" , #insert
"O" : "!" , #end
}
_special_key = {
"A" : "A" , #top
"B" : "B" , #down
"C" : "C" , #right
"D" : "D" , #left
"3" : "?" , #suppr
"2" : "_" , #insert
"F" : "!" , #end
}
def _getch() :
"""
Attend la frappe d'un caractère puis le retourne
@returns:
ch {str}
"""
sys.stdout.flush()
ch = ""
if os.name == "nt" :
ch = msvcrt.getwch()
else :
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try :
tty.setraw(sys.stdin.fileno())
ch = sys.stdin.read(1)
finally :
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
#if os.name == "nt" :
# if ord(ch) == 224 :
# ch = "^[" + _special_key_tn[_getch()]
#else :
# if ord(ch) == 27 :
# _getch()
# ch = "^[" + _special_key[_getch()]
# print(_getch())
if os.name == "nt" and msvcrt.kbhit() :
print("ok")
return ch
if __name__ == "__main__":
print("key pressed :", _getch())
print("key pressed :", _getch())
print("key pressed :", _getch())
print("key pressed :", _getch())
print("key pressed :", _getch())
print("key pressed :", _getch())
(the _input function but it has no problem at the moment)
class color :
def list() :
for i in range(100) :
print(f"\033[1;{i}m{i}")
white = lambda string: "\033[1;37m" + string + "\033[0;00m"
grey = lambda string: "\033[1;30m" + string + "\033[0;00m"
yellow = lambda string: "\033[1;33m" + string + "\033[0;00m"
green = lambda string: "\033[1;32m" + string + "\033[0;00m"
blue = lambda string: "\033[1;34m" + string + "\033[0;00m"
cyan = lambda string: "\033[1;36m" + string + "\033[0;00m"
red = lambda string: "\033[1;31m" + string + "\033[0;00m"
magenta = lambda string: "\033[1;35m" + string + "\033[0;00m"
black = lambda string: "\033[1;30m" + string + "\033[0;00m"
darkwhite = lambda string: "\033[0;37m" + string + "\033[0;00m"
darkyellow = lambda string: "\033[0;33m" + string + "\033[0;00m"
darkgreen = lambda string: "\033[0;32m" + string + "\033[0;00m"
darkblue = lambda string: "\033[0;34m" + string + "\033[0;00m"
darkcyan = lambda string: "\033[0;36m" + string + "\033[0;00m"
darkred = lambda string: "\033[0;31m" + string + "\033[0;00m"
darkmagenta = lambda string: "\033[0;35m" + string + "\033[0;00m"
darkblack = lambda string: "\033[0;30m" + string + "\033[0;00m"
placeholder = lambda string: "\033[1;30m" + string + "\033[0;00m" + f"\033[{len(string)}D"
def _input(string="", end="\n", pos=(0, 0), placeholder="", max=-1, width=-1, background="") :
enc = "utf-8"
line = ""
i = 0
start = 0
if width == -1 :
width = tuple(os.get_terminal_size())[0] - 2
width -= len(string) + pos[0]
stop = f"\033[{width+1}C"
placeholder = placeholder[:width]
y_pos = f"\033[{pos[1]}E" if pos[1] != 0 else ""
print(f"{y_pos}\033[{pos[0] + 1}G\0337\033[0G{background}\0338{string}◄{color.placeholder(placeholder)}\0338{stop}►\0338◄", end="")
while True :
sys.stdout.flush()
char = msvcrt.getwch()
if char in ["\n", "\r", "\n\r", "\r\n"] :
# entry
break
if char == "\x08" :
# backspace
if i != 0 :
temp = len(line)
line = line[:i-1] + line[i:]
i -= temp - len(line)
char = ""
if char == '\xe0' :
# arrows
mov = msvcrt.getwch()
if mov == "K" : # left
if i > 0 :
i -= 1
if mov == "M" : # right
if i < len(line) :
i += 1
if mov == "S" : # suppr
if len(line) - i != 0 :
temp = len(line)
line = line[:i] + line[i+1:]
char = ""
if max != -1 and len(line) >= max :
# limit lenght
char = ""
line = line[:i] + char + line[i:]
i += len(char)
if i > width :
width += 1
start += 1
elif i < start :
start -= 1
width -= 1
right = left = 0
if width < len(line) :
right = 1
if start > 0 :
left = 1
j = f"\033[{i-start+1}C" if i-start+1 != 0 else ""
l = color.green("◄") if left else "◄"
r = color.green("►") if right else "►"
ph = color.placeholder(placeholder) if len(line) == 0 else ""
print(f"\033[2K\033[0G{background}\0338{string}{l}{ph}{line[start:width]}\0338{stop}{r}\0338{j}", end="")
print(end, end="")
return line
PS: If you have ideas for optimizations for my functions, give them, because on my pc it runs almost well, but I doubt that it runs well on all PCs.
Solution 1:[1]
indeed, these are sequences ANSI, but I did not know them and they were not in the doc I had (gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797). Now I have another problem: When I type "only" esc, the program waits for another input. How can I check if there are still "pending entries"? (I tried sys.stdin but found it didn't work...
My new code:
import os
import sys
if os.name == "nt":
import msvcrt
else:
import tty
import termios
_escapes_sequences_tn = {
"H" : "A" , #top
"P" : "B" , #down
"M" : "C" , #right
"K" : "D" , #left
}
def _getch() :
"""
Attend la frappe d'un caractère puis le retourne
@returns:
ch {str}
"""
sys.stdout.flush()
ch = ""
if os.name == "nt" :
ch = msvcrt.getwch()
else :
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try :
tty.setraw(sys.stdin.fileno())
ch = sys.stdin.read(1)
finally :
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
if os.name == "nt" :
if ord(ch) == 224 :
ch = "^[" + _escapes_sequences_tn[_getch()]
else :
if ord(ch) == 27 :
_ch = _getch()
if len(_ch) == 1 and ord(_ch) == 91 :
_ch += _getch()
while _ch[-1].isnumeric() :
_ch += _getch()
else :
_ch += _getch()
ch = "^[" + _ch
return ch
if __name__ == "__main__":
print("key pressed :", _getch())
print("key pressed :", _getch())
print("key pressed :", _getch())
print("key pressed :", _getch())
print("key pressed :", _getch())
print("key pressed :", _getch())
Solution 2:[2]
This is a possible solution :
import os
import sys
if os.name == "nt":
import msvcrt
else:
import tty
import termios
class _keyboard_nt:
TOP = "^[H" # top
DOWN = "^[P" # down
RIGHT = "^[M" # right
LEFT = "^[K" # left
DEL = "^[S" # del
END = "^[O" # end
NL = "\r" # newline
TAB = "\t" # tab
BS = "\x08" # backspace
ctrl_A = "\x01" # ctrl+A
ctrl_Z = "\x1a" # ctrl+Z
ctrl_E = "\x05" # ctrl+E
ctrl_R = "\x12" # ctrl+R
ctrl_T = "\x14" # ctrl+T
ctrl_Y = "\x19" # ctrl+Y
ctrl_U = "\x15" # ctrl+U
ctrl_I = "\t" # ctrl+I
ctrl_O = "\x0f" # ctrl+O
ctrl_P = "\x10" # ctrl+P
ctrl_Q = "\x11" # ctrl+Q
ctrl_S = "\x13" # ctrl+S
ctrl_D = "\x04" # ctrl+D
ctrl_F = "\x06" # ctrl+F
ctrl_G = "\x07" # ctrl+G
ctrl_H = "\x08" # ctrl+H
ctrl_J = "\n" # ctrl+J
ctrl_K = "\x0b" # ctrl+K
ctrl_L = "\x0c" # ctrl+L
ctrl_M = "\r" # ctrl+M
ctrl_W = "\x17" # ctrl+W
ctrl_X = "\x18" # ctrl+X
ctrl_C = "\x03" # ctrl+C
ctrl_B = "\x02" # ctrl+B
ctrl_N = "\x0e" # ctrl+N
ctrl_UP = "^[\x8d" # ctrl+UP
ctrl_DOWN = "^[\x91" # ctrl+DOWN
ctrl_LEFT = "^[s" # ctrl+LEFT
ctrl_RIGHT = "^[t" # ctrl+RIGHT
ctrl_NL = "\n" # ctrl+newline
ctrl_BS = "\x7f" # ctrl+backspace
ctrl_AST = "\x1c" # ctrl+*
ctrl_DOLLAR = "\x1d" # ctrl+$
def ctrl(char) :
assert type(char) == str, "char is not a str"
ctrl_char = {
"\x01" : "A", "\x1a" : "Z", "\x05" : "E", "\x12" : "R",
"\x14" : "T", "\x19" : "Y", "\x15" : "U", "\t" : "I",
"\x0f" : "O", "\x10" : "P", "\x11" : "Q", "\x13" : "S",
"\x04" : "D", "\x06" : "F", "\x07" : "G", "\x08" : "H",
"\n" : "J", "\x0b" : "K", "\x0c" : "L", "\r" : "M",
"\x17" : "W", "\x18" : "X", "\x03" : "C", "\x02" : "B",
"\x0e" : "N",
"^[\x8d": "UP" , "^[\x91": "DOWN" ,
"^[s" : "LEFT", "^[t" : "RIGHT",
"\n" : "NL" , "\x7f" : "BS" ,
"\x1c" : "*", "\x1d" : "$",
}
return ctrl_char.get(char)
class _keyboard_ln:
TOP = "^[[A" # top
DOWN = "^[[B" # down
RIGHT = "^[[C" # right
LEFT = "^[[D" # left
DEL = "^[[3~" # del
END = "^[[F" # end
NL = "\r" # newline
TAB = "\t" # tab
BS = "\x7f" # backspace
ctrl_A = "\x01" # ctrl+A
ctrl_Z = "\x1a" # ctrl+Z
ctrl_E = "\x05" # ctrl+E
ctrl_R = "\x12" # ctrl+R
ctrl_T = "\x14" # ctrl+T
ctrl_Y = "\x19" # ctrl+Y
ctrl_U = "\x15" # ctrl+U
ctrl_I = "\t" # ctrl+I
ctrl_O = "\x0f" # ctrl+O
ctrl_P = "\x10" # ctrl+P
ctrl_Q = "\x11" # ctrl+Q
ctrl_S = "\x13" # ctrl+S
ctrl_D = "\x04" # ctrl+D
ctrl_F = "\x06" # ctrl+F
ctrl_G = "\x07" # ctrl+G
ctrl_H = "\x08" # ctrl+H
ctrl_J = "\n" # ctrl+J
ctrl_K = "\x0b" # ctrl+K
ctrl_L = "\x0c" # ctrl+L
ctrl_M = "\r" # ctrl+M
ctrl_W = "\x17" # ctrl+W
ctrl_X = "\x18" # ctrl+X
ctrl_C = "\x03" # ctrl+C
ctrl_B = "\x02" # ctrl+B
ctrl_N = "\x0e" # ctrl+N
ctrl_UP = "^[[1;5A" # ctrl+UP
ctrl_DOWN = "^[[1;5B" # ctrl+DOWN
ctrl_LEFT = "^[[1;5D" # ctrl+LEFT
ctrl_RIGHT = "^[[1;5C" # ctrl+RIGHT
ctrl_NL = "\n" # ctrl+newline
ctrl_BS = "\x08" # ctrl+backspace
ctrl_AST = "\x1c" # ctrl+*
ctrl_DOLLAR = "\x1d" # ctrl+$
def ctrl(char) :
assert type(char) == str, "char is not a str"
ctrl_char = {
"\x01" : "A", "\x1a" : "Z", "\x05" : "E", "\x12" : "R",
"\x14" : "T", "\x19" : "Y", "\x15" : "U", "\t" : "I",
"\x0f" : "O", "\x10" : "P", "\x11" : "Q", "\x13" : "S",
"\x04" : "D", "\x06" : "F", "\x07" : "G", "\x08" : "H",
"\n" : "J", "\x0b" : "K", "\x0c" : "L", "\r" : "M",
"\x17" : "W", "\x18" : "X", "\x03" : "C", "\x02" : "B",
"\x0e" : "N",
"^[[1;5A": "UP" , "^[[1;5B": "DOWN" ,
"^[[1;5D": "LEFT", "^[[1;5C": "RIGHT",
"\n" : "NL" , "\x08" : "BS" ,
"\x1c" : "*", "\x1d" : "$",
}
return ctrl_char.get(char)
def _getch_nt() :
"""
Attend la frappe d'un caractère puis le retourne (pour Windows)
@returns:
ch {str}
"""
sys.stdout.flush()
ch = ""
ch = msvcrt.getwch()
if ord(ch) == 224 :
ch = "^[" + getch()
return ch
def _getch_ln() :
"""
Attend la frappe d'un caractère puis le retourne (pour Linux)
@returns:
ch {str}
"""
sys.stdout.flush()
ch = ""
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try :
tty.setraw(sys.stdin.fileno())
ch = sys.stdin.read(1)
finally :
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
if ord(ch) == 27 :
_ch = getch()
_ch += getch()
while _ch[-1].isnumeric() or _ch[-1] == ";" :
_ch += getch()
ch = "^[" + _ch
return ch
if os.name == "nt" :
getch = _getch_nt
class keyboard(_keyboard_nt):
pass
else :
getch = _getch_ln
class keyboard(_keyboard_ln):
pass
if __name__ == "__main__":
ch = ""
while ch != keyboard.END :
ch = getch()
print("key pressed :", keyboard.ctrl(ch))
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Manolo |
