Reach consistent state

This commit is contained in:
Digital Studium 2024-04-11 21:53:04 +03:00
parent f9dcbf2a13
commit ff85b0111c
3 changed files with 161 additions and 132 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.idea

276
kls
View File

@ -3,9 +3,10 @@ import curses
import subprocess import subprocess
stdscr = None stdscr = None
curses.set_escdelay(1) # в curses зачем-то сделали задержку на срабатывание Escape, уменьшаем её до 1 милисекунды (до 0 нельзя)
running = True running = True
# states of menu # состояния меню
SELECTED_WITHOUT_SEARCH = 1 SELECTED_WITHOUT_SEARCH = 1
SELECTED_WITH_SEARCH = 2 SELECTED_WITH_SEARCH = 2
NOT_SELECTED_WITHOUT_SEARCH = 3 NOT_SELECTED_WITHOUT_SEARCH = 3
@ -31,17 +32,12 @@ class Menu:
self.state = state self.state = state
self.name = name # заголовок окна self.name = name # заголовок окна
self.rows = rows # строки окна self.rows = rows # строки окна
self.filtered_rows = rows # фильтрованные строки окна
self.begin_x = begin_x # где начинается окно по х? self.begin_x = begin_x # где начинается окно по х?
self.win = curses.newwin(curses.LINES, curses.COLS // 3, 0, self.win = curses.newwin(curses.LINES, curses.COLS // 3, 0,
begin_x) # окно с высотой во весь экран, шириной экран / 3, и началом по х в точке begin_x begin_x) # окно с высотой во весь экран, шириной экран / 3, и началом по х в точке begin_x
self.win.box() # ? self.win.box() # ?
self.win.addstr(1, 2, self.name) # рисуем заголовок
for index, row in enumerate(self.rows): # рисуем строки
self.win.addstr(index + 3, 2,
row) # + 3 потому что я хочу чтобы строки оборажались ниже заголовка на три строки
self.row = 0 # выбранная строка self.row = 0 # выбранная строка
self.search_string = "" self.filter = ""
# рисуем первое меню # рисуем первое меню
@ -80,7 +76,7 @@ menus = [menu1, menu2, menu3]
# menu.win.clear() # menu.win.clear()
# menu.win.box() # menu.win.box()
# menu.win.addstr(1, 2, menu.name) # menu.win.addstr(1, 2, menu.name)
# menu.win.addstr(curses.LINES - 2, 2, menu.search_string) # menu.win.addstr(curses.LINES - 2, 2, menu.filter)
# if rows and menu.name == "Resources": # if rows and menu.name == "Resources":
# menu.rows = rows # menu.rows = rows
# menu.row = 0 # menu.row = 0
@ -90,38 +86,30 @@ menus = [menu1, menu2, menu3]
# menu.win.addstr(1, 2, menu.name, curses.A_REVERSE | curses.A_ITALIC) # menu.win.addstr(1, 2, menu.name, curses.A_REVERSE | curses.A_ITALIC)
# def navigate_horizontally(direction, current_menu): def update_menu3():
# increment = {"right": 1, "left": -1} menu1_filtered_rows = list(filter(lambda x: (x.startswith(menu1.filter)), menu1.rows)) # фильтруем строки
# menus[current_menu].win.addstr(1, 2, menus[current_menu].name) # удаляем выделение с текущего меню menu2_filtered_rows = list(filter(lambda x: (x.startswith(menu2.filter)), menu2.rows)) # фильтруем строки
# current_menu = (current_menu + increment[direction]) % 3 # переходим к предыдущему/следующему меню if not menu1_filtered_rows or not menu2_filtered_rows:
# menus[current_menu].win.addstr(1, 2, menus[current_menu].name, curses.A_REVERSE | curses.A_ITALIC) # и выделяем его resources = [f"No resources matched criteria.", ]
# return current_menu else:
namespace = menu1_filtered_rows[menu1.row]
api_resource = menu2_filtered_rows[menu2.row]
command = "f'kubectl get {api_resource} -n {namespace} --no-headers -o template=\"{{{{range .items}}}}{{{{.metadata.name}}}} {{{{end}}}}\"'"
bytes_list = subprocess.check_output(eval(command), shell=True).split()
resources = [bytes_list[i].decode('utf-8') for i in range(len(bytes_list))]
if not resources:
resources = [f"No resources found in {namespace} namespace.", ]
menu3.rows = resources
menu3.row = 0
# def update_menu3(menu): def draw_menu(menu):
# namespace = menu1.filtered_rows[menu1.row] draw_header(menu) # рисуем заголовок
# api_resource = menu2.filtered_rows[menu2.row] draw_rows(menu) # рисуем строки меню
# command = "f'kubectl get {api_resource} -n {namespace} --no-headers -o template=\"{{{{range .items}}}}{{{{.metadata.name}}}} {{{{end}}}}\"'" draw_search_box(menu) # рисуем строку поиска
# bytes_list = subprocess.check_output(eval(command), shell=True).split()
# resources = [bytes_list[i].decode('utf-8') for i in range(len(bytes_list))]
# if not resources:
# resources = [f"No resources found in {namespace} namespace.",]
# menu3.rows = resources
# menu3.filtered_rows = resources
# menu3.win.clear()
# draw_rows(menu3)
# run_command(command, menu, rows=resources)
def draw_header(menu):
def draw_search_box(menu, content): if menu.state in [1, 2]:
menu.win.addstr(curses.LINES - 2, 2, content) # рисуем контент
menu.win.clrtoeol() # очищаем остальную часть строки
menu.win.box() # рисуем рамку
menu.win.refresh() # обновляем окно
def draw_header(menu, selected=False):
if selected:
menu.win.addstr(1, 2, menu.name, curses.A_REVERSE | curses.A_ITALIC) menu.win.addstr(1, 2, menu.name, curses.A_REVERSE | curses.A_ITALIC)
else: else:
menu.win.addstr(1, 2, menu.name) menu.win.addstr(1, 2, menu.name)
@ -129,8 +117,7 @@ def draw_header(menu, selected=False):
def draw_rows(menu): def draw_rows(menu):
menu.win.addstr(1, 2, menu.name) # рисуем заголовок filtered_rows = list(filter(lambda x: (x.startswith(menu.filter)), menu.rows)) # фильтруем строки
filtered_rows = list(filter(lambda x: (x.startswith(menu.search_string)), menu.rows)) # фильтруем строки
for index, row in enumerate(filtered_rows): # рисуем то, что отфильтровали for index, row in enumerate(filtered_rows): # рисуем то, что отфильтровали
menu.win.addstr(index + 3, 2, row) menu.win.addstr(index + 3, 2, row)
if filtered_rows: if filtered_rows:
@ -139,40 +126,43 @@ def draw_rows(menu):
menu.win.refresh() menu.win.refresh()
def navigation_without_search(menu): def draw_search_box(menu):
# рисуем строку поиска
if menu.state in [2, 4]:
content = f"/{menu.filter}"
else:
content = "Press / for search"
menu.win.addstr(curses.LINES - 2, 2, content) # рисуем контент
menu.win.clrtoeol() # очищаем остальную часть строки
menu.win.box() # рисуем рамку
menu.win.refresh() # обновляем окно
def catch_input(menu):
global running global running
draw_header(menu, selected=True)
draw_search_box(menu, "Press / for search") # рисуем строку поиска
key_pressed = stdscr.getkey() key_pressed = stdscr.getkey()
match key_pressed: match key_pressed:
case "/": case "/":
menu.state = SELECTED_WITH_SEARCH menu.state = SELECTED_WITH_SEARCH
case "q": case "q":
if menu.state == 1:
running = False running = False
case '\t' | "KEY_RIGHT": else:
navigate_horizontally("right", menu) menu.filter += key_pressed
case "KEY_BTAB" | "KEY_LEFT": menu.win.clear() # очищаю всё окно потому что лень очищать все строки только в конкретной области окна
navigate_horizontally("left", menu) menu.row = 0
case "KEY_DOWN": draw_menu(menu)
navigate_vertically("down", menu) case "\x1b": # Escape disables search mode
# update_menu3(menu) if menu.state == SELECTED_WITH_SEARCH:
case "KEY_UP": menu.filter = ""
navigate_vertically("up", menu)
# update_menu3(menu)
def navigation_with_search(menu):
global running
draw_header(menu, selected=True)
draw_search_box(menu, f"/{menu.search_string}") # рисуем строку поиска
key_pressed = stdscr.getkey()
match key_pressed:
case "KEY_BACKSPACE":
if menu.search_string:
menu.search_string = menu.search_string[:-1] # удаляем символ из строки поиска
menu.win.clear() menu.win.clear()
draw_search_box(menu, f"/{menu.search_string}")
draw_rows(menu) draw_rows(menu)
menu.state = SELECTED_WITHOUT_SEARCH
case "KEY_BACKSPACE":
if menu.filter:
menu.filter = menu.filter[:-1] # удаляем символ из строки поиска
menu.win.clear()
draw_menu(menu)
else: else:
menu.state = SELECTED_WITHOUT_SEARCH menu.state = SELECTED_WITHOUT_SEARCH
case '\t' | "KEY_RIGHT": case '\t' | "KEY_RIGHT":
@ -181,29 +171,31 @@ def navigation_with_search(menu):
navigate_horizontally("left", menu) navigate_horizontally("left", menu)
case "KEY_DOWN": case "KEY_DOWN":
navigate_vertically("down", menu) navigate_vertically("down", menu)
# update_menu3(menu)
case "KEY_UP": case "KEY_UP":
navigate_vertically("up", menu) navigate_vertically("up", menu)
# update_menu3(menu)
case _: case _:
if key_pressed.isalpha() or key_pressed == "-": # namespace не может иметь иных символов кроме a-z и - if menu.state == SELECTED_WITH_SEARCH and key_pressed.isalpha() or key_pressed == "-": # объекты в кубе не могут иметь иных символов кроме a-z и -
menu.search_string += key_pressed menu.filter += key_pressed
menu.win.clear() menu.win.clear()
menu.row = 0 menu.row = 0
draw_search_box(menu, f"/{menu.search_string}") draw_menu(menu)
draw_rows(menu) if menu3.state not in [1, 2]:
menu3.win.clear()
update_menu3()
draw_menu(menu3)
def navigate_horizontally(direction, menu): def navigate_horizontally(direction, menu):
increment = {"right": 1, "left": -1} increment = {"right": 1, "left": -1}
# чтобы понять, какой порядковый номер у следующего/предыдущего меню, нужно сперва определить номер текущего меню
menu_index = {menu1: 0, menu2: 1, menu3: 2} menu_index = {menu1: 0, menu2: 1, menu3: 2}
next_menu = menus[(menu_index[menu] + increment[direction]) % 3] next_menu = menus[(menu_index[menu] + increment[direction]) % 3]
draw_header(menu) # убираем выделение с текущего меню if menu.filter:
if menu.search_string:
menu.state = NOT_SELECTED_WITH_SEARCH menu.state = NOT_SELECTED_WITH_SEARCH
else: else:
menu.state = NOT_SELECTED_WITHOUT_SEARCH menu.state = NOT_SELECTED_WITHOUT_SEARCH
if next_menu.search_string: draw_header(menu) # убираем выделение с текущего меню
if next_menu.filter:
next_menu.state = SELECTED_WITH_SEARCH next_menu.state = SELECTED_WITH_SEARCH
else: else:
next_menu.state = SELECTED_WITHOUT_SEARCH next_menu.state = SELECTED_WITHOUT_SEARCH
@ -211,10 +203,10 @@ def navigate_horizontally(direction, menu):
def navigate_vertically(direction, menu): def navigate_vertically(direction, menu):
increment = {"down": 1, "up": -1} increment = {"down": 1, "up": -1}
filtered_rows = list(filter(lambda x: (x.startswith(menu.search_string)), menu.rows)) # фильтруем строки filtered_rows = list(filter(lambda x: (x.startswith(menu.filter)), menu.rows)) # фильтруем строки
if not filtered_rows: if not filtered_rows or len(filtered_rows) == 1:
return return
if filtered_rows[menu.row].startswith("No resources"): # это касается только третьего меню elif filtered_rows[menu.row].startswith("No resources"): # это касается только третьего меню
return return
menu.win.addstr(menu.row + 3, 2, filtered_rows[menu.row]) # удаляем выделение с текущей строки menu.win.addstr(menu.row + 3, 2, filtered_rows[menu.row]) # удаляем выделение с текущей строки
menu.row = (menu.row + increment[direction]) % len(filtered_rows) # переходим к предыдущей/следующей строке menu.row = (menu.row + increment[direction]) % len(filtered_rows) # переходим к предыдущей/следующей строке
@ -226,62 +218,112 @@ def main(stdscr):
stdscr.refresh() stdscr.refresh()
# начальный экран # начальный экран
for menu in menus: for menu in menus:
draw_rows(menu) # рисуем строки меню draw_menu(menu)
draw_search_box(menu, "Press / for search") # рисуем строку поиска
while running: while running:
### выбрано первое меню ### ### выбрано первое меню ###
menu = menu1
if menu1.state == 1 and menu2.state == 3 and menu3.state == 3: if menu1.state == 1 and menu2.state == 3 and menu3.state == 3:
navigation_without_search(menu1) draw_header(menu) # рисуем заголовок
if menu1.state == 1 and menu2.state == 4 and menu3.state == 3: draw_search_box(menu) # рисуем строку поиска
navigation_without_search(menu1) catch_input(menu) # перехватываем нажатия клавиш
if menu1.state == 1 and menu2.state == 3 and menu3.state == 4: elif menu1.state == 1 and menu2.state == 4 and menu3.state == 3:
navigation_without_search(menu1) draw_header(menu) # рисуем заголовок
if menu1.state == 1 and menu2.state == 4 and menu3.state == 4: draw_search_box(menu) # рисуем строку поиска
navigation_without_search(menu1) catch_input(menu) # перехватываем нажатия клавиш
elif menu1.state == 1 and menu2.state == 3 and menu3.state == 4:
draw_header(menu) # рисуем заголовок
draw_search_box(menu) # рисуем строку поиска
catch_input(menu) # перехватываем нажатия клавиш
elif menu1.state == 1 and menu2.state == 4 and menu3.state == 4:
draw_header(menu) # рисуем заголовок
draw_search_box(menu) # рисуем строку поиска
catch_input(menu) # перехватываем нажатия клавиш
elif menu1.state == 2 and menu2.state == 3 and menu3.state == 3: elif menu1.state == 2 and menu2.state == 3 and menu3.state == 3:
navigation_with_search(menu1) draw_header(menu) # рисуем заголовок
draw_search_box(menu) # рисуем строку поиска
catch_input(menu) # перехватываем нажатия клавиш
elif menu1.state == 2 and menu2.state == 4 and menu3.state == 3: elif menu1.state == 2 and menu2.state == 4 and menu3.state == 3:
navigation_with_search(menu1) draw_header(menu) # рисуем заголовок
draw_search_box(menu) # рисуем строку поиска
catch_input(menu) # перехватываем нажатия клавиш
elif menu1.state == 2 and menu2.state == 3 and menu3.state == 4: elif menu1.state == 2 and menu2.state == 3 and menu3.state == 4:
navigation_with_search(menu1) draw_header(menu) # рисуем заголовок
draw_search_box(menu) # рисуем строку поиска
catch_input(menu) # перехватываем нажатия клавиш
elif menu1.state == 2 and menu2.state == 4 and menu3.state == 4: elif menu1.state == 2 and menu2.state == 4 and menu3.state == 4:
navigation_with_search(menu1) draw_header(menu) # рисуем заголовок
draw_search_box(menu) # рисуем строку поиска
catch_input(menu) # перехватываем нажатия клавиш
### выбрано второе меню ### ### выбрано второе меню ###
menu = menu2
if menu1.state == 3 and menu2.state == 1 and menu3.state == 3: if menu1.state == 3 and menu2.state == 1 and menu3.state == 3:
navigation_without_search(menu2) draw_header(menu) # рисуем заголовок
if menu1.state == 3 and menu2.state == 1 and menu3.state == 4: draw_search_box(menu) # рисуем строку поиска
navigation_without_search(menu2) catch_input(menu) # перехватываем нажатия клавиш
if menu1.state == 4 and menu2.state == 1 and menu3.state == 3: elif menu1.state == 3 and menu2.state == 1 and menu3.state == 4:
navigation_without_search(menu2) draw_header(menu) # рисуем заголовок
if menu1.state == 4 and menu2.state == 1 and menu3.state == 4: draw_search_box(menu) # рисуем строку поиска
navigation_without_search(menu2) catch_input(menu) # перехватываем нажатия клавиш
elif menu1.state == 4 and menu2.state == 1 and menu3.state == 3:
draw_header(menu) # рисуем заголовок
draw_search_box(menu) # рисуем строку поиска
catch_input(menu) # перехватываем нажатия клавиш
elif menu1.state == 4 and menu2.state == 1 and menu3.state == 4:
draw_header(menu) # рисуем заголовок
draw_search_box(menu) # рисуем строку поиска
catch_input(menu) # перехватываем нажатия клавиш
elif menu1.state == 3 and menu2.state == 2 and menu3.state == 3: elif menu1.state == 3 and menu2.state == 2 and menu3.state == 3:
navigation_with_search(menu2) draw_header(menu) # рисуем заголовок
draw_search_box(menu) # рисуем строку поиска
catch_input(menu) # перехватываем нажатия клавиш
elif menu1.state == 3 and menu2.state == 2 and menu3.state == 4: elif menu1.state == 3 and menu2.state == 2 and menu3.state == 4:
navigation_with_search(menu2) draw_header(menu) # рисуем заголовок
draw_search_box(menu) # рисуем строку поиска
catch_input(menu) # перехватываем нажатия клавиш
elif menu1.state == 4 and menu2.state == 2 and menu3.state == 3: elif menu1.state == 4 and menu2.state == 2 and menu3.state == 3:
navigation_with_search(menu2) draw_header(menu) # рисуем заголовок
draw_search_box(menu) # рисуем строку поиска
catch_input(menu) # перехватываем нажатия клавиш
elif menu1.state == 4 and menu2.state == 2 and menu3.state == 4: elif menu1.state == 4 and menu2.state == 2 and menu3.state == 4:
navigation_with_search(menu2) draw_header(menu) # рисуем заголовок
draw_search_box(menu) # рисуем строку поиска
catch_input(menu) # перехватываем нажатия клавиш
### выбрано третье меню ### ### выбрано третье меню ###
menu = menu3
if menu1.state == 3 and menu2.state == 3 and menu3.state == 1: if menu1.state == 3 and menu2.state == 3 and menu3.state == 1:
navigation_without_search(menu3) draw_header(menu) # рисуем заголовок
if menu1.state == 3 and menu2.state == 4 and menu3.state == 1: draw_search_box(menu) # рисуем строку поиска
navigation_without_search(menu3) catch_input(menu) # перехватываем нажатия клавиш
if menu1.state == 4 and menu2.state == 3 and menu3.state == 1: elif menu1.state == 3 and menu2.state == 4 and menu3.state == 1:
navigation_without_search(menu3) draw_header(menu) # рисуем заголовок
if menu1.state == 4 and menu2.state == 4 and menu3.state == 1: draw_search_box(menu) # рисуем строку поиска
navigation_without_search(menu3) catch_input(menu) # перехватываем нажатия клавиш
elif menu1.state == 4 and menu2.state == 3 and menu3.state == 1:
draw_header(menu) # рисуем заголовок
draw_search_box(menu) # рисуем строку поиска
catch_input(menu) # перехватываем нажатия клавиш
elif menu1.state == 4 and menu2.state == 4 and menu3.state == 1:
draw_header(menu) # рисуем заголовок
draw_search_box(menu) # рисуем строку поиска
catch_input(menu) # перехватываем нажатия клавиш
elif menu1.state == 3 and menu2.state == 3 and menu3.state == 2: elif menu1.state == 3 and menu2.state == 3 and menu3.state == 2:
navigation_with_search(menu3) draw_header(menu) # рисуем заголовок
draw_search_box(menu) # рисуем строку поиска
catch_input(menu) # перехватываем нажатия клавиш
elif menu1.state == 3 and menu2.state == 4 and menu3.state == 2: elif menu1.state == 3 and menu2.state == 4 and menu3.state == 2:
navigation_with_search(menu3) draw_header(menu) # рисуем заголовок
draw_search_box(menu) # рисуем строку поиска
catch_input(menu) # перехватываем нажатия клавиш
elif menu1.state == 4 and menu2.state == 3 and menu3.state == 2: elif menu1.state == 4 and menu2.state == 3 and menu3.state == 2:
navigation_with_search(menu3) draw_header(menu) # рисуем заголовок
draw_search_box(menu) # рисуем строку поиска
catch_input(menu) # перехватываем нажатия клавиш
elif menu1.state == 4 and menu2.state == 4 and menu3.state == 2: elif menu1.state == 4 and menu2.state == 4 and menu3.state == 2:
navigation_with_search(menu3) draw_header(menu) # рисуем заголовок
draw_search_box(menu) # рисуем строку поиска
catch_input(menu) # перехватываем нажатия клавиш
# if current_menu == 0: # if current_menu == 0:
@ -298,13 +340,13 @@ def main(stdscr):
# case "KEY_UP": # case "KEY_UP":
# navigate_vertically("up", current_menu) # navigate_vertically("up", current_menu)
# case "KEY_BACKSPACE": # case "KEY_BACKSPACE":
# if search_string: # if filter:
# search_string = search_string[:-1] # filter = filter[:-1]
# else: # else:
# search_mode = False # search_mode = False
# if key_pressed.isalpha() or key_pressed == "-": # if key_pressed.isalpha() or key_pressed == "-":
# search_string += key_pressed # filter += key_pressed
# elif key_pressed == "/" and search_string == "": # elif key_pressed == "/" and filter == "":
# pass # pass
# else: # else:
# continue # continue
@ -314,10 +356,10 @@ def main(stdscr):
# menu.win.box() # menu.win.box()
# menu.win.addstr(1, 2, menu.name) # добавляем заголовок окна # menu.win.addstr(1, 2, menu.name) # добавляем заголовок окна
# if menu.name == "Namespaces": # if menu.name == "Namespaces":
# menu.rows = list(filter(lambda x: (x.startswith(search_string)), namespaces)) # меняем строки у окна Namespaces # menu.rows = list(filter(lambda x: (x.startswith(filter)), namespaces)) # меняем строки у окна Namespaces
# menu.row = 0 # menu.row = 0
# if search_mode: # if search_mode:
# menu.win.addstr(curses.LINES - 2, 2, f"/{search_string}") # menu.win.addstr(curses.LINES - 2, 2, f"/{filter}")
# else: # else:
# menu.win.addstr(curses.LINES - 2, 2, "Press / for search") # menu.win.addstr(curses.LINES - 2, 2, "Press / for search")
# elif menu.name == "Resources": # elif menu.name == "Resources":

14
~
View File

@ -1,14 +0,0 @@
import time
import curses
def draw(canvas):
while True:
key = canvas.getkey()
print(f"Вы ввели {key}")
if __name__ == '__main__':
curses.update_lines_cols()
curses.wrapper(draw)