import tkinter as tk
from tkinter import messagebox, ttk
import math
import logging
import json
import os
from typing import Union

# Настройка логирования
logging.basicConfig(
filename='calculator.log',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)

HISTORY_FILE = 'history.json'
LOCALE_FILES = {
'ru': 'locales/ru.json',
'en': 'locales/en.json'
}
current_locale = 'ru'

def load_locale(locale_file: str) -> dict:
try:
with open(locale_file, 'r', encoding='utf-8-sig') as f:
return json.load(f)
except FileNotFoundError:
logging.error(f"Файл локализации не найден: {locale_file}")
return {}
except json.JSONDecodeError as e:
logging.error(f"Ошибка парсинга файла локализации: {locale_file} - {e}")
return {}

locale = load_locale(LOCALE_FILES[current_locale])

def translate(key: str) -> str:
return locale.get(key, key)

def add(x: float, y: float) -> float:
return x + y

def subtract(x: float, y: float) -> float:
return x - y

def multiply(x: float, y: float) -> float:
return x * y

def divide(x: float, y: float) -> float:
if y == 0:
raise ValueError(translate("На ноль делить нельзя"))
return x / y

def power(x: float, y: float) -> float:
return x ** y

def sqrt(x: float) -> float:
if x < 0:
raise ValueError(translate("Нельзя вычислить квадратный корень из отрицательного числа"))
return math.sqrt(x)

def is_valid_number(entry: str) -> bool:
try:
float(entry)
return True
except ValueError:
return False

def save_history() -> None:
try:
with open(HISTORY_FILE, 'w', encoding='utf-8') as f:
history = history_text.get("1.0", tk.END).strip()
json.dump(history.split('\n'), f, ensure_ascii=False, indent=4)
logging.info(translate("История операций сохранена"))
except Exception as e:
logging.error(f"Ошибка при сохранении истории: {e}")

def load_history() -> None:
if os.path.exists(HISTORY_FILE):
try:
with open(HISTORY_FILE, 'r', encoding='utf-8') as f:
history = json.load(f)
history_text.delete('1.0', tk.END) # Очистка виджета перед загрузкой
for entry in history:
history_text.insert(tk.END, entry + '\n')
history_text.see(tk.END)
logging.info(translate("История операций загружена"))
except Exception as e:
logging.error(f"Ошибка при загрузке истории: {e}")

def calculate(num1: float, num2: Union[float, None], operation: str) -> float:
if operation == translate("Сложение"):
return add(num1, num2)
elif operation == translate("Вычитание"):
return subtract(num1, num2)
elif operation == translate("Умножение"):
return multiply(num1, num2)
elif operation == translate("Деление"):
return divide(num1, num2)
elif operation == translate("Возведение в степень"):
return power(num1, num2)
elif operation == translate("Квадратный корень"):
return sqrt(num1)
else:
raise ValueError(translate("Неверная операция"))

def perform_operation(event: tk.Event = None) -> None:
try:
if not is_valid_number(entry1.get()):
raise ValueError(translate("Первое число должно быть числом"))
num1 = float(entry1.get())

operation = selected_operation.get()

if operation == translate("Квадратный корень"):
result = calculate(num1, None, operation)
num2 = None # Для логирования и истории
entry2.delete(0, tk.END)
else:
if not is_valid_number(entry2.get()):
raise ValueError(translate("Второе число должно быть числом"))
num2 = float(entry2.get())
result = calculate(num1, num2, operation)

result_label.config(text=f"{translate('Результат')}: {result}")

# Форматирование записи для истории
if operation == translate("Квадратный корень"):
history_entry = f"{operation}: √{num1} = {result}"
else:
history_entry = f"{operation}: {num1} {operation_symbol(operation)} {num2} = {result}"

history_text.insert(tk.END, history_entry + '\n')
history_text.see(tk.END)
save_history()
logging.info(f"{translate('Выполнена операция')}: {operation} {translate('с числами')} {num1} и {num2 if num2 is not None else ''}. {translate('Результат')}: {result}")
except ValueError as e:
messagebox.showerror(translate("Ошибка"), str(e))
logging.error(f"{translate('Ошибка ввода данных')}: {str(e)}")
except Exception as e:
messagebox.showerror(translate("Ошибка"), translate("Неверный ввод данных"))
logging.error(f"{translate('Непредвиденная ошибка')}: {str(e)}")

