import tkinter as tk
from tkinter import ttk

class Calculator(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("计算器")
        self.resizable(False, False)
        self.geometry("320x420")
        self.configure(bg="#eee")

        self.expr = ""  # 存储表达式字符串

        self._create_widgets()
        self._bind_keys()

    def _create_widgets(self):
        # 显示屏（可显示多行用于历史/较长表达式）
        self.display_var = tk.StringVar()
        display = ttk.Entry(self, textvariable=self.display_var, font=("Helvetica", 20), justify="right")
        display.pack(fill="x", padx=10, pady=10, ipady=10)
        display.focus_set()

        # 按钮布局
        btns = [
            ["C", "⌫", "(", ")"],
            ["7", "8", "9", "÷"],
            ["4", "5", "6", "×"],
            ["1", "2", "3", "-"],
            ["±", "0", ".", "+"],
            ["=",]
        ]

        # 使用框架排列按钮
        frame = ttk.Frame(self)
        frame.pack(padx=10, pady=5, fill="both", expand=True)

        for r, row in enumerate(btns):
            row_frame = ttk.Frame(frame)
            row_frame.pack(fill="x", expand=True)
            for c, label in enumerate(row):
                btn = ttk.Button(row_frame, text=label, command=lambda L=label: self._on_button(L))
                btn.pack(side="left", expand=True, fill="both", padx=3, pady=3)
                btn.configure(width=6)
            # 如果是最后一行的 =，让它横跨全部宽度
            if r == len(btns) - 1:
                row_frame.pack_forget()
                eq_row = ttk.Frame(frame)
                eq_row.pack(fill="x", expand=True)
                eq_btn = ttk.Button(eq_row, text="=", command=lambda: self._on_button("="))
                eq_btn.pack(expand=True, fill="both", padx=3, pady=3)
                eq_btn.configure(width=30)

    def _bind_keys(self):
        self.bind("<Key>", self._on_key)
        self.bind("<Return>", lambda e: self._evaluate())
        self.bind("<BackSpace>", lambda e: self._backspace())
        self.bind("<Escape>", lambda e: self._clear())

    def _on_key(self, event):
        ch = event.char
        if ch in "0123456789":
            self._append(ch)
        elif ch in "+-*/().":
            # 将 *,/ 替换为显示符号 × ÷ 时的内部符号
            self._append(ch)
        elif ch == "\r":
            self._evaluate()
        # 其他按键忽略

    def _on_button(self, label):
        if label == "C":
            self._clear()
        elif label == "⌫":
            self._backspace()
        elif label == "=":
            self._evaluate()
        elif label == "÷":
            self._append(" / ")
        elif label == "×":
            self._append(" * ")
        elif label == "±":
            self._toggle_sign()
        else:
            # 数字或小数点或括号或运算符
            if label in "0123456789.":
                self._append(label)
            elif label in "+-":
                self._append(f" {label} ")
            elif label in ("(", ")"):
                self._append(label)

    def _append(self, s):
        # 简单处理空格，使得字符串更可读
        if s.startswith(" ") or s.endswith(" "):
            self.expr += s
        else:
            self.expr += s
        self._update_display()

    def _clear(self):
        self.expr = ""
        self._update_display()

    def _backspace(self):
        if not self.expr:
            return
        # 删除最后一个字符（注意处理空格）
        if self.expr.endswith(" "):
            # 删除末尾空格（假设运算符以空格包围）
            self.expr = self.expr[:-3]
        else:
            self.expr = self.expr[:-1]
        self._update_display()

    def _toggle_sign(self):
        # 尝试将当前最后一个数字加/去负号（较简单的实现）
        # 找到表达式末尾数字的位置
        import re
        m = re.search(r"([0-9\.]+)$", self.expr)
        if not m:
            return
        num = m.group(1)
        start = m.start(1)
        if start >= 1 and self.expr[start-1] == "-":
            # 如果前面有负号且是运算符的一部分，移除负号
            # 比如 "5 + -3" -> "5 + 3"
            self.expr = self.expr[:start-1] + self.expr[start:]
        else:
            # 插入负号
            self.expr = self.expr[:start] + "-" + self.expr[start:]
        self._update_display()

    def _safe_eval(self, expr):
        # 将显示的 × ÷ 转换为 python 可识别的运算符，并做安全检查
        # 允许的基本字符：数字、运算符、空格、点、小括号
        allowed_chars = "0123456789+-*/(). %eE"
        for ch in expr:
            if ch not in allowed_chars:
                raise ValueError("非法字符")
        # 禁止连续的两个运算符（除了允许的科学计数法 e/E 的 + -）
        # 使用 eval 前再替换一些符号
        # 直接使用 eval，但限制内置与全局环境
        try:
            # eval 结果可能是 int 或 float
            res = eval(expr, {"__builtins__": None}, {})
        except Exception as e:
            raise e
        return res

    def _evaluate(self):
        if not self.expr:
            return
        # 准备表达式：将显示中可能出现的全角符号替换为 python 运算符
        expr = self.expr.replace("×", "*").replace("÷", "/")
        try:
            result = self._safe_eval(expr)
            # 格式化结果：如果是整数则不显示小数点
            if isinstance(result, float):
                if result.is_integer():
                    result = int(result)
                else:
                    result = round(result, 10)  # 限制小数位
            self.expr = str(result)
            self._update_display()
        except Exception:
            self.expr = "ERROR"
            self._update_display()
            self.after(1200, lambda: self._clear())

    def _update_display(self):
        self.display_var.set(self.expr)

if __name__ == "__main__":
    app = Calculator()
    app.mainloop()