from designer import right_column, StepByStepDisplay import flet as ft import sys import os # Добавляем путь к модулю step_by_step_solver sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from step_by_step_solver import StepByStepSolver, Operation class Button_quinquagesimal_calculator(ft.CupertinoButton): def __init__(self, text: str = "50-ричный калькулятор"): self.result_left = ft.Text(value="0", size=30, width=300) self.result_right = ft.Text(value="0", size=30, width=300) self.result_mult = ft.Text(value="0", size=30, width=300) self.result_div = ft.Text(value="0", size=30, width=300) self.decimal_left = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300) self.decimal_right = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300) self.decimal_mult = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300) self.decimal_div = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300) # Поля ввода для вычитания self.input_left_1 = None self.input_left_2 = None # Поля ввода для сложения self.input_right_1 = None self.input_right_2 = None # Поля ввода для умножения self.input_mult_1 = None self.input_mult_2 = None # Поля ввода для деления self.input_div_1 = None self.input_div_2 = None # Контейнеры для пошагового решения self.steps_left = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5) self.steps_right = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5) self.steps_mult = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5) self.steps_div = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5) # Допустимые символы: 0-9, A-Z, a-n (всего: 10 + 26 + 14 = 50) self.digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn" # Солвер для 50-ричной системы с нашим алфавитом self.solver = StepByStepSolver(base=50, digits=self.digits) super().__init__( bgcolor="#00000000", disabled_bgcolor="#3A1E51", text=text, color="white", on_click=self.test_data ) def validate_base50(self, value: str) -> bool: """Проверяет, является ли строка корректным 50-ричным числом. Допустимые символы: 0-9, A-Z, a-n (0-49), опциональный знак минус """ if value is None or value == "": return True v = value.strip() if v == "": return True if v[0] == '-': v = v[1:] # В 50-ричной системе используются цифры 0-9, A-Z, a-n (всего 50 символов) valid_chars = self.digits # 0-9, A-Z, a-n return all(c in valid_chars for c in v) def to_base50(self, decimal_num: int) -> str: """Преобразование десятичного числа в 50-ричное""" if decimal_num == 0: return "0" is_negative = decimal_num < 0 decimal_num = abs(decimal_num) result = "" while decimal_num > 0: remainder = decimal_num % 50 result = self.digits[remainder] + result decimal_num //= 50 return ("-" + result) if is_negative else result def from_base50(self, base50_str: str) -> int: """Преобразование 50-ричного числа в десятичное""" if not base50_str: return 0 base50_str = base50_str.strip() if base50_str == "" or base50_str == "0": return 0 is_negative = base50_str.startswith('-') if is_negative: base50_str = base50_str[1:] result = 0 for char in base50_str: if char in self.digits: digit_value = self.digits.index(char) result = result * 50 + digit_value else: raise ValueError(f"Недопустимый символ '{char}' для 50-ричной системы") return -result if is_negative else result def show_steps_left(self, num1: str, num2: str, operation: Operation): """Отображает пошаговое решение для левой колонки (вычитание)""" StepByStepDisplay.show_steps(self.steps_left, self.solver, num1, num2, operation) def show_steps_right(self, num1: str, num2: str, operation: Operation): """Отображает пошаговое решение для правой колонки (сложение)""" StepByStepDisplay.show_steps(self.steps_right, self.solver, num1, num2, operation) def show_steps_mult(self, num1: str, num2: str, operation: Operation): """Отображает пошаговое решение для колонки умножения""" StepByStepDisplay.show_steps(self.steps_mult, self.solver, num1, num2, operation) def show_steps_div(self, num1: str, num2: str, operation: Operation): """Отображает пошаговое решение для колонки деления""" StepByStepDisplay.show_steps(self.steps_div, self.solver, num1, num2, operation) def calculate_subtraction(self, e): try: val1 = (self.input_left_1.value or "").strip() val2 = (self.input_left_2.value or "").strip() # Проверка корректности 50-ричных чисел if not self.validate_base50(val1) or not self.validate_base50(val2): self.result_left.value = "Ошибка: неверный формат" self.decimal_left.value = "Используйте 0-9, A-Z, a-n" e.page.update() return # Преобразуем в десятичные числа num1 = self.from_base50(val1 if val1 else "0") num2 = self.from_base50(val2 if val2 else "0") # Вычисляем результат result = num1 - num2 # Преобразуем обратно в 50-ричный формат base50_result = self.to_base50(result) self.result_left.value = base50_result self.decimal_left.value = f"({result}₁₀)" e.page.update() # Получаем пошаговое решение (убираем знак минус для солвера) clean_val1 = val1.lstrip('-') if val1.startswith('-') else val1 clean_val2 = val2.lstrip('-') if val2.startswith('-') else val2 self.show_steps_left(clean_val1 if clean_val1 else "0", clean_val2 if clean_val2 else "0", Operation.SUBTRACTION) except ValueError as ex: self.result_left.value = "Ошибка" self.decimal_left.value = str(ex) e.page.update() def calculate_addition(self, e): try: val1 = (self.input_right_1.value or "").strip() val2 = (self.input_right_2.value or "").strip() # Проверка корректности 50-ричных чисел if not self.validate_base50(val1) or not self.validate_base50(val2): self.result_right.value = "Ошибка: неверный формат" self.decimal_right.value = "Используйте 0-9, A-Z, a-n" e.page.update() return # Преобразуем в десятичные числа num1 = self.from_base50(val1 if val1 else "0") num2 = self.from_base50(val2 if val2 else "0") # Вычисляем результат result = num1 + num2 # Преобразуем обратно в 50-ричный формат base50_result = self.to_base50(result) self.result_right.value = base50_result self.decimal_right.value = f"({result}₁₀)" e.page.update() # Получаем пошаговое решение clean_val1 = val1.lstrip('-') if val1.startswith('-') else val1 clean_val2 = val2.lstrip('-') if val2.startswith('-') else val2 self.show_steps_right(clean_val1 if clean_val1 else "0", clean_val2 if clean_val2 else "0", Operation.ADDITION) except ValueError as ex: self.result_right.value = "Ошибка" self.decimal_right.value = str(ex) e.page.update() def calculate_multiplication(self, e): try: val1 = (self.input_mult_1.value or "").strip() val2 = (self.input_mult_2.value or "").strip() # Проверка корректности 50-ричных чисел if not self.validate_base50(val1) or not self.validate_base50(val2): self.result_mult.value = "Ошибка: неверный формат" self.decimal_mult.value = "Используйте 0-9, A-Z, a-n" e.page.update() return # Преобразуем в десятичные числа num1 = self.from_base50(val1 if val1 else "0") num2 = self.from_base50(val2 if val2 else "0") # Вычисляем результат result = num1 * num2 # Преобразуем обратно в 50-ричный формат base50_result = self.to_base50(result) self.result_mult.value = base50_result self.decimal_mult.value = f"({result}₁₀)" e.page.update() # Получаем пошаговое решение clean_val1 = val1.lstrip('-') if val1.startswith('-') else val1 clean_val2 = val2.lstrip('-') if val2.startswith('-') else val2 self.show_steps_mult(clean_val1 if clean_val1 else "0", clean_val2 if clean_val2 else "0", Operation.MULTIPLICATION) except ValueError as ex: self.result_mult.value = "Ошибка" self.decimal_mult.value = str(ex) e.page.update() def calculate_division(self, e): try: val1 = (self.input_div_1.value or "").strip() val2 = (self.input_div_2.value or "").strip() # Проверка корректности 50-ричных чисел if not self.validate_base50(val1) or not self.validate_base50(val2): self.result_div.value = "Ошибка: неверный формат" self.decimal_div.value = "Используйте 0-9, A-Z, a-n" e.page.update() return # Преобразуем в десятичные числа num1 = self.from_base50(val1 if val1 else "0") num2 = self.from_base50(val2 if val2 else "0") # Проверка деления на ноль if num2 == 0: self.result_div.value = "Ошибка" self.decimal_div.value = "Деление на ноль!" e.page.update() return # Вычисляем результат (целочисленное деление) result = num1 // num2 # Преобразуем обратно в 50-ричный формат base50_result = self.to_base50(result) self.result_div.value = base50_result self.decimal_div.value = f"({result}₁₀)" e.page.update() # Получаем пошаговое решение clean_val1 = val1.lstrip('-') if val1.startswith('-') else val1 clean_val2 = val2.lstrip('-') if val2.startswith('-') else val2 self.show_steps_div(clean_val1 if clean_val1 else "0", clean_val2 if clean_val2 else "0", Operation.DIVISION) except ValueError as ex: self.result_div.value = "Ошибка" self.decimal_div.value = str(ex) e.page.update() def test_data(self, e): # Создаем поля ввода для 50-ричных чисел # Явно перечисляем ВСЕ 50 допустимых символов в regex # 0-9 (10), A-Z (26), a-n (14) = 50 символов allowed_chars = r"^-?[0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn]*$" self.input_left_1 = ft.TextField( label="50-ричное число 1", width=250, text_align=ft.TextAlign.RIGHT, value="0", input_filter=ft.InputFilter(regex_string=allowed_chars), hint_text="Например: 1A (60₁₀) или Z (35₁₀) или a (36₁₀)" ) self.input_left_2 = ft.TextField( label="50-ричное число 2", width=250, text_align=ft.TextAlign.RIGHT, value="0", input_filter=ft.InputFilter(regex_string=allowed_chars), hint_text="Например: B (11₁₀) или n (49₁₀)" ) self.input_right_1 = ft.TextField( label="50-ричное число 1", width=250, text_align=ft.TextAlign.RIGHT, value="0", input_filter=ft.InputFilter(regex_string=allowed_chars), hint_text="Например: 1A (60₁₀) или c (38₁₀)" ) self.input_right_2 = ft.TextField( label="50-ричное число 2", width=250, text_align=ft.TextAlign.RIGHT, value="0", input_filter=ft.InputFilter(regex_string=allowed_chars), hint_text="Например: C (12₁₀) или k (46₁₀)" ) self.input_mult_1 = ft.TextField( label="50-ричное число 1", width=250, text_align=ft.TextAlign.RIGHT, value="0", input_filter=ft.InputFilter(regex_string=allowed_chars), hint_text="Например: A (10₁₀) или d (39₁₀)" ) self.input_mult_2 = ft.TextField( label="50-ричное число 2", width=250, text_align=ft.TextAlign.RIGHT, value="0", input_filter=ft.InputFilter(regex_string=allowed_chars), hint_text="Например: 5 (5₁₀) или B (11₁₀)" ) self.input_div_1 = ft.TextField( label="50-ричное число 1", width=250, text_align=ft.TextAlign.RIGHT, value="0", input_filter=ft.InputFilter(regex_string=allowed_chars), hint_text="Например: 14 (54₁₀) или Z (35₁₀)" ) self.input_div_2 = ft.TextField( label="50-ричное число 2", width=250, text_align=ft.TextAlign.RIGHT, value="0", input_filter=ft.InputFilter(regex_string=allowed_chars), hint_text="Например: 2 (2₁₀) или A (10₁₀)" ) right_column.controls = [ft.Container( bgcolor="#55257D", padding=ft.padding.all(10), border_radius=10, expand=True, content=ft.Column( scroll=ft.ScrollMode.AUTO, expand=True, controls=[ ft.Text("50-ричный калькулятор", size=20, color="white", weight=ft.FontWeight.BOLD), ft.Text("Система счисления с основанием 50", size=11, color="#B0B0B0", italic=True), ft.Text("Используйте: 0-9 (0-9), A-Z (10-35), a-n (36-49)", size=11, color="#B0B0B0"), ft.Text("Примеры: 10₍₅₀₎=50₁₀, A₍₅₀₎=10₁₀, Z₍₅₀₎=35₁₀, n₍₅₀₎=49₁₀", size=10, color="#90EE90"), ft.Row( expand=True, alignment=ft.MainAxisAlignment.CENTER, vertical_alignment=ft.CrossAxisAlignment.START, controls=[ ft.Column( expand=True, alignment=ft.MainAxisAlignment.START, horizontal_alignment=ft.CrossAxisAlignment.CENTER, controls=[ self.input_left_1, ft.Text("——— Вычитание", size=12, color="white"), self.input_left_2, ft.Container( content=ft.Column([ self.result_left, self.decimal_left ], horizontal_alignment=ft.CrossAxisAlignment.CENTER), bgcolor="#3A1E51", padding=10, border_radius=5, ), ft.CupertinoButton("=", on_click=self.calculate_subtraction), ft.Container( expand=True, content=self.steps_left, bgcolor="#1A0A2A", padding=10, border_radius=5, ) ] ), ft.Column( expand=True, alignment=ft.MainAxisAlignment.START, horizontal_alignment=ft.CrossAxisAlignment.CENTER, controls=[ self.input_right_1, ft.Text("—|— Сложение", size=12, color="white"), self.input_right_2, ft.Container( content=ft.Column([ self.result_right, self.decimal_right ], horizontal_alignment=ft.CrossAxisAlignment.CENTER), bgcolor="#3A1E51", padding=10, border_radius=5, ), ft.CupertinoButton("=", on_click=self.calculate_addition), ft.Container( expand=True, content=self.steps_right, bgcolor="#1A0A2A", padding=10, border_radius=5, ) ] ), ft.Column( expand=True, alignment=ft.MainAxisAlignment.START, horizontal_alignment=ft.CrossAxisAlignment.CENTER, controls=[ self.input_mult_1, ft.Text("——— Умножение", size=12, color="white"), self.input_mult_2, ft.Container( content=ft.Column([ self.result_mult, self.decimal_mult ], horizontal_alignment=ft.CrossAxisAlignment.CENTER), bgcolor="#3A1E51", padding=10, border_radius=5, ), ft.CupertinoButton("=", on_click=self.calculate_multiplication), ft.Container( expand=True, content=self.steps_mult, bgcolor="#1A0A2A", padding=10, border_radius=5, ) ] ), ft.Column( expand=True, alignment=ft.MainAxisAlignment.START, horizontal_alignment=ft.CrossAxisAlignment.CENTER, controls=[ self.input_div_1, ft.Text("——— Деление", size=12, color="white"), self.input_div_2, ft.Container( content=ft.Column([ self.result_div, self.decimal_div ], horizontal_alignment=ft.CrossAxisAlignment.CENTER), bgcolor="#3A1E51", padding=10, border_radius=5, ), ft.CupertinoButton("=", on_click=self.calculate_division), ft.Container( expand=True, content=self.steps_div, bgcolor="#1A0A2A", padding=10, border_radius=5, ) ] ) ] ) ], ) )] e.page.update()