def operation_symbol(operation: str) -> str:
symbols = {
translate("Сложение"): '+',
translate("Вычитание"): '-',
translate("Умножение"): '*',
translate("Деление"): '/',
translate("Возведение в степень"): '^',
}
return symbols.get(operation, '')

def clear_entries(event: tk.Event = None) -> None:
entry1.delete(0, tk.END)
entry2.delete(0, tk.END)
result_label.config(text=f"{translate('Результат')}: ")

def on_operation_change(*args) -> None:
if selected_operation.get() == translate("Квадратный корень"):
entry2.config(state=tk.DISABLED)
else:
entry2.config(state=tk.NORMAL)

def show_info(event: tk.Event = None) -> None:
messagebox.showinfo(translate("О калькуляторе"), translate("Этот калькулятор выполнен на Python с использованием библиотеки Tkinter."))

def show_shortcuts(event: tk.Event = None) -> None:
messagebox.showinfo(
translate("Клавиатурные шорткаты"),
translate("Ctrl+C: Скопировать результат\nCtrl+I: О приложении\nCtrl+Q: Выход\nCtrl+K: Клавиатурные шорткаты")
)

def exit_app(event: tk.Event = None) -> None:
save_history()
root.destroy()

def copy_result(event: tk.Event = None) -> None:
result_text = result_label.cget("text")
if result_text != f"{translate('Результат')}: ":
root.clipboard_clear()
root.clipboard_append(result_label.cget("text").split(": ")[1])
messagebox.showinfo(translate("Скопировано"), translate("Результат скопирован в буфер обмена."))
else:
messagebox.showwarning(translate("Предупреждение"), translate("Нет результата для копирования."))

def switch_language() -> None:
global current_locale, locale
current_locale = 'en' if current_locale == 'ru' else 'ru'
locale = load_locale(LOCALE_FILES[current_locale])
update_ui_language()

def update_labels() -> None:
labels = {
"Первое число": label1,
"Второе число": label2,
"Операция": label3,
"Результат": result_label,
"История операций": label_history
}

for key, widget in labels.items():
widget.config(text=translate(key))

def update_buttons() -> None:
buttons = {
"Вычислить": calculate_button,
"Очистить": clear_button,
"Скопировать результат": copy_button,
"О приложении": info_button,
"Клавиатурные шорткаты": shortcuts_button,
"Выход": exit_button,
"Переключить язык": language_button
}

for key, widget in buttons.items():
widget.config(text=translate(key))

def update_operation_menu() -> None:
menu = operation_menu['menu']
menu.delete(0, 'end')
operations = [
translate("Сложение"),
translate("Вычитание"),
translate("Умножение"),
translate("Деление"),
translate("Возведение в степень"),
translate("Квадратный корень")
]
for operation in operations:
menu.add_command(label=operation, command=tk._setit(selected_operation, operation))
selected_operation.set(operations[0])

def update_ui_language() -> None:
root.title(translate("Калькулятор"))
update_labels()
update_buttons()
update_operation_menu()
theme_label.config(text=translate("Тема") + ":")

def switch_theme(theme: str) -> None:
style.theme_use(theme)

root = tk.Tk()
root.title(translate("Калькулятор"))
root.geometry("800x500")
root.configure(bg="#2e3f4f")

style = ttk.Style()
style.theme_use('clam')

selected_operation = tk.StringVar()
selected_operation.set(translate("Сложение"))
selected_operation.trace("w", on_operation_change)

label1 = ttk.Label(root, text=translate("Первое число"), font=("Arial", 14), background="#2e3f4f", foreground="white")
label1.grid(row=0, column=0, padx=10, pady=10, sticky='w')
entry1 = ttk.Entry(root, font=("Arial", 14))
entry1.grid(row=0, column=1, padx=10, pady=10, sticky='w')

label2 = ttk.Label(root, text=translate("Второе число"), font=("Arial", 14), background="#2e3f4f", foreground="white")
label2.grid(row=1, column=0, padx=10, pady=10, sticky='w')
entry2 = ttk.Entry(root, font=("Arial", 14))
entry2.grid(row=1, column=1, padx=10, pady=10, sticky='w')

