This commit is contained in:
2025-11-08 11:16:24 +05:00
parent 81c60f2c70
commit 760fb43e62
16 changed files with 2798 additions and 257 deletions

View File

@@ -0,0 +1,421 @@
from designer import right_column
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.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.input_left_1 = None
self.input_left_2 = None
# Поля ввода для сложения
self.input_right_1 = None
self.input_right_2 = None
# Контейнеры для пошагового решения
self.steps_left = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
self.steps_right = 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):
"""Отображает пошаговое решение для левой колонки (вычитание)"""
try:
# Получаем шаги решения
steps = self.solver.solve(num1, num2, operation)
# Очищаем предыдущие шаги
self.steps_left.controls.clear()
# Добавляем заголовок
self.steps_left.controls.append(
ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700")
)
# Добавляем каждый шаг
for i, step in enumerate(steps, 1):
step_content = []
# Название шага
step_content.append(
ft.Text(f"Шаг {i}: {step.title}",
size=12, weight=ft.FontWeight.BOLD, color="#00FFFF")
)
# Описание
step_content.append(
ft.Text(step.description, size=10, color="#E0E0E0")
)
# Визуализация (если есть)
if step.visual:
step_content.append(
ft.Text(step.visual,
size=10,
font_family="Courier New",
color="#90EE90",
selectable=True)
)
# Результат (если есть)
if step.result:
step_content.append(
ft.Text(f"{step.result}",
size=11,
weight=ft.FontWeight.BOLD,
color="#00FF00")
)
# Добавляем контейнер с шагом
self.steps_left.controls.append(
ft.Container(
content=ft.Column(step_content, spacing=3),
bgcolor="#2A1A3A",
padding=8,
border_radius=5,
border=ft.border.all(1, "#4A3A5A")
)
)
self.steps_left.update()
except Exception as ex:
self.steps_left.controls.clear()
self.steps_left.controls.append(
ft.Text(f"Ошибка: {str(ex)}", color="red", size=10)
)
self.steps_left.update()
def show_steps_right(self, num1: str, num2: str, operation: Operation):
"""Отображает пошаговое решение для правой колонки (сложение)"""
try:
# Получаем шаги решения
steps = self.solver.solve(num1, num2, operation)
# Очищаем предыдущие шаги
self.steps_right.controls.clear()
# Добавляем заголовок
self.steps_right.controls.append(
ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700")
)
# Добавляем каждый шаг
for i, step in enumerate(steps, 1):
step_content = []
# Название шага
step_content.append(
ft.Text(f"Шаг {i}: {step.title}",
size=12, weight=ft.FontWeight.BOLD, color="#00FFFF")
)
# Описание
step_content.append(
ft.Text(step.description, size=10, color="#E0E0E0")
)
# Визуализация (если есть)
if step.visual:
step_content.append(
ft.Text(step.visual,
size=10,
font_family="Courier New",
color="#90EE90",
selectable=True)
)
# Результат (если есть)
if step.result:
step_content.append(
ft.Text(f"{step.result}",
size=11,
weight=ft.FontWeight.BOLD,
color="#00FF00")
)
# Добавляем контейнер с шагом
self.steps_right.controls.append(
ft.Container(
content=ft.Column(step_content, spacing=3),
bgcolor="#2A1A3A",
padding=8,
border_radius=5,
border=ft.border.all(1, "#4A3A5A")
)
)
self.steps_right.update()
except Exception as ex:
self.steps_right.controls.clear()
self.steps_right.controls.append(
ft.Text(f"Ошибка: {str(ex)}", color="red", size=10)
)
self.steps_right.update()
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 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₁₀)"
)
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,
)
]
)
]
)
],
)
)]
e.page.update()