python計算機製作

 

製作計算機的小心得

最近我花了一點時間,用 Python 做了一個小型的計算機程式。這算是我第一次用到 Tkinter 這個套件,主要是想練習圖形介面(GUI)的製作,也讓自己從純文字輸出進一步嘗試互動操作。

Tkinter 是 Python 內建的 GUI 工具,不需要額外安裝就能使用。它提供了像是 ButtonLabelEntry 這些元件,可以讓使用者輸入數字、按下按鈕後看到結果。我覺得它的邏輯其實蠻直覺的,只是排版的部分要多試幾次才會順眼。

我的程式裡除了基本的加減乘除之外,我還加上了一些進階功能,像是質因數分解、因數列出、階乘等等。這些功能主要是練習數學運算的邏輯,像質因數分解就要用迴圈和條件判斷,階乘則可以用遞迴或是簡單的乘法累加來實作。

整個過程裡我學到最多的,其實不是數學公式,而是介面設計的邏輯。要讓使用者輸入、再顯示結果,看似簡單,但要讓整體運作流暢、畫面整潔,就需要一點結構規劃。雖然 Tkinter 不算華麗,但對初學者來說很夠用。

最後這次的作品讓我更熟悉了 Python 的基礎,也對 GUI 的概念有了實際體驗。下一步我可能會想試試看用 Flask 或 HTML 版本的計算機,看看能不能做成網頁版的。

這是我的程式:

import tkinter as tk
from tkinter import font
from functools import partial

# ----------------- 運算函數 -----------------
def gcd(a, b):
a, b = abs(a), abs(b)
while b != 0:
a, b = b, a % b
return a

def lcm(a, b):
return abs(a * b) // gcd(a, b)

def square(a, b):
return a ** b

def factorize(n):
n_abs = abs(n)
if n_abs == 0:
return [(0, 1)]
res = []
count = 0
while n_abs % 2 == 0:
n_abs //= 2
count += 1
if count > 0:
res.append((2, count))
d = 3
while d*d <= n_abs:
count = 0
while n_abs % d == 0:
n_abs //= d
count += 1
if count > 0:
res.append((d, count))
d += 2
if n_abs > 1:
res.append((n_abs, 1))
return res

def level(n):
a = 1
n = int(n)
for i in range(1, n+1):
a = a * i
return a

def factors(n):
factors = []
n = 0
for i in range(1, n+1):
if n % i == 0:
factors.append(i)
n = n + 1
return factors

def how_many_factors(n):
len(factors(n))

# ----------------- 事件函數 -----------------
def run_op(op):
try:
a = float(entryA.get())
b = float(entryB.get())
except ValueError:
result_var.set("請輸入有效數字")
return
try:
if op == "add":
res = a + b
elif op == "sub":
res = a - b
elif op == "mul":
res = a * b
elif op == "div":
res = a / b
elif op == "mod":
res = a % b
result_var.set(f"結果:{res}")
except ZeroDivisionError:
result_var.set("除以零錯誤")

def run_gcd():
try:
a = int(entryA.get())
b = int(entryB.get())
result_var.set(f"GCD({a},{b}) = {gcd(a,b)}")
except ValueError:
result_var.set("請輸入有效整數")

def run_lcm():
try:
a = int(entryA.get())
b = int(entryB.get())
result_var.set(f"LCM({a},{b}) = {lcm(a,b)}")
except ValueError:
result_var.set("請輸入有效整數")

def run_square():
try:
a = int(entryA.get())
b = int(entryB.get())
result_var.set(f"{a}^{b} = {square(a,b)}")
except ValueError:
result_var.set("請輸入有效整數")


def run_factor():
try:
n = int(single_entry.get())
except ValueError:
pf_result_var.set("僅接受整數")
return
if n == 0:
pf_result_var.set("0 無質因數")
return
if n in (1, -1):
pf_result_var.set(f"{n} 無質因數")
return

factors = factorize(n)
parts = [f"{p}^{e}" if e>1 else str(p) for p, e in factors]
sign = '-' if n < 0 else ''
pf_result_var.set(f"{n} = {sign}{' × '.join(parts)}")

def run_factors():
try:
n = int(single_entry.get())
except ValueError:
pf_result_var.set("僅接受整數")
return
if n == 0:
pf_result_var.set("0 的因數無意義")
return
factors_list = [i for i in range(1, abs(n)+1) if n % i == 0]
pf_result_var.set(f"{n} 的因數:{factors_list}")

def run_factors_count():
try:
n = int(single_entry.get())
except ValueError:
pf_result_var.set("僅接受整數")
return
if n == 0:
pf_result_var.set("0 的因數個數無意義")
return
count = len([i for i in range(1, abs(n)+1) if n % i == 0])
pf_result_var.set(f"{n} 的因數個數:{count}")

def run_factorial():
try:
n = int(single_entry.get())
except ValueError:
pf_result_var.set("僅接受整數")
return
if n < 0:
pf_result_var.set("階乘僅接受非負整數")
return
res = 1
for i in range(1, n+1):
res *= i
pf_result_var.set(f"{n}! = {res}")

# ----------------- 輸入框事件 -----------------
def on_entry_click(event, placeholder):
if event.widget.get() == placeholder:
event.widget.delete(0, "end")
event.widget.config(fg="white", highlightbackground="#1e88e5")

def on_focus_out(event, placeholder):
if event.widget.get() == "":
event.widget.insert(0, placeholder)
event.widget.config(fg="#888", highlightbackground="#444")

