Варианты калькулятора на tkinter: от базового до продвинутого
Калькулятор на tkinter: варианты реализации
Основное решение представляет собой простой калькулятор с графическим интерфейсом на tkinter, поддерживающий четыре базовых арифметических действия. Используется функция eval для вычисления введённого выражения. Несмотря на удобство, такой подход небезопасен для приложений, работающих с пользовательским вводом из непроверенных источников. Для учебных проектов и прототипов этого обычно достаточно.
import tkinter as tk
def calculate():
try:
result = eval(entry.get())
label.config(text="Результат: " + str(result))
except Exception as e:
label.config(text="Ошибка: " + str(e))
root = tk.Tk()
root.title("Простой калькулятор")
entry = tk.Entry(root, width=30)
entry.pack(pady=10)
btn = tk.Button(root, text="Вычислить", command=calculate)
btn.pack()
label = tk.Label(root, text="Введите выражение")
label.pack()
root.mainloop()вывод окна python (создание окна с выводом в python)
Пользователь вводит строку, например 2+3*4, нажимает кнопку, и программа выводит результат. При ошибках (деление на ноль, неверный синтаксис) выводится сообщение.
Типичные проблемы:
- Использование eval позволяет выполнять произвольный код, что может быть опасно при работе с внешними данными.
- Отсутствует проверка корректности выражения: ввод букв приводит к ошибке NameError.
- Нет возможности очистить поле ввода после расчёта.
Цель использования: Быстрая демонстрация возможностей tkinter и базового взаимодействия с элементами управления. Подходит для обучения или личного использования, когда вопросы безопасности не критичны.
Как сделать калькулятор с кнопками для каждого символа, а не только с полем ввода?
Решение с кнопками даёт более привычный интерфейс. Каждое нажатие добавляет символ в поле ввода.
import tkinter as tk
def press(key):
entry.insert(tk.END, key)
def equal():
try:
result = eval(entry.get())
entry.delete(0, tk.END)
entry.insert(tk.END, str(result))
except:
entry.delete(0, tk.END)
entry.insert(tk.END, "Ошибка")
def clear():
entry.delete(0, tk.END)
root = tk.Tk()
root.title("Калькулятор с кнопками")
entry = tk.Entry(root, width=30, borderwidth=5)
entry.grid(row=0, column=0, columnspan=4, padx=10, pady=10)
buttons = [
('1',1,0), ('2',1,1), ('3',1,2), ('+',1,3),
('4',2,0), ('5',2,1), ('6',2,2), ('-',2,3),
('7',3,0), ('8',3,1), ('9',3,2), ('*',3,3),
('C',4,0), ('0',4,1), ('=',4,2), ('/',4,3)
]
for (text, row, col) in buttons:
if text == '=':
btn = tk.Button(root, text=text, command=equal, width=5)
elif text == 'C':
btn = tk.Button(root, text=text, command=clear, width=5)
else:
btn = tk.Button(root, text=text, command=lambda t=text: press(t), width=5)
btn.grid(row=row, column=col, padx=2, pady=2)
root.mainloop()открыть окно python (открыть окно на python)
Теперь пользователь вводит выражение, нажимая кнопки. Кнопка = вычисляет результат, C очищает поле.
Проблемы:
- Всё так же используется eval, что небезопасно.
- После вычисления результат отображается в поле ввода, что может быть неудобно для редактирования.
Цель: Создание интуитивно понятного интерфейса, напоминающего настольные калькуляторы. Подходит для пользователей, предпочитающих ввод мышью.
Как реализовать безопасный калькулятор без eval?
Вместо eval можно написать парсер, который разбирает строку и вычисляет значение по правилам математики. Ниже приведён упрощённый алгоритм с использованием обратной польской записи (ОПЗ).
import operator, re
def to_postfix(expression):
"""Преобразует инфиксное выражение в ОПЗ"""
precedence = {'+':1, '-':1, '*':2, '/':2}
output = []
stack = []
tokens = re.findall(r'\d+\.?\d*|[+\-*/()]', expression)
for token in tokens:
if token.isdigit() or '.' in token:
output.append(token)
elif token == '(':
stack.append(token)
elif token == ')':
while stack and stack[-1] != '(':
output.append(stack.pop())
stack.pop()
else: # оператор
while stack and stack[-1] != '(' and precedence.get(stack[-1], 0) >= precedence[token]:
output.append(stack.pop())
stack.append(token)
while stack:
output.append(stack.pop())
return output
def evaluate_postfix(postfix):
"""Вычисляет значение постфиксного выражения"""
ops = {
'+': operator.add,
'-': operator.sub,
'*': operator.mul,
'/': operator.truediv
}
stack = []
for token in postfix:
if token in ops:
b = stack.pop()
a = stack.pop()
stack.append(ops[token](a, b))
else:
stack.append(float(token))
return stack[0]
def safe_calc(expression):
# удалим все лишние пробелы и проверим на недопустимые символы
expression = expression.replace(' ', '')
if not re.match(r'^[\d+\-*/().]+$', expression):
raise ValueError("Недопустимые символы")
postfix = to_postfix(expression)
return evaluate_postfix(postfix)
# Пример использования
print(safe_calc("2+3*4")) # 14.0
Python окно (создание окон в python tkinter)
В tkinter остаётся только заменить вызов eval на вызов safe_calc. Это исключает выполнение произвольного кода.
Проблемы:
- Парсер не обрабатывает унарный минус (например, -5+3).
- Не поддерживаются скобки вложенной глубины более одного уровня? Нет, поддерживаются.
- Требуется дополнительная обработка десятичных разделителей (запятая вместо точки).
Цель: Безопасный для внешнего ввода калькулятор. Применяется в проектах, где пользователь может вводить данные произвольного вида.
Как добавить функции научного калькулятора (тригонометрия, возведение в степень)?
Расширить функциональность можно, либо используя eval с добавлением допустимых функций (sin, cos, log и т.д.), либо написав расширенный парсер. Если не требуется высокая безопасность, удобно использовать eval с ограниченной областью видимости.
import math
def safe_eval(expression):
allowed_names = {
k: v for k, v in math.__dict__.items() if not k.startswith('__')
}
allowed_names["abs"] = abs
# можно добавить свои функции
try:
return eval(expression, {"__builtins__": {}}, allowed_names)
except Exception as e:
return f"Ошибка: {e}"
# Примеры
print(safe_eval("sin(pi/2)")) # 1.0
print(safe_eval("2**10")) # 1024Python tkinter canvas (холст canvas в tkinter)
Интерфейс tkinter можно дополнить кнопками для функций: sin, cos, log, sqrt и т.д. При нажатии они вставляют соответствующую строку, например sin(.
Проблемы:
- Использование eval может быть подвержено атакам, если злоумышленник найдёт способ обойти ограничения.
- Необходимость экранировать специальные символы (например, ** для возведения в степень нужно заменять на ^ и наоборот).
Цель: Инженерные расчёты. Подходит для студентов и специалистов, которым нужны расширенные математические операции.
Как сделать калькулятор с сохранением истории вычислений?
Добавляется текстовый виджет или Listbox, куждая записывается каждое выполненное выражение и результат. При запуске история может загружаться из файла.
import tkinter as tk
from tkinter import scrolledtext
history = []
def calculate():
expr = entry.get()
try:
result = eval(expr)
history.append(f"{expr} = {result}")
history_box.insert(tk.END, history[-1])
entry.delete(0, tk.END)
label.config(text=f"Результат: {result}")
except Exception as e:
label.config(text=f"Ошибка: {e}")
root = tk.Tk()
root.title("Калькулятор с историей")
entry = tk.Entry(root, width=30)
entry.pack(pady=5)
btn = tk.Button(root, text="Вычислить", command=calculate)
btn.pack()
label = tk.Label(root, text="")
label.pack()
history_box = scrolledtext.ScrolledText(root, width=40, height=10)
history_box.pack(pady=5)
root.mainloop()
При каждом нажатии кнопки запись добавляется в список history и в виджет. Можно добавить кнопку очистки истории или сохранения в файл.
Проблемы:
- История хранится только в памяти; при закрытии программы теряется.
- Длинная история может замедлить работу интерфейса при большом количестве записей.
Цель: Анализ предыдущих вычислений. Удобно при повторяющихся расчётах или для проверки последовательности операций.
Расширенные примеры реализации калькулятора
Пример 1: Калькулятор с поддержкой клавиатурного ввода
Пользователь может набирать выражение на клавиатуре, а нажатие Enter запускает расчёт. Дополнительно привязываются другие клавиши: Escape для очистки.
import tkinter as tk
def calculate(event=None):
try:
result = eval(entry.get())
entry.delete(0, tk.END)
entry.insert(0, str(result))
except:
entry.delete(0, tk.END)
entry.insert(0, "Ошибка")
def clear(event=None):
entry.delete(0, tk.END)
root = tk.Tk()
root.title("Калькулятор с клавиатурой")
entry = tk.Entry(root, width=30)
entry.pack()
entry.bind('', calculate)
entry.bind('', clear)
root.mainloop()
Окно с полем ввода. После ввода 45+55 и нажатия Enter поле показывает 100. После Escape поле очищается.
Пример 2: Калькулятор с изменяемой цветовой схемой (темизация)
Используется библиотека ttkbootstrap для современных тем. После установки (pip install ttkbootstrap) можно быстро переключить внешний вид.
import tkinter as tk
from ttkbootstrap import Style
style = Style(theme='darkly') # или 'flatly', 'superhero', 'minty'
root = style.master
root.title("Тёмный калькулятор")
entry = tk.Entry(root, width=30, font=('Arial', 14))
entry.pack(pady=10)
def calc():
try:
result = eval(entry.get())
entry.delete(0, tk.END)
entry.insert(0, str(result))
except:
entry.delete(0, tk.END)
entry.insert(0, "Ошибка")
btn = tk.Button(root, text="Вычислить", command=calc, bootstyle='success')
btn.pack()
root.mainloop()
Окно с тёмной темой: тёмный фон, зелёная кнопка, светлый текст в поле ввода. Внешний вид можно изменить простой заменой параметра theme.
Пример 3: Калькулятор с дробными числами и десятичным разделителем
Поддержка точек и запятых. Внутренне запятая заменяется на точку для корректного вычисления.
import tkinter as tk
def calculate(event=None):
expr = entry.get().replace(',', '.')
try:
result = eval(expr)
result_str = str(result).replace('.', ',')
entry.delete(0, tk.END)
entry.insert(0, result_str)
except:
entry.delete(0, tk.END)
entry.insert(0, "Ошибка")
root = tk.Tk()
root.title("Калькулятор с запятой")
entry = tk.Entry(root, width=30)
entry.pack()
entry.bind('', calculate)
root.mainloop()
После ввода 2,5+3,5 и нажатия Enter отображается 6,0.
Пример 4: Калькулятор с отображением пошагового решения (для демонстрации)
Показывает промежуточные результаты при нажатии каждой кнопки операции. Используется список и временные переменные.
import tkinter as tk
class StepCalc:
def __init__(self, root):
self.root = root
self.root.title("Пошаговый калькулятор")
self.total = 0
self.current = ''
self.operation = None
self.steps = []
self.entry = tk.Entry(root, width=30)
self.entry.grid(row=0, column=0, columnspan=4)
self.steps_text = tk.Text(root, height=5, width=40)
self.steps_text.grid(row=1, column=0, columnspan=4)
buttons = [
('1',1,0), ('2',1,1), ('3',1,2), ('+',1,3),
('4',2,0), ('5',2,1), ('6',2,2), ('-',2,3),
('7',3,0), ('8',3,1), ('9',3,2), ('*',3,3),
('C',4,0), ('0',4,1), ('=',4,2), ('/',4,3)
]
for (text, row, col) in buttons:
if text == '=':
btn = tk.Button(root, text=text, command=self.result)
elif text == 'C':
btn = tk.Button(root, text=text, command=self.clear)
else:
btn = tk.Button(root, text=text, command=lambda t=text: self.click(t))
btn.grid(row=row, column=col, padx=2, pady=2)
def click(self, key):
if key.isdigit() or key == '.':
self.entry.insert(tk.END, key)
else:
self.current = self.entry.get()
if self.current:
self.steps.append(f"Текущее: {self.current}")
self.operation = key
self.entry.delete(0, tk.END)
self.steps.append(f"Операция: {key}")
self.update_steps()
def result(self):
self.current = self.entry.get()
if self.current and self.operation:
try:
expr = f"{self.total} {self.operation} {self.current}"
self.total = eval(expr)
self.steps.append(f"Результат: {expr} = {self.total}")
self.entry.delete(0, tk.END)
self.entry.insert(0, str(self.total))
self.operation = None
self.update_steps()
except:
self.entry.delete(0, tk.END)
self.entry.insert(0, "Ошибка")
def clear(self):
self.total = 0
self.current = ''
self.operation = None
self.steps.clear()
self.entry.delete(0, tk.END)
self.update_steps()
def update_steps(self):
self.steps_text.delete('1.0', tk.END)
for step in self.steps[-5:]: # показываем последние 5 шагов
self.steps_text.insert(tk.END, step + '\n')
root = tk.Tk()
app = StepCalc(root)
root.mainloop()
Пользователь вводит 5, нажимает +, вводит 3, нажимает =. В текстовом поле появляются шаги: "Текущее: 5", "Операция: +", "Результат: 5 + 3 = 8".