diff --git a/kiauh/components/klipper_firmware/menus/klipper_build_menu.py b/kiauh/components/klipper_firmware/menus/klipper_build_menu.py index cd92e4f..6a3ab5b 100644 --- a/kiauh/components/klipper_firmware/menus/klipper_build_menu.py +++ b/kiauh/components/klipper_firmware/menus/klipper_build_menu.py @@ -81,6 +81,7 @@ class KlipperBuildFirmwareMenu(BaseMenu): line = f"{COLOR_RED}Dependencies are missing!{RESET_FORMAT}" menu += f"║ {line:<62} ║\n" + menu += "╟───────────────────────────────────────────────────────╢\n" print(menu, end="") diff --git a/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py b/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py index a32ccac..9c0c8b1 100644 --- a/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py +++ b/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py @@ -249,7 +249,7 @@ class KlipperSelectMcuIdMenu(BaseMenu): self.flash_options = FlashOptions() self.mcu_list = self.flash_options.mcu_list self.input_label_txt = "Select MCU to flash" - self.footer_type = FooterType.BACK_HELP + self.footer_type = FooterType.BACK def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None: self.previous_menu = ( @@ -265,7 +265,7 @@ class KlipperSelectMcuIdMenu(BaseMenu): def print_menu(self) -> None: header = "!!! ATTENTION !!!" - header2 = f"[{COLOR_CYAN}List of available MCUs{RESET_FORMAT}]" + header2 = f"[{COLOR_CYAN}List of detected MCUs{RESET_FORMAT}]" color = COLOR_RED count = 62 - len(color) - len(RESET_FORMAT) menu = textwrap.dedent( @@ -277,15 +277,21 @@ class KlipperSelectMcuIdMenu(BaseMenu): ║ ONLY flash a firmware created for the respective MCU! ║ ║ ║ ╟{header2:─^64}╢ + ║ ║ """ )[1:] for i, mcu in enumerate(self.mcu_list): mcu = mcu.split("/")[-1] - menu += f" ● MCU #{i}: {COLOR_CYAN}{mcu}{RESET_FORMAT}\n" - menu += "╟───────────────────────────┬───────────────────────────╢" + menu += f"║ {i}) {COLOR_CYAN}{mcu:<51}{RESET_FORMAT}║\n" - print(menu, end="\n") + menu += textwrap.dedent( + """ + ║ ║ + ╟───────────────────────────────────────────────────────╢ + """ + )[1:] + print(menu, end="") def flash_mcu(self, **kwargs): try: @@ -343,8 +349,8 @@ class KlipperSelectSDFlashBoardMenu(BaseMenu): for i, board in enumerate(self.available_boards): line = f" {i}) {board}" - menu += f"|{line:<55}|\n" - + menu += f"║{line:<55}║\n" + menu += "╟───────────────────────────────────────────────────────╢" print(menu, end="") def board_select(self, **kwargs): @@ -392,8 +398,8 @@ class KlipperFlashOverviewMenu(BaseMenu): def set_options(self) -> None: self.options = { - "Y": Option(self.execute_flash), - "N": Option(self.abort_process), + "y": Option(self.execute_flash), + "n": Option(self.abort_process), } self.default_option = Option(self.execute_flash) @@ -406,7 +412,7 @@ class KlipperFlashOverviewMenu(BaseMenu): method = self.flash_options.flash_method.value command = self.flash_options.flash_command.value conn_type = self.flash_options.connection_type.value - mcu = self.flash_options.selected_mcu + mcu = self.flash_options.selected_mcu.split("/")[-1] board = self.flash_options.selected_board baudrate = self.flash_options.selected_baudrate subheader = f"[{COLOR_CYAN}Overview{RESET_FORMAT}]" @@ -420,26 +426,37 @@ class KlipperFlashOverviewMenu(BaseMenu): ║ sure everything is correct, start the process. If any ║ ║ parameter needs to be changed, you can go back (B) ║ ║ step by step or abort and start from the beginning. ║ - ║{subheader:-^64}║ + ║{subheader:─^64}║ + ║ ║ """ )[1:] - menu += f" ● MCU: {COLOR_CYAN}{mcu}{RESET_FORMAT}\n" - menu += f" ● Connection: {COLOR_CYAN}{conn_type}{RESET_FORMAT}\n" - menu += f" ● Flash method: {COLOR_CYAN}{method}{RESET_FORMAT}\n" - menu += f" ● Flash command: {COLOR_CYAN}{command}{RESET_FORMAT}\n" + menu += textwrap.dedent( + f""" + ║ MCU: {COLOR_CYAN}{mcu:<48}{RESET_FORMAT} ║ + ║ Connection: {COLOR_CYAN}{conn_type:<41}{RESET_FORMAT} ║ + ║ Flash method: {COLOR_CYAN}{method:<39}{RESET_FORMAT} ║ + ║ Flash command: {COLOR_CYAN}{command:<38}{RESET_FORMAT} ║ + """ + )[1:] if self.flash_options.flash_method is FlashMethod.SD_CARD: - menu += f" ● Board type: {COLOR_CYAN}{board}{RESET_FORMAT}\n" - menu += f" ● Baudrate: {COLOR_CYAN}{baudrate}{RESET_FORMAT}\n" + menu += textwrap.dedent( + f""" + ║ Board type: {COLOR_CYAN}{board:<41}{RESET_FORMAT} ║ + ║ Baudrate: {COLOR_CYAN}{baudrate:<43}{RESET_FORMAT} ║ + """ + )[1:] menu += textwrap.dedent( """ + ║ ║ ╟───────────────────────────────────────────────────────╢ ║ Y) Start flash process ║ ║ N) Abort - Return to Advanced Menu ║ + ╟───────────────────────────────────────────────────────╢ """ - ) + )[1:] print(menu, end="") def execute_flash(self, **kwargs): diff --git a/kiauh/core/menus/__init__.py b/kiauh/core/menus/__init__.py index 1166127..3fdc671 100644 --- a/kiauh/core/menus/__init__.py +++ b/kiauh/core/menus/__init__.py @@ -22,7 +22,10 @@ class Option: :param opt_data: Can be used to pass any additional data to the menu option """ - method: Type[Callable] | None = None + def __repr__(self): + return f"Option(method={self.method.__name__}, opt_index={self.opt_index}, opt_data={self.opt_data})" + + method: Type[Callable] opt_index: str = "" opt_data: Any = None diff --git a/kiauh/core/menus/base_menu.py b/kiauh/core/menus/base_menu.py index d4eb30e..4963d93 100644 --- a/kiauh/core/menus/base_menu.py +++ b/kiauh/core/menus/base_menu.py @@ -25,6 +25,7 @@ from core.constants import ( ) from core.logger import Logger from core.menus import FooterType, Option +from utils.input_utils import get_selection_input def clear() -> None: @@ -141,7 +142,7 @@ class BaseMenu(metaclass=PostInitCaller): def __go_to_help(self, **kwargs) -> None: if self.help_menu is None: return - self.help_menu(previous_menu=self).run() + self.help_menu(previous_menu=self.__class__).run() def __exit(self, **kwargs) -> None: Logger.print_ok("###### Happy printing!", False) @@ -177,46 +178,20 @@ class BaseMenu(metaclass=PostInitCaller): self.print_menu() self.print_footer() - def validate_user_input(self, usr_input: str) -> Option: - """ - Validate the user input and either return an Option, a string or None - :param usr_input: The user input in form of a string - :return: Option, str or None - """ - usr_input = usr_input.lower() - option = self.options.get( - usr_input, - Option(method=None, opt_index="", opt_data=None), - ) - - # if option/usr_input is None/empty string, we execute the menus default option if specified - if (option is None or usr_input == "") and self.default_option is not None: - self.default_option.opt_index = usr_input - return self.default_option - - # user selected a regular option - option.opt_index = usr_input - return option - - def handle_user_input(self) -> Option: - """Handle the user input, return the validated input or print an error.""" - while True: - print(f"{COLOR_CYAN}###### {self.input_label_txt}: {RESET_FORMAT}", end="") - usr_input = input().lower() - validated_input = self.validate_user_input(usr_input) - - if validated_input.method is not None: - return validated_input - else: - Logger.print_error("Invalid input!", False) - def run(self) -> None: """Start the menu lifecycle. When this function returns, the lifecycle of the menu ends.""" try: self.display_menu() - option = self.handle_user_input() - option.method(opt_index=option.opt_index, opt_data=option.opt_data) + option = get_selection_input(self.input_label_txt, self.options) + selected_option: Option = self.options.get(option) + + selected_option.method( + opt_index=selected_option.opt_index, + opt_data=selected_option.opt_data, + ) + self.run() + except Exception as e: Logger.print_error( f"An unexpected error occured:\n{e}\n{traceback.format_exc()}" diff --git a/kiauh/utils/input_utils.py b/kiauh/utils/input_utils.py index 3fa5783..75af939 100644 --- a/kiauh/utils/input_utils.py +++ b/kiauh/utils/input_utils.py @@ -137,7 +137,7 @@ def get_selection_input(question: str, option_list: List | Dict, default=None) - else: raise ValueError("Invalid option_list type") - Logger.print_error(INVALID_CHOICE) + Logger.print_error("Invalid option! Please select a valid option.", False) def format_question(question: str, default=None) -> str: