iT邦幫忙

0

[Day25]單位換算器 GUI(Tkinter)2

  • 分享至 

  • xImage
  •  

今天在 Day 24 的基礎上,加入溫度換算(°C/°F/K),並增加日常實用的小功能:

  • 即時換算(單位或數值變動就自動計算)
  • 交換單位一鍵切換
  • 複製結果到剪貼簿
  • 歷史紀錄(可快速回看剛轉過什麼)

轉換原理:溫度為什麼特別?
溫度不是單純「倍數關係」,需要平移+縮放:

  • 攝氏 → 開爾文:K = °C + 273.15
  • 華氏 → 開爾文:K = (°F − 32) × 5/9 + 273.15
  • 反向同理(先統一到 K,再轉到目標)。

程式碼(存成 unit_converter_pro.py)
可獨立執行,不必先安裝 Day 22;已包含長度、重量、溫度與新功能。

import tkinter as tk
from tkinter import ttk, messagebox

# ---- 資料定義 ----
CATEGORIES = ["長度", "重量", "溫度"]

LENGTH_TO_M = {
    "mm": 1e-3, "cm": 1e-2, "m": 1.0, "km": 1e3,
    "inch": 0.0254, "foot": 0.3048, "yard": 0.9144, "mile": 1609.344
}
MASS_TO_KG = {
    "g": 1e-3, "kg": 1.0, "oz": 0.028349523125, "lb": 0.45359237, "ton": 1000.0
}
TEMP_UNITS = ["°C", "°F", "K"]

UNITS_BY_CAT = {
    "長度": list(LENGTH_TO_M.keys()),
    "重量": list(MASS_TO_KG.keys()),
    "溫度": TEMP_UNITS,
}

# ---- 轉換 ----
def convert_length(val, from_u, to_u):
    return (val * LENGTH_TO_M[from_u]) / LENGTH_TO_M[to_u]

def convert_mass(val, from_u, to_u):
    return (val * MASS_TO_KG[from_u]) / MASS_TO_KG[to_u]

def c_to_k(c): return c + 273.15
def f_to_k(f): return (f - 32) * 5/9 + 273.15
def k_to_c(k): return k - 273.15
def k_to_f(k): return (k - 273.15) * 9/5 + 32

def convert_temp(val, from_u, to_u):
    k = c_to_k(val) if from_u == "°C" else (f_to_k(val) if from_u == "°F" else val)
    return k if to_u == "K" else (k_to_c(k) if to_u == "°C" else k_to_f(k))

# ---- UI 行為 ----
def on_category_change(_=None):
    cat = cat_var.get()
    units = UNITS_BY_CAT[cat]
    from_var.set(units[0])
    to_var.set(units[1] if len(units) > 1 else units[0])
    from_box["values"] = units; to_box["values"] = units
    result_var.set(""); detail_var.set("")
    val_entry.focus()
    auto_convert()

def auto_convert(_=None):
    """單位或數值變動即換算"""
    try:
        val = float(val_var.get())
    except ValueError:
        result_var.set(""); detail_var.set(""); return
    frm, to = from_var.get(), to_var.get()
    cat = cat_var.get()
    try:
        if cat == "長度":
            ans = convert_length(val, frm, to)
            result_var.set(f"{ans:.6g} {to}")
            detail_var.set(f"{val} {frm} × (to m) / (to {to})")
        elif cat == "重量":
            ans = convert_mass(val, frm, to)
            result_var.set(f"{ans:.6g} {to}")
            detail_var.set(f"{val} {frm} × (to kg) / (to {to})")
        else:
            ans = convert_temp(val, frm, to)
            result_var.set(f"{ans:.2f} {to}")
            detail_var.set("溫度以 K 為中繼:C↔K、F↔K 公式換算")
    except Exception as e:
        messagebox.showerror("轉換錯誤", str(e))
        return

def swap_units():
    a, b = from_var.get(), to_var.get()
    from_var.set(b); to_var.set(a)
    auto_convert()

def copy_result():
    text = result_var.get().strip()
    if not text:
        messagebox.showinfo("提示", "沒有可複製的結果"); return
    root.clipboard_clear(); root.clipboard_append(text); root.update()
    messagebox.showinfo("已複製", f"已複製:{text}")

def add_history():
    text = result_var.get().strip()
    if not text: return
    src = f"{val_var.get()} {from_var.get()}"
    dst = text
    hist_list.insert(0, f"{src}  →  {dst}")

# ---- 介面 ----
root = tk.Tk()
root.title("單位換算器 - 進階版")

main = ttk.Frame(root, padding=16); main.grid(sticky="nsew")
root.rowconfigure(0, weight=1); root.columnconfigure(0, weight=1)

# 左側:操作
left = ttk.Frame(main); left.grid(row=0, column=0, sticky="nsew", padx=(0,12))
# 右側:歷史
right = ttk.Frame(main); right.grid(row=0, column=1, sticky="nsew")
main.columnconfigure(0, weight=1); main.columnconfigure(1, weight=1)
main.rowconfigure(0, weight=1)

# 類別
ttk.Label(left, text="類別").grid(row=0, column=0, sticky="e", pady=4)
cat_var = tk.StringVar(value=CATEGORIES[0])
cat_box = ttk.Combobox(left, textvariable=cat_var, values=CATEGORIES, state="readonly", width=8)
cat_box.grid(row=0, column=1, sticky="w", pady=4)
cat_box.bind("<<ComboboxSelected>>", on_category_change)

# 數值
ttk.Label(left, text="數值").grid(row=1, column=0, sticky="e", pady=4)
val_var = tk.StringVar(value="1")
val_entry = ttk.Entry(left, textvariable=val_var, width=14, justify="center")
val_entry.grid(row=1, column=1, sticky="w", pady=4)
val_entry.bind("<KeyRelease>", auto_convert)

# 單位
ttk.Label(left, text="從").grid(row=2, column=0, sticky="e", pady=4)
from_var = tk.StringVar(); from_box = ttk.Combobox(left, textvariable=from_var, state="readonly", width=8)
from_box.grid(row=2, column=1, sticky="w", pady=4)
from_box.bind("<<ComboboxSelected>>", auto_convert)

ttk.Label(left, text="到").grid(row=3, column=0, sticky="e", pady=4)
to_var = tk.StringVar(); to_box = ttk.Combobox(left, textvariable=to_var, state="readonly", width=8)
to_box.grid(row=3, column=1, sticky="w", pady=4)
to_box.bind("<<ComboboxSelected>>", auto_convert)

# 按鈕
btns = ttk.Frame(left); btns.grid(row=4, column=0, columnspan=2, pady=8)
ttk.Button(btns, text="交換單位", command=swap_units).grid(row=0, column=0, padx=4)
ttk.Button(btns, text="複製結果", command=copy_result).grid(row=0, column=1, padx=4)

# 結果
result_var = tk.StringVar(value="")
detail_var = tk.StringVar(value="")
ttk.Label(left, text="結果").grid(row=5, column=0, sticky="e", pady=6)
ttk.Label(left, textvariable=result_var, font=("Segoe UI", 12)).grid(row=5, column=1, sticky="w", pady=6)
ttk.Label(left, textvariable=detail_var, foreground="#666").grid(row=6, column=0, columnspan=2, sticky="w")

# 右側:歷史紀錄(最新在最上方)
ttk.Label(right, text="歷史紀錄").grid(row=0, column=0, sticky="w")
hist_list = tk.Listbox(right, height=10)
hist_list.grid(row=1, column=0, sticky="nsew")
right.rowconfigure(1, weight=1); right.columnconfigure(0, weight=1)

# 每次換算後把結果加到歷史(Enter 也會)
def confirm_and_add(_=None):
    auto_convert(); add_history()
root.bind("<Return>", confirm_and_add)

# 初始化
on_category_change()
val_entry.focus()
root.mainloop()

使用

python unit_converter_pro.py

實作:
https://ithelp.ithome.com.tw/upload/images/20251008/20169368AlOf3IdRQP.png
小技巧
溫度顯示我用 小數點兩位,長度/重量顯示 有效位數 6;可依需求調整。
若想新增「面積/體積」,用同樣「基準單位」思路(面積→m²、體積→m³)即可一秒擴充。
想要「輸入即時格式化」(例如自動把 1..2 修成 1.2),可在 val_entry 的 KeyRelease 裡加規則。

兩天小結
Day 24打底:用「基準單位法」完成長度、重量轉換,UI 簡潔。
Day 25加溫度(平移+縮放)、加上快用功能(即時換算、交換、複製、歷史),提升可用性。


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言