Загрузка данных


import string
import kivy
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button

DIGITS = string.digits + string.ascii_uppercase


class BaseConverterApp(App):
    def build(self):
        self.title = "Конвертер систем счисления с арифметикой"

        # ТД-1: Хранилище состояния в оперативной памяти
        self.state = {
            "base": 10,
            "operand1": "",
            "operand2": "",
            "result": "",
            "last_error": ""
        }

        root = BoxLayout(orientation='vertical', padding=15, spacing=10)

        # Сетка полей ввода (ТИ-1)
        grid = GridLayout(cols=2, spacing=8, size_hint_y=None)
        grid.bind(minimum_height=grid.setter('height'))

        grid.add_widget(Label(text="Основание (2–36):", size_hint_y=None, height=35, halign='left'))
        self.input_base = TextInput(text="10", multiline=False, size_hint_y=None, height=35)
        grid.add_widget(self.input_base)

        grid.add_widget(Label(text="Операнд 1:", size_hint_y=None, height=35))
        self.input_op1 = TextInput(multiline=False, size_hint_y=None, height=35)
        grid.add_widget(self.input_op1)

        grid.add_widget(Label(text="Операнд 2:", size_hint_y=None, height=35))
        self.input_op2 = TextInput(multiline=False, size_hint_y=None, height=35)
        grid.add_widget(self.input_op2)

        grid.add_widget(Label(text="Десятичное число:", size_hint_y=None, height=35))
        self.input_dec = TextInput(multiline=False, size_hint_y=None, height=35)
        grid.add_widget(self.input_dec)

        root.add_widget(grid)

        # Панель кнопок с точными названиями по ТИ-1 и ТИ-4
        btn_layout = GridLayout(cols=4, spacing=8, size_hint_y=None, height=45)

        self.btn_convert = Button(text="Перевести")
        self.btn_convert.bind(on_press=self.handle_convert)
        btn_layout.add_widget(self.btn_convert)

        self.btn_add = Button(text="Сложить")
        self.btn_add.bind(on_press=self.handle_add)
        btn_layout.add_widget(self.btn_add)

        self.btn_multiply = Button(text="Умножить")
        self.btn_multiply.bind(on_press=self.handle_multiply)
        btn_layout.add_widget(self.btn_multiply)

        self.btn_from_dec = Button(text="Из десятичной")
        self.btn_from_dec.bind(on_press=self.handle_from_dec)
        btn_layout.add_widget(self.btn_from_dec)

        root.add_widget(btn_layout)

        # Поле вывода результата (ТИ-1, ТИ-2)
        root.add_widget(Label(text="Результат:", size_hint_y=None, height=25, halign='left'))
        self.txt_result = TextInput(
            readonly=True,
            multiline=True,
            size_hint_y=0.4,
            background_color=(0.95, 0.95, 0.95, 1)
        )
        root.add_widget(self.txt_result)

        # Текстовое поле сообщений об ошибках (ТИ-1, ТИ-3, НТ-2)
        root.add_widget(Label(text="Сообщения об ошибках:", size_hint_y=None, height=25, halign='left'))
        self.txt_error = TextInput(
            readonly=True,
            multiline=True,
            size_hint_y=0.3,
            foreground_color=(0.85, 0.1, 0.1, 1),
            background_color=(0.98, 0.95, 0.95, 1)
        )
        root.add_widget(self.txt_error)

        return root

    def set_error(self, err_msg):
        """Регистрирует ошибку и обновляет интерфейс согласно ТД-1 и ТИ-3."""
        self.state["last_error"] = err_msg
        self.txt_error.text = err_msg

    def set_success(self, result_text):
        """Фиксирует успешный результат согласно ТД-1 и ТИ-2."""
        self.state["result"] = result_text
        self.state["last_error"] = ""
        self.txt_result.text = result_text
        self.txt_error.text = ""

    def validate_and_get_base(self):
        """Проверка основания по БП-1 и ФТ-8."""
        raw_base = self.input_base.text.strip()
        if not raw_base:
            self.set_error("Ошибка 3: основание должно быть целым числом от 2 до 36")
            return None
        try:
            base_val = int(raw_base)
            if not (2 <= base_val <= 36):
                self.set_error("Ошибка 3: основание должно быть целым числом от 2 до 36")
                return None
            return base_val
        except ValueError:
            self.set_error("Ошибка 3: основание должно быть целым числом от 2 до 36")
            return None

    def validate_operand(self, op_text, base_val):
        """Проверка операнда по БП-2, БП-3, ФТ-2 и ФТ-7."""
        cleaned = op_text.strip().upper()
        if not cleaned:
            # Пустая строка — ошибка допустимости цифр/ввода
            max_char = DIGITS[base_val - 1]
            self.set_error(f"Ошибка 2: цифра  недопустима для основания {base_val}. Допустимый диапазон: 0–{max_char}")
            return None

        allowed = DIGITS[:base_val]
        for ch in cleaned:
            if ch not in allowed:
                max_char = DIGITS[base_val - 1]
                self.set_error(
                    f"Ошибка 2: цифра {ch} недопустима для основания {base_val}. Допустимый диапазон: 0–{max_char}")
                return None
        return cleaned

    def int_to_base(self, num, base):
        """Перевод целого неотрицательного числа в целевое основание."""
        if num == 0:
            return "0"
        digits = []
        while num > 0:
            digits.append(DIGITS[num % base])
            num //= base
        return "".join(reversed(digits))

    def update_operands_state(self):
        """Сохраняет операнды как есть при выполнении операций (ТД-1)."""
        self.state["operand1"] = self.input_op1.text
        self.state["operand2"] = self.input_op2.text

    def handle_convert(self, instance):
        """Операция перевода операндов в десятичную систему (ФТ-3, ТИ-2)."""
        self.update_operands_state()
        base = self.validate_and_get_base()
        if base is None:
            return
        self.state["base"] = base

        op1 = self.validate_operand(self.input_op1.text, base)
        if op1 is None:
            return

        dec1 = int(op1, base)
        res_str = f"Операнд 1: {dec1} (основание 10)"

        op2_raw = self.input_op2.text.strip()
        if op2_raw:
            op2 = self.validate_operand(op2_raw, base)
            if op2 is None:
                return
            dec2 = int(op2, base)
            res_str += f"\nОперанд 2: {dec2} (основание 10)"

        self.set_success(res_str)

    def handle_add(self, instance):
        """Операция сложения операндов (ФТ-4, БП-4, ТИ-2)."""
        self.update_operands_state()
        base = self.validate_and_get_base()
        if base is None:
            return
        self.state["base"] = base

        op1 = self.validate_operand(self.input_op1.text, base)
        if op1 is None:
            return

        op2 = self.validate_operand(self.input_op2.text, base)
        if op2 is None:
            return

        sum_dec = int(op1, base) + int(op2, base)
        result_in_base = self.int_to_base(sum_dec, base)
        self.set_success(f"{result_in_base} (основание {base})")

    def handle_multiply(self, instance):
        """Операция умножения операндов (ФТ-5, БП-4, ТИ-2)."""
        self.update_operands_state()
        base = self.validate_and_get_base()
        if base is None:
            return
        self.state["base"] = base

        op1 = self.validate_operand(self.input_op1.text, base)
        if op1 is None:
            return

        op2 = self.validate_operand(self.input_op2.text, base)
        if op2 is None:
            return

        prod_dec = int(op1, base) * int(op2, base)
        result_in_base = self.int_to_base(prod_dec, base)
        self.set_success(f"{result_in_base} (основание {base})")

    def handle_from_dec(self, instance):
        """Обратный перевод из десятичной системы (ФТ-6, ТИ-2)."""
        base = self.validate_and_get_base()
        if base is None:
            return
        self.state["base"] = base

        raw_dec = self.input_dec.text.strip()
        if not raw_dec or not raw_dec.isdigit():
            self.set_error("Ошибка 1: введите целое неотрицательное число")
            return

        dec_val = int(raw_dec)
        res_in_base = self.int_to_base(dec_val, base)
        self.set_success(f"{res_in_base} (основание {base})")


if __name__ == '__main__':
    BaseConverterApp().run()