label3 = ttk.Label(root, text=translate("Операция"), font=("Arial", 14), background="#2e3f4f", foreground="white")
label3.grid(row=2, column=0, padx=10, pady=10, sticky='w')

operations = [
translate("Сложение"),
translate("Вычитание"),
translate("Умножение"),
translate("Деление"),
translate("Возведение в степень"),
translate("Квадратный корень")
]

operation_menu = tk.OptionMenu(root, selected_operation, *operations)
operation_menu.config(font=("Arial", 12))
operation_menu.grid(row=2, column=1, padx=10, pady=10, sticky='w')

calculate_button = ttk.Button(root, text=translate("Вычислить"), command=perform_operation, style="TButton")
calculate_button.grid(row=3, column=0, columnspan=2, pady=10, sticky='w')

clear_button = ttk.Button(root, text=translate("Очистить"), command=clear_entries, style="TButton")
clear_button.grid(row=4, column=0, columnspan=2, pady=10, sticky='w')

result_label = ttk.Label(root, text=f"{translate('Результат')}: ", font=("Arial", 14), background="#2e3f4f", foreground="white")
result_label.grid(row=5, column=0, columnspan=2, pady=10, sticky='w')

copy_button = ttk.Button(root, text=translate("Скопировать результат"), command=copy_result, style="TButton")
copy_button.grid(row=6, column=0, columnspan=2, pady=10, sticky='w')

info_button = ttk.Button(root, text=translate("О приложении"), command=show_info, style="TButton")
info_button.grid(row=7, column=0, columnspan=2, pady=10, sticky='w')

shortcuts_button = ttk.Button(root, text=translate("Клавиатурные шорткаты"), command=show_shortcuts, style="TButton")
shortcuts_button.grid(row=8, column=0, columnspan=2, pady=10, sticky='w')

exit_button = ttk.Button(root, text=translate("Выход"), command=exit_app, style="TButton")
exit_button.grid(row=9, column=0, columnspan=2, pady=10, sticky='w')

language_button = ttk.Button(root, text=translate("Переключить язык"), command=switch_language, style="TButton")
language_button.grid(row=10, column=0, columnspan=2, pady=10, sticky='w')

label_history = ttk.Label(root, text=translate("История операций"), font=("Arial", 14), background="#2e3f4f", foreground="white")
label_history.grid(row=0, column=2, padx=10, pady=10, sticky='nw')

history_frame = ttk.Frame(root)
history_frame.grid(row=1, column=2, rowspan=10, padx=10, pady=10, sticky='nw')
history_text = tk.Text(history_frame, height=18, width=30, font=("Arial", 12))
history_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
history_scroll = ttk.Scrollbar(history_frame)
history_scroll.pack(side=tk.RIGHT, fill=tk.Y)
history_text.config(yscrollcommand=history_scroll.set)
history_scroll.config(command=history_text.yview)

# Функция для обработки шорткатов по keycode
def handle_shortcuts(event):
# event.state & 0x4 проверяет, нажата ли клавиша Control
if event.state & 0x4:
if event.keycode == 67: # Keycode для 'C'
copy_result()
elif event.keycode == 73: # Keycode для 'I'
show_info()
elif event.keycode == 81: # Keycode для 'Q'
exit_app()
elif event.keycode == 75: # Keycode для 'K'
show_shortcuts()

root.bind('<Key>', handle_shortcuts)
root.bind('<Return>', perform_operation)
root.bind('<Escape>', clear_entries)

theme_label = ttk.Label(root, text=translate("Тема") + ":", font=("Arial", 14), background="#2e3f4f", foreground="white")
theme_label.grid(row=11, column=0, padx=10, pady=10, sticky='w')
theme_menu = ttk.Combobox(root, values=style.theme_names(), state="readonly")
theme_menu.set(style.theme_use())
theme_menu.grid(row=11, column=1, padx=10, pady=10, sticky='w')
theme_menu.bind("<<ComboboxSelected>>", lambda event: switch_theme(theme_menu.get()))

load_history()

root.mainloop()

помоги справить ошибку 
D:\скрипты пайтон\калькулятор>python calculator.pyw
  File "D:\скрипты пайтон\калькулятор\calculator.pyw", line 24
    try:
    ^
IndentationError: expected an indented block
пришли правильный код полностью