import customtkinter as ctk
# ── 主題設定 ──────────────────────────────────────────────
ctk.set_appearance_mode("dark")
ctk.set_default_color_theme("blue")
class Calculator(ctk.CTk):
def __init__(self):
super().__init__()
self.title("計算機")
self.geometry("380x600")
self.resizable(False, False)
self.configure(fg_color="#1a1a2e")
# 計算狀態
self._expression = "" # 完整算式
self._current = "0" # 顯示中的數字
self._new_num = True # 下一次輸入是否為新數字
self._build_ui()
# ── 介面建構 ─────────────────────────────────────────
def _build_ui(self):
# 顯示區
display_frame = ctk.CTkFrame(self, fg_color="#16213e", corner_radius=16)
display_frame.pack(fill="x", padx=20, pady=(24, 12))
self._expr_label = ctk.CTkLabel(
display_frame, text="", font=("Consolas", 14),
text_color="#8892b0", anchor="e"
)
self._expr_label.pack(fill="x", padx=16, pady=(12, 0))
self._main_label = ctk.CTkLabel(
display_frame, text="0", font=("Consolas", 48, "bold"),
text_color="#e2e8f0", anchor="e"
)
self._main_label.pack(fill="x", padx=16, pady=(0, 16))
# 按鈕區
btn_frame = ctk.CTkFrame(self, fg_color="transparent")
btn_frame.pack(fill="both", expand=True, padx=20, pady=(0, 20))
# 按鈕佈局:(文字, 欄位跨度, 顏色類型)
layout = [
[("AC", 1, "clear"), ("±", 1, "func"), ("%", 1, "func"), ("÷", 1, "op")],
[("7", 1, "num"), ("8", 1, "num"), ("9", 1, "num"), ("×", 1, "op")],
[("6", 1, "num"), ("5", 1, "num"), ("4", 1, "num"), ("−", 1, "op")],
[("1", 1, "num"), ("2", 1, "num"), ("3", 1, "num"), ("+", 1, "op")],
[("0", 2, "num"), (".", 1, "num"), ("=", 1, "eq")],
]
colors = {
"num": ("#2d3561", "#e2e8f0"), # (bg, fg)
"op": ("#e94560", "#ffffff"),
"func": ("#2d3561", "#cdd6f4"),
"clear": ("#e94560", "#ffffff"),
"eq": ("#00b4d8", "#ffffff"),
}
hover = {
"num": "#3d4571",
"op": "#ff6b6b",
"func": "#3d4571",
"clear": "#ff6b6b",
"eq": "#48cae4",
}
for row_idx, row in enumerate(layout):
btn_frame.grid_rowconfigure(row_idx, weight=1, uniform="row")
col = 0
for (text, span, kind) in row:
btn_frame.grid_columnconfigure(col, weight=1, uniform="col")
bg, fg = colors[kind]
btn = ctk.CTkButton(
btn_frame,
text=text,
font=("Consolas", 22, "bold"),
fg_color=bg,
text_color=fg,
hover_color=hover[kind],
corner_radius=14,
command=lambda t=text: self._on_click(t),
)
btn.grid(
row=row_idx, column=col,
columnspan=span, sticky="nsew",
padx=5, pady=5
)
col += span
# ── 事件處理 ─────────────────────────────────────────
def _on_click(self, key: str):
if key == "AC":
self._clear()
elif key == "±":
self._toggle_sign()
elif key == "%":
self._percent()
elif key in ("÷", "×", "−", "+"):
self._set_operator(key)
elif key == "=":
self._calculate()
elif key == ".":
self._add_decimal()
else:
self._append_digit(key)
def _clear(self):
self._expression = ""
self._current = "0"
self._new_num = True
self._refresh()
def _toggle_sign(self):
if self._current != "0":
if self._current.startswith("-"):
self._current = self._current[1:]
else:
self._current = "-" + self._current
self._refresh()
def _percent(self):
try:
self._current = self._fmt(float(self._current) / 100)
self._refresh()
except Exception:
pass
def _append_digit(self, d: str):
if self._new_num:
self._current = d
self._new_num = False
else:
if self._current == "0":
self._current = d
else:
if len(self._current.replace("-", "").replace(".", "")) < 12:
self._current += d
self._refresh()
def _add_decimal(self):
if self._new_num:
self._current = "0."
self._new_num = False
elif "." not in self._current:
self._current += "."
self._refresh()
def _set_operator(self, op: str):
self._expression = f"{self._current} {op} "
self._new_num = True
self._refresh()
def _calculate(self):
if not self._expression:
return
try:
expr = self._expression + self._current
# 替換為 Python 可計算的符號
safe = (expr
.replace("÷", "/")
.replace("×", "*")
.replace("−", "-"))
result = eval(safe) # noqa: S307
self._expr_label.configure(text=expr + " =")
self._current = self._fmt(result)
self._expression = ""
self._new_num = True
self._main_label.configure(text=self._current)
return
except ZeroDivisionError:
self._current = "除以零"
except Exception:
self._current = "錯誤"
self._expression = ""
self._new_num = True
self._refresh()
def _fmt(self, v) -> str:
"""將數值格式化成簡潔字串。"""
if isinstance(v, float) and v.is_integer():
return str(int(v))
s = f"{v:.10g}"
return s
def _refresh(self):
self._expr_label.configure(text=self._expression)
self._main_label.configure(text=self._current)
if __name__ == "__main__":
app = Calculator()
app.mainloop()