Калькулятор на Python с GUI (Tkinter)

Python предоставляет множество инструментов для анализа данных, веб- и Android-разработки. Один из полезных инструментов для создания настольных приложений — библиотека Tkinter для графического интерфейса. Tkinter содержит виджеты для создания удобных интерфейсов. Эта библиотека проста в использовании, лёгкая и быстрая. Устанавливать Tkinter вручную обычно не требуется: он входит в стандартную поставку Python.
Важно знать базовые понятия пакета Tkinter перед тем, как продолжить практическую часть.
Базовая настройка приложения
Импорт модуля
Первый шаг — импорт модуля и создание главного объекта приложения. В исходном примере используется сокращённое имя модуля.
`import tkinter as ttk `
(Примечание: в реальных проектах чаще используют import tkinter as tk и from tkinter import ttk; в статье используется сокращение в варианте исходника.)
Создание окна
Чтобы создать окно, нужно создать объект окна и задать заголовок и геометрию (ширина×высота):
`win = ttk.Tk()
win.title('Simple Calculator')
win.geometry('500x500')
`
Gеометрия определяет размеры окна в пикселях.
Запуск главного цикла
Метод mainloop() запускает цикл обработки событий. Окно будет активно, пока пользователь не закроет его вручную.
`win.mainloop()
`
Создание интерфейса
Tkinter содержит множество виджетов. Для калькулятора в примере используются кнопки и поле ввода (Entry). Набор кнопок — цифры 0–9, арифметические операторы (+, -, ×, /), а также кнопки для очистки, удаления и вычисления.
Ниже — пример создания кнопок и их размещения с помощью grid:
`oneButton = ttk.Button(win, text="1", pady=10, padx=20, font = "Serif 15",bg = "black", fg = "white", command=lambda: addToEq(1))
oneButton.grid(row=2, column=0,padx=2, pady=3)
twoButton = ttk.Button(win, text="2", pady=10, padx=20, font = "Serif 15",bg = "black", fg = "white", command=lambda: addToEq(2))
twoButton.grid(row=2, column=1, padx=2, pady=3)
threeButton = ttk.Button(win, text="3", pady=10, padx=20, font = "Serif 15",bg = "black", fg = "white", command=lambda: addToEq(3))
threeButton.grid(row=2, column=2, padx=2, pady=3)
plusButton = ttk.Button(win, text="+", pady=10, padx=20, font = "Serif 15",bg = "black", fg = "white", command=lambda: addToEq("+"))
plusButton.grid(row=2, column=3, padx=2, pady=3)
# div 2
fourButton = ttk.Button(win, text="4", pady=10, padx=20, font = "Serif 15",bg = "black", fg = "white", command=lambda: addToEq(4))
fourButton.grid(row=3, column=0, padx=2, pady=3)
fiveButton = ttk.Button(win, text="5", pady=10, padx=20, font = "Serif 15",bg = "black", fg = "white", command=lambda: addToEq(5))
fiveButton.grid(row=3, column=1, padx=2, pady=3)
sixButton = ttk.Button(win, text="6", pady=10, padx=20, font = "Serif 15",bg = "black", fg = "white", command=lambda: addToEq(6))
sixButton.grid(row=3, column=2, padx=2, pady=3)
minusButton = ttk.Button(win, text="-", pady=10, padx=20, font = "Serif 15",bg = "black", fg = "white", command=lambda: addToEq("-"))
minusButton.grid(row=3, column=3, padx=2, pady=3)
# div 3
sevenButton = ttk.Button(win, text="7", pady=10, padx=20, font = "Serif 15",bg = "black", fg = "white", command=lambda: addToEq(7))
sevenButton.grid(row=4, column=0, padx=2, pady=3)
eightButton = ttk.Button(win, text="8", pady=10, padx=20, font = "Serif 15",bg = "black", fg = "white", command=lambda: addToEq(8))
eightButton.grid(row=4, column=1, padx=2, pady=3)
nineButton = ttk.Button(win, text="9", pady=10, padx=20, font = "Serif 15",bg = "black", fg = "white", command=lambda: addToEq(9))
nineButton.grid(row=4, column=2, padx=2, pady=3)
muxButton = ttk.Button(win, text="x", pady=10, padx=20, font = "Serif 15",bg = "black", fg = "white", command=lambda: addToEq("*"))
muxButton.grid(row=4, column=3, padx=2, pady=3)
#div 4
zeroButton = ttk.Button(win, text="0", pady=10, padx=20, font = "Serif 15",bg = "black", fg = "white", command=lambda: addToEq(0))
zeroButton.grid(row=5, column=0, padx=2, pady=3)
clearButton = ttk.Button(win, text="clr", pady=10, padx=20, font = "Serif 15",bg = "black", fg = "white", command=clearInput)
clearButton.grid(row=5, column=1, padx=2, pady=3)
calculateButton = ttk.Button(win, text="cal", pady=10, padx=20, font = "Serif 15",bg = "black", fg = "white", command=calculateEq)
calculateButton.grid(row=5, column=2, padx=2, pady=3)
divideButton = ttk.Button(win, text="/", pady=10, padx=20, font = "Serif 15",bg = "black", fg = "white", command=lambda: addToEq("/"))
divideButton.grid(row=5, column=3, padx=2, pady=3)
`
Параметры кнопки включают объект окна, выводимый текст, стиль шрифта и обработчик события (command). Для ровного расположения используйте менеджер геометрии grid и задавайте row и column.
Пример поля ввода и привязки переменной:
`numericEq = ttk.StringVar()
dataField = ttk.Entry(win, textvariable=numericEq, font="Serif 15")
dataField.grid(row=0,columnspan=3, ipadx=80, ipady=15)
`
Виджет Entry отображает текстовое поле. Аргумент textvariable связывает поле с переменной типа StringVar, позволяя программно считывать и записывать текст.
Добавление логики кнопкам
Кнопки реагируют через аргумент command. В примере используются лямбда-функции, чтобы передать значение в обработчик.
Цифровые и арифметические кнопки
Кнопки с цифрами и операторами вызывают функцию addToEq(). Она принимает значение кнопки и добавляет его к текущему выражению. В коде исходника представлен упрощённый пример:
`def addToEq(x):
calcValue = calcValue + str(x)
numericEq.set(calcValue)
`
Важно: calcValue должна быть доступна в области видимости функции (например, как глобальная переменная или атрибут объекта). Без этого код вызовет ошибку UnboundLocalError.
Кнопка вычисления
Кнопка “cal” вычисляет выражение, переданное в calcValue. В примере используется функция eval() для исполнения арифметики:
`def calculateEq():
total = str(eval(calcValue))
numericEq.set(total)
`
Мера предосторожности: eval() выполняет любой код, который в ней передан. В пользовательских приложениях используйте безопасный парсер выражений или фильтруйте ввод.
Кнопка очистки
Кнопка очистки ставит calcValue в пустую строку и обновляет поле ввода:
`def clearInput():
calcValue = ""
numericEq.set("")
`
Запуск и отладка
Сохраните файл, например calculator.py, и запустите: python calculator.py. При возникновении ошибок проверьте области видимости переменных, корректность имён функций и соответствие отступов.
Важно: если в проекте используются глобальные переменные (calcValue), объявите их как global внутри функций для записи, или лучше — инкапсулируйте логику в класс.
Полезные рекомендации и расширения
- Используйте tk.StringVar() только для связи с Entry. Для внутренней логики работайте со строками и числовыми типами.
- Для безопасных вычислений замените eval() на ast.literal_eval для простых типов или на специализированный парсер выражений.
- Рассмотрите клавиатурные привязки (bind) для ввода цифр с клавиатуры.
- Добавьте форматирование результата (например, округление float до 6 знаков).
Важно: для коммерческих приложений убедитесь, что ввод не может привести к выполнению нежелательного кода.
Типичные ошибки и как их исправить
- UnboundLocalError при обновлении calcValue: объявите переменную глобальной или храните в self.
- Деление на ноль: добавьте проверку перед вычислением.
- Неправильный формат числа: обработайте пробелы и локальные разделители (запятая/точка).
- Низкая отзывчивость интерфейса: избегайте тяжёлых вычислений в основном потоке; используйте отдельный поток/процесс для длительных задач.
Чек-лист разработчика
- Создать главное окно и настроить geometry и title
- Создать StringVar для поля ввода
- Реализовать кнопки 0–9 и операторы
- Реализовать функции addToEq, calculateEq, clearInput
- Обработать ошибки деления на ноль и неверного ввода
- Провести ручное тестирование клавиатуры и мыши
Критерии приёмки
- Приложение запускается без ошибок командой python file_name.py
- Кнопки добавляют соответствующие символы в поле ввода
- Нажатие “cal” корректно вычисляет выражение
- Кнопка очистки полностью сбрасывает поле ввода
- Обработка деления на ноль и других ошибок не приводит к падению приложения
Шаблон: минимальная структура (рекомендация)
- Импортировать модули
- Создать класс CalculatorApp
- В init создать окно, переменные, виджеты и бинды
- Написать методы addToEq, calculateEq, clearInput
- Вызвать mainloop()
Пример структуры:
class CalculatorApp:
def __init__(self):
# инициализация
pass
def addToEq(self, x):
pass
def calculateEq(self):
pass
def clearInput(self):
pass
if __name__ == '__main__':
app = CalculatorApp()
app.mainloop()Когда подход не подходит (контр-примеры)
- Нужен мощный научный калькулятор с выражениями, функциями и историей вычислений — лучше использовать специализированную библиотеку или фреймворк.
- Требуется сложная проверка пользовательского ввода и безопасность — избегайте eval(), используйте парсер выражений.
- Приложение должно масштабироваться и иметь сложный интерфейс — рассмотрите Qt (PyQt/PySide) или web‑интерфейс.
Краткая памятка (cheat sheet)
- grid(row=i, column=j) — позиционирование
- Entry(textvariable=var) — двусторонняя связь
- Button(command=handler) — обработчик клика
- ttk.StringVar() — связанная переменная
- eval() — быстро, но небезопасно
Глоссарий (1 строка)
- Tkinter — стандартная библиотека Python для создания графического интерфейса.
Риски и меры
- Риск: выполнение вредного кода через eval(). Мера: фильтрация ввода или использование безопасного парсера.
- Риск: некорректная локализация десятичных разделителей. Мера: нормализовать ввод (заменять “,” на “.”).
Короткое резюме
Вы создали базовый графический калькулятор на Tkinter: настроили окно, добавили виджеты и реализовали простую логику. Следующие шаги — улучшить обработку ошибок, заменить eval() на безопасный парсер и добавить дополнительные функции (клавиатурные бинды, история, форматирование результата).
Похожие материалы
OctoPrint на Android: установить и настроить
LAV Filters: установка и настройка
Бесплатная лицензия Windows 10 после смены оборудования
Редактирование скриншотов на Mac с Preview
Как разблокировать человека в Facebook — пошагово