mirror of
https://github.com/dw-0/kiauh.git
synced 2025-12-23 15:53:36 +05:00
shortens imports and helps with auto imports from IDE Signed-off-by: Dominik Willner <th33xitus@gmail.com>
190 lines
6.1 KiB
Python
190 lines
6.1 KiB
Python
#!/usr/bin/env python3
|
|
|
|
# ======================================================================= #
|
|
# Copyright (C) 2020 - 2024 Dominik Willner <th33xitus@gmail.com> #
|
|
# #
|
|
# This file is part of KIAUH - Klipper Installation And Update Helper #
|
|
# https://github.com/dw-0/kiauh #
|
|
# #
|
|
# This file may be distributed under the terms of the GNU GPLv3 license #
|
|
# ======================================================================= #
|
|
|
|
import subprocess
|
|
import sys
|
|
import textwrap
|
|
from abc import abstractmethod, ABC
|
|
from typing import Dict, Any, Literal, Union, Callable
|
|
|
|
from core.menus import QUIT_FOOTER, BACK_FOOTER, BACK_HELP_FOOTER
|
|
from utils.constants import (
|
|
COLOR_GREEN,
|
|
COLOR_YELLOW,
|
|
COLOR_RED,
|
|
COLOR_CYAN,
|
|
RESET_FORMAT,
|
|
)
|
|
from utils.logger import Logger
|
|
|
|
|
|
def clear():
|
|
subprocess.call("clear", shell=True)
|
|
|
|
|
|
def print_header():
|
|
line1 = " [ KIAUH ] "
|
|
line2 = "Klipper Installation And Update Helper"
|
|
line3 = ""
|
|
color = COLOR_CYAN
|
|
count = 62 - len(color) - len(RESET_FORMAT)
|
|
header = textwrap.dedent(
|
|
f"""
|
|
/=======================================================\\
|
|
| {color}{line1:~^{count}}{RESET_FORMAT} |
|
|
| {color}{line2:^{count}}{RESET_FORMAT} |
|
|
| {color}{line3:~^{count}}{RESET_FORMAT} |
|
|
\=======================================================/
|
|
"""
|
|
)[1:]
|
|
print(header, end="")
|
|
|
|
|
|
def print_quit_footer():
|
|
text = "Q) Quit"
|
|
color = COLOR_RED
|
|
count = 62 - len(color) - len(RESET_FORMAT)
|
|
footer = textwrap.dedent(
|
|
f"""
|
|
|-------------------------------------------------------|
|
|
| {color}{text:^{count}}{RESET_FORMAT} |
|
|
\=======================================================/
|
|
"""
|
|
)[1:]
|
|
print(footer, end="")
|
|
|
|
|
|
def print_back_footer():
|
|
text = "B) « Back"
|
|
color = COLOR_GREEN
|
|
count = 62 - len(color) - len(RESET_FORMAT)
|
|
footer = textwrap.dedent(
|
|
f"""
|
|
|-------------------------------------------------------|
|
|
| {color}{text:^{count}}{RESET_FORMAT} |
|
|
\=======================================================/
|
|
"""
|
|
)[1:]
|
|
print(footer, end="")
|
|
|
|
|
|
def print_back_help_footer():
|
|
text1 = "B) « Back"
|
|
text2 = "H) Help [?]"
|
|
color1 = COLOR_GREEN
|
|
color2 = COLOR_YELLOW
|
|
count = 34 - len(color1) - len(RESET_FORMAT)
|
|
footer = textwrap.dedent(
|
|
f"""
|
|
|-------------------------------------------------------|
|
|
| {color1}{text1:^{count}}{RESET_FORMAT} | {color2}{text2:^{count}}{RESET_FORMAT} |
|
|
\=======================================================/
|
|
"""
|
|
)[1:]
|
|
print(footer, end="")
|
|
|
|
|
|
class BaseMenu(ABC):
|
|
NAVI_OPTIONS = {"quit": ["q"], "back": ["b"], "back_help": ["b", "h"]}
|
|
|
|
def __init__(
|
|
self,
|
|
options: Dict[str, Union[Callable, Any]],
|
|
options_offset: int = 0,
|
|
header: bool = True,
|
|
footer_type: Literal[
|
|
"QUIT_FOOTER", "BACK_FOOTER", "BACK_HELP_FOOTER"
|
|
] = QUIT_FOOTER,
|
|
):
|
|
self.previous_menu = None
|
|
self.options = options
|
|
self.options_offset = options_offset
|
|
self.header = header
|
|
self.footer_type = footer_type
|
|
|
|
@abstractmethod
|
|
def print_menu(self) -> None:
|
|
raise NotImplementedError("Subclasses must implement the print_menu method")
|
|
|
|
def print_footer(self) -> None:
|
|
footer_type_map = {
|
|
QUIT_FOOTER: print_quit_footer,
|
|
BACK_FOOTER: print_back_footer,
|
|
BACK_HELP_FOOTER: print_back_help_footer,
|
|
}
|
|
footer_function = footer_type_map.get(self.footer_type, print_quit_footer)
|
|
footer_function()
|
|
|
|
def display(self) -> None:
|
|
# clear()
|
|
if self.header:
|
|
print_header()
|
|
self.print_menu()
|
|
self.print_footer()
|
|
|
|
def handle_user_input(self) -> str:
|
|
while True:
|
|
choice = input(f"{COLOR_CYAN}###### Perform action: {RESET_FORMAT}").lower()
|
|
option = self.options.get(choice, None)
|
|
|
|
has_navi_option = self.footer_type in self.NAVI_OPTIONS
|
|
user_navigated = choice in self.NAVI_OPTIONS[self.footer_type]
|
|
if has_navi_option and user_navigated:
|
|
return choice
|
|
|
|
if option is not None:
|
|
return choice
|
|
else:
|
|
Logger.print_error("Invalid input!", False)
|
|
|
|
def start(self) -> None:
|
|
while True:
|
|
self.display()
|
|
choice = self.handle_user_input()
|
|
|
|
if choice == "q":
|
|
Logger.print_ok("###### Happy printing!", False)
|
|
sys.exit(0)
|
|
elif choice == "b":
|
|
return
|
|
elif choice == "h":
|
|
print("help!")
|
|
else:
|
|
self.execute_option(choice)
|
|
|
|
def execute_option(self, choice: str) -> None:
|
|
option = self.options.get(choice, None)
|
|
|
|
if isinstance(option, type) and issubclass(option, BaseMenu):
|
|
self.navigate_to_menu(option, True)
|
|
elif isinstance(option, BaseMenu):
|
|
self.navigate_to_menu(option, False)
|
|
elif callable(option):
|
|
option(opt_index=choice)
|
|
elif option is None:
|
|
raise NotImplementedError(f"No implementation for option {choice}")
|
|
else:
|
|
raise TypeError(
|
|
f"Type {type(option)} of option {choice} not of type BaseMenu or Method"
|
|
)
|
|
|
|
def navigate_to_menu(self, menu, instantiate: bool) -> None:
|
|
"""
|
|
Method for handling the actual menu switch. Can either take in a menu type or an already
|
|
instantiated menu class. Use instantiated menu classes only if the menu requires specific input parameters
|
|
:param menu: A menu type or menu instance
|
|
:param instantiate: Specify if the menu requires instantiation
|
|
:return: None
|
|
"""
|
|
menu = menu() if instantiate else menu
|
|
menu.previous_menu = self
|
|
menu.start()
|