# ----------------- 模擬按鈕 -----------------
def create_btn(frame, text, command, width, height=30, radius=10,
bg="#0d47a1", fg="white", hover="#1976d2"):
c = tk.Canvas(frame, width=width, height=height, bg=frame["bg"], highlightthickness=0)

def draw(color):
c.delete("all")
c.create_arc(0, 0, 2*radius, 2*radius, start=90, extent=90, fill=color, outline=color)
c.create_arc(width-2*radius, 0, width, 2*radius, start=0, extent=90, fill=color, outline=color)
c.create_arc(0, height-2*radius, 2*radius, height, start=180, extent=90, fill=color, outline=color)
c.create_arc(width-2*radius, height-2*radius, width, height, start=270, extent=90, fill=color, outline=color)
c.create_rectangle(radius, 0, width-radius, height, fill=color, outline=color)
c.create_rectangle(0, radius, width, height-radius, fill=color, outline=color)
c.create_text(width//2, height//2, text=text, fill=fg, font=font_btn)

draw(bg)
c.bind("<Enter>", lambda e: draw(hover))
c.bind("<Leave>", lambda e: draw(bg))
c.bind("<Button-1>", lambda e: command())
c.pack(side="left", padx=3, pady=3, expand=True, fill="x")
return c


# ----------------- Tkinter GUI -----------------
root = tk.Tk()
root.title("高級計算機")
root.configure(bg="#0b1020")
root.geometry("480x440")

# 字型設定
font_title = font.Font(family="Segoe UI", size=16, weight="bold")
font_entry = font.Font(family="Segoe UI", size=12)
font_btn = font.Font(family="Segoe UI", size=12, weight="bold")
font_result = font.Font(family="Consolas", size=12)

# Container Frame
container = tk.Frame(root, bg="#0f1724", padx=20, pady=20)
container.pack(expand=True, fill="both", padx=20, pady=20)

# 標題
tk.Label(container, text="高級計算機", font=font_title, fg="#e8eef6", bg="#0f1724").pack(pady=10)

# 輸入框
placeholderA = "輸入數字 A"
entryA = tk.Entry(container, font=font_entry, fg="#888", bg="#0b1020", insertbackground="white",
highlightthickness=2, highlightbackground="#444", relief="flat")
entryA.pack(fill="x", pady=5)
entryA.insert(0, placeholderA)
entryA.bind("<FocusIn>", lambda e: on_entry_click(e, placeholderA))
entryA.bind("<FocusOut>", lambda e: on_focus_out(e, placeholderA))

placeholderB = "輸入數字 B"
entryB = tk.Entry(container, font=font_entry, fg="#888", bg="#0b1020", insertbackground="white",
highlightthickness=2, highlightbackground="#444", relief="flat")
entryB.pack(fill="x", pady=5)
entryB.insert(0, placeholderB)
entryB.bind("<FocusIn>", lambda e: on_entry_click(e, placeholderB))
entryB.bind("<FocusOut>", lambda e: on_focus_out(e, placeholderB))

# 第一列:四則運算
frame_ops1 = tk.Frame(container, bg="#0f1724")
frame_ops1.pack(pady=5, fill="x")
create_btn(frame_ops1, "A + B", lambda: run_op("add"),85)
create_btn(frame_ops1, "A - B", lambda: run_op("sub"),85)
create_btn(frame_ops1, "A x B", lambda: run_op("mul"),85)
create_btn(frame_ops1, "A ÷ B", lambda: run_op("div"),85)

# 第二列:mod / GCD / LCM
frame_ops2 = tk.Frame(container, bg="#0f1724")
frame_ops2.pack(pady=5, fill="x")
create_btn(frame_ops2, "A mod B", lambda: run_op("mod"),85)
create_btn(frame_ops2, "GCD(A,B)", run_gcd,85)
create_btn(frame_ops2, "LCM(A,B)", run_lcm,85)
create_btn(frame_ops2, "A ^ b", run_square,85)

# 結果顯示
result_var = tk.StringVar(value="尚無運算")
tk.Label(container, textvariable=result_var, font=font_result, fg="#dff1ff", bg="#0f1724",
anchor="w", justify="left", height=2).pack(fill="x", pady=5)

# 質因數分解
placeholderPf = "輸入數字C"
single_entry = tk.Entry(container, font=font_entry, fg="#888", bg="#0b1020", insertbackground="white",
highlightthickness=2, highlightbackground="#444", relief="flat")
single_entry.pack(fill="x", pady=5)
single_entry.insert(0, placeholderPf)
single_entry.bind("<FocusIn>", lambda e: on_entry_click(e, placeholderPf))
single_entry.bind("<FocusOut>", lambda e: on_focus_out(e, placeholderPf))

# 質因數分解按鈕
frame_pf = tk.Frame(container, bg="#0f1724")
frame_pf.pack(pady=5, fill="x")
create_btn(frame_pf, "質因數分解", run_factor, 85)
create_btn(frame_pf, "因數", run_factors, 85)
create_btn(frame_pf, "因數個數", run_factors_count, 85)
create_btn(frame_pf, "階乘", run_factorial, 85)


# 顯示質因數分解結果
pf_result_var = tk.StringVar(value="(尚無結果)")
tk.Label(container, textvariable=pf_result_var, font=font_result, fg="#dff1ff", bg="#0f1724",
anchor="w", justify="left", height=2).pack(fill="x", pady=5)

# 啟動 GUI
root.mainloop()




留言

  1. 你的邏輯好強!還有後設認知也讓我大為驚艷!

    回覆刪除

張貼留言

這個網誌中的熱門文章

pygame自動生成迷宮遊戲製作

平方的方法