Калькулятор можно собрать на встроенном Tkinter без внешних библиотек и без eval. Вы введёте два числа, выберете действие и получите ответ в окне. Ошибка ввода и деление на ноль не уронят программу.
Учебный калькулятор хорош тем, что здесь встречаются сразу три слоя: математика, проверка ввода и интерфейс. Если смешать их в одной функции, код быстро превратится в лес из if. Мы отделим функцию calculate от окна, поэтому её можно проверить без кликов.
Превращаем два поля в float и ловим ошибку ввода.
Сопоставляем знак с функцией из operator, не используем eval.
Обновляем одну метку и оставляем окно готовым к следующему расчёту.
Из каких частей состоит калькулятор
В окне будут два Entry, Combobox со знаками операций, кнопка и Label для ответа. Нажатие не должно знать, как именно считается сумма или частное. Кнопка только передаёт данные функции и показывает ответ.
| Часть | Объект | Задача |
|---|---|---|
| Ввод | ttk.Entry | Хранит текст до проверки |
| Операция | ttk.Combobox | Ограничивает выбор четырьмя знаками |
| Расчёт | calculate() | Возвращает число или понятную ошибку |
| Вывод | StringVar | Меняет текст без пересоздания Label |
Граница проходит между StringVar и calculate: до неё лежит пользовательский текст, после неё только числа и известная операция.

От текста к ответу. Ошибка перехватывается до обновления Label
Пишем расчёт и окно Tkinter
Модуль operator даёт готовые функции add, sub, mul и truediv. Мы свяжем их со знаками в словаре. Это убирает цепочку условий и не позволяет выполнить произвольную строку как Python-код.
import operator
import tkinter as tk
from tkinter import ttk
OPERATIONS = {
"+": operator.add,
"-": operator.sub,
"×": operator.mul,
"÷": operator.truediv,
}
def calculate(left: str, right: str, sign: str) -> float:
a = float(left.replace(",", "."))
b = float(right.replace(",", "."))
if sign == "÷" and b == 0:
raise ValueError("На ноль делить нельзя")
return OPERATIONS[sign](a, b)
root = tk.Tk()
root.title("Калькулятор")
root.resizable(False, False)
left = tk.StringVar(value="12")
right = tk.StringVar(value="3")
sign = tk.StringVar(value="+")
answer = tk.StringVar(value="Ответ появится здесь")
frame = ttk.Frame(root, padding=24)
frame.grid()
ttk.Entry(frame, textvariable=left, width=12).grid(row=0, column=0)
ttk.Combobox(frame, textvariable=sign, values=list(OPERATIONS),
state="readonly", width=4).grid(row=0, column=1, padx=8)
ttk.Entry(frame, textvariable=right, width=12).grid(row=0, column=2)
def show_result() -> None:
try:
value = calculate(left.get(), right.get(), sign.get())
answer.set(f"Результат: {value:g}")
except (ValueError, KeyError):
answer.set("Проверьте числа и операцию")
ttk.Button(frame, text="Посчитать", command=show_result).grid(
row=1, column=0, columnspan=3, sticky="ew", pady=16
)
ttk.Label(frame, textvariable=answer).grid(row=2, column=0, columnspan=3)
root.mainloop()Сумма, разность и произведение считаются одной функцией. Запятая в дроби заменяется на точку до float. Деление на ноль даёт текст в окне, а не traceback. Кнопка работает повторно без перезапуска.

Для четырёх операций выбор однозначен. Сравнение показывает, какой вариант легче поддерживать и проверять.
Почему здесь не нужен eval
Функция eval превращает строку в выполняемое выражение. Для учебного калькулятора это лишнее и опасное расширение возможностей. Нам известны ровно четыре допустимых действия, поэтому словарь точнее описывает правила программы.
| Ситуация | Что происходит | Что видит пользователь |
|---|---|---|
| 12 + 3 | Вызов operator.add | Результат: 15 |
| 2,5 × 4 | Замена запятой, затем mul | Результат: 10 |
| 10 ÷ 0 | Явная проверка до truediv | На ноль делить нельзя |
| abc + 1 | float вызывает ValueError | Проверьте числа |
Обрабатывайте ошибку там, где есть понятный способ её показать. calculate может выбросить ValueError, а show_result переводит его в короткую фразу в Label. Так ядро остаётся удобным для тестов, а интерфейс не показывает внутренние детали Python.
Практика: проверяем границы
Не ограничивайтесь примером 2 + 2. Полезные тесы находят границы: ноль, отрицательное число, дробь с запятой, пустое поле и многократное нажатие. Важен не один красивый кадр, а стабильная работа всех четырёх операций.
- Запустите
python calculator.pyи посчитайте 12 + 3. - Проверьте вычитание и умножение с отрицательным числом.
- Введите
2,5и4, выберите умножение. - Поделите 10 на 0 и убедитесь, что окно не закрылось.
- Очистите одно поле и нажмите кнопку ещё раз.
- Измените начальные значения StringVar и перезапустите файл.
Все четыре операции возвращают ожидаемые числа. Неверный ввод и ноль в знаменателе не закрывают окно. После ошибки можно исправить поле и сразу посчитать снова. Код не выполняет текст пользователя через eval.

Проверьте их до оформления. Пройдите все четыре сценария перед публикацией проекта.
Доводим калькулятор до готового вида
Визуальный слой можно улучшать после того, как расчёт стал надёжным. Добавьте отступы, фокус на первом поле и клавишу Enter. Не пытайтесь сразу повторить системный калькулятор с памятью и скобками. Текущий проект уже закрывает главную учебную цель: данные проходят от интерфейса к чистой функции и обратно.
Программа получает лишнюю возможность выполнять текст. Оставьте явный набор операций.
Широкий except скроет опечатку в коде. Ловите только ValueError и KeyError.
Ноль во втором поле должен дать понятное сообщение.
Вынесите расчёт в calculate, иначе его сложно проверять.
Что получится в итоге

Готовое окно показывает два числа, выбор операции, кнопку и ответ без лишних экранов.
Нужно ли ставить Tkinter через pip?
Обычно нет. Tkinter входит в стандартную библиотеку Python, хотя в некоторых Linux-дистрибутивах GUI-пакет ставят через системный менеджер.
Почему не используем eval?
Нам нужны четыре заранее известные операции. Словарь функций точнее и безопаснее.
Как принять дробь с запятой?
Заменить запятую на точку перед вызовом float.
Как понять, что проект готов?
Он повторяет целевой сценарий несколько раз подряд, понятно реагирует на ошибочный ввод и не теряет состояние при обычных действиях.
Можно ли копировать код целиком?
Можно как стартовую точку, но сначала замените значения из примера, затем запустите проект по шагам. Если вы не можете объяснить одну строку, её лучше разобрать до следующего шага.
Функции, словари и исключения можно закрепить в курсе Python. Если строка и число снова конфликтуют, откройте разбор TypeError.
Следующий проект можно взять из генератора паролей или игры камень, ножницы, бумага. В разделе «Практика» Кодика такой файл можно запускать прямо с телефона.