๐งฎ Python๊ณผ Tkinter๋ฅผ ์ฌ์ฉํ ํ๋ ํต๊ณ ๊ณ์ฐ๊ธฐ ๋ง๋ค๊ธฐ
Source: Dev.to
์ ๋งํฌ์ ํฌํจ๋ ํ
์คํธ๋ฅผ ๋ฒ์ญํ๋ ค๋ฉด ํด๋น ๋ด์ฉ์ ์ ๊ณตํด ์ฃผ์๊ฒ ์ด์?
๋งํฌ ์์ฒด๋ ๊ทธ๋๋ก ๋๊ณ , ๋ฒ์ญ์ด ํ์ํ ๋ณธ๋ฌธ์ ์๋ ค์ฃผ์๋ฉด ํ๊ตญ์ด๋ก ๋ฒ์ญํด ๋๋ฆฌ๊ฒ ์ต๋๋ค.
๊ฐ์
Python์ ๋ฐ์ดํฐ ๊ณผํ ์ํ๊ณ๋ก ์์ฃผ ์นญ์ฐฌ๋ฐ์ง๋ง, ์ ์ฉํ ๊ฒ์ ๋ง๋ค๊ธฐ ์ํด ๋ฐ๋์ Pandas๋ NumPy๊ฐ ํ์ํ์ง๋ ์์ต๋๋ค.
์ด๋ฒ ํฌ์คํธ์์๋ StatMate๋ฅผ ๋ง๋ค ๊ฒ์ ๋๋ค โ ๊น๋ํ๊ณ ํ๋์ ์ธ ๋ฐ์คํฌํฑ ํต๊ณ ๊ณ์ฐ๊ธฐ์ด๋ฉฐ, ๋ค์์ ์ฌ์ฉํฉ๋๋ค:
- ๐ช Tkinter โ Python์ ํ์ค GUI ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- ๐จ svโttk โ Tkinter์ฉ ํ๋์ ์ธ ํ ๋ง
- ๐ statistics โ Python ๋ด์ฅ ํต๊ณ ๋ชจ๋
๋ง์ง๋ง๊น์ง ์งํํ๋ฉด ๋ค์์ ๊ณ์ฐํ๋ ์ ๊ตํ GUI ์ฑ์ ์ป๊ฒ ๋ฉ๋๋ค:
- ํ๊ท
- ์ค์๊ฐ
- ์ต๋น๊ฐ
- ๋ถ์ฐ
- ํ์ค ํธ์ฐจ
- ์ต์ / ์ต๋
โฆ์ค์๊ฐ ์ ๋ ฅ ๊ฒ์ฆ ๋ฐ ์กฐ์ ๊ฐ๋ฅํ ์์์ ์๋ฆฌ์ ๊ธฐ๋ฅ์ ํฌํจํฉ๋๋ค.
โจ ๊ธฐ๋ฅ
- svโttk๋ฅผ ์ฌ์ฉํ ํ๋์ ์ธ ๋ผ์ดํธ ํ ๋ง
- ์ค์๊ฐ ์ ๋ ฅ ๊ฒ์ฆ
- ํ๋ ์ด์คํ๋ ํ ์คํธ UX
- ์กฐ์ ๊ฐ๋ฅํ ์์์ ์ ๋ฐ๋
- ๊น๋ํ ์ํ ํ์์ค ํผ๋๋ฐฑ
- ์ฐ์ํ ์ค๋ฅ ์ฒ๋ฆฌ
๐ฆ ์๊ตฌ ์ฌํญ
Only one external dependency is needed:
pip install sv-ttk
Everything else comes from Pythonโs standard library.
๐ง ๋์์ธ ๊ฐ์
์ฑ์ ๋ ผ๋ฆฌ์ ์ธ ์น์ ์ผ๋ก ๊ตฌ์ฑ๋ฉ๋๋ค:
- Helpers โ ์ํ ์ ๋ฐ์ดํธ, ํฌ๋งทํ , ๊ณ์ฐ ๋๋ผ์ด๋ฒ
- Validation โ ์ ๊ท์ ๊ธฐ๋ฐ ์ซ์ ์ ๋ ฅ ํํฐ๋ง
- Statistics Logic โ
statistics๋ชจ๋ ์ฌ์ฉ - UI Layout โ ์นด๋, ๋ผ๋ฒจ, ๋ฒํผ ๋ฐ ๊ฒฐ๊ณผ ํจ๋
- UX Enhancements โ ํ๋ ์ด์คํ๋ ํ ์คํธ, ์ํ ๋ฐ, ์ ๋ฐ๋ ์ ์ด
๐งฎ ๊ณ์ฐ์ด ์๋ํ๋ ๋ฐฉ์
์ฌ์ฉ์๋ ์ซ์๋ฅผ ์ผํ๋ก ๊ตฌ๋ถ๋ ๋ชฉ๋ก์ผ๋ก ์ ๋ ฅํฉ๋๋ค. ์:
1, 2, 3, 4, 5
์ฑ์ ๋ค์๊ณผ ๊ฐ์ด ์ฒ๋ฆฌํฉ๋๋ค:
- ์ ๋ ฅ์ ํ์ฑํ๊ณ ์ ์ ํฉ๋๋ค
- ๊ฐ์
float๋ก ๋ณํํฉ๋๋ค - ํต๊ณ๋ฅผ ์์ ํ๊ฒ ๊ณ์ฐํฉ๋๋ค
- ์ ํ๋ ์ ๋ฐ๋์ ๋ฐ๋ผ ์ถ๋ ฅ์ ํฌ๋งทํฉ๋๋ค
ํน์ ๊ฒฝ์ฐ(์: ๊ณ ์ ๋ชจ๋๊ฐ ์๊ฑฐ๋ ๊ฐ์ด ํ๋๋ฟ์ธ ๊ฒฝ์ฐ)๋ ์ ์์ ์ผ๋ก ์ฒ๋ฆฌ๋ฉ๋๋ค.
๐งโ๐ป ์ ์ฒด ์์ค ์ฝ๋
import tkinter as tk
from tkinter import ttk, messagebox
import sv_ttk
import statistics
import re
# =========================
# Helpers
# =========================
def set_status(msg):
status_var.set(msg)
root.update_idletasks()
def fmt(value):
try:
p = precision_var.get()
return f"{value:.{p}f}"
except Exception:
return str(value)
def calculate_stats():
raw_input = numbers_entry.get()
if raw_input.strip() == 'e.g., 1, 2, 3, 4':
messagebox.showerror("Invalid Input", "Please enter a valid list of numbers.")
return
try:
numbers = [float(x.strip()) for x in raw_input.split(",") if x.strip() != ""]
if not numbers:
raise ValueError
mean_val = statistics.mean(numbers)
median_val = statistics.median(numbers)
try:
mode_val = statistics.mode(numbers)
except statistics.StatisticsError:
mode_val = "No unique mode"
variance_val = statistics.variance(numbers) if len(numbers) > 1 else 0
stdev_val = statistics.stdev(numbers) if len(numbers) > 1 else 0
result_var.set(
f"๐ Mean: {fmt(mean_val)}\n"
f"๐ Median: {fmt(median_val)}\n"
f"๐ฏ Mode: {mode_val}\n"
f"๐ Variance: {fmt(variance_val)}\n"
f"๐ Std. Dev.: {fmt(stdev_val)}\n"
f"๐ฝ Min: {fmt(min(numbers))}\n"
f"๐ผ Max: {fmt(max(numbers))}"
)
set_status("โ
Statistics calculated successfully")
except ValueError:
messagebox.showerror("Invalid Input", "Please enter a valid list of numbers.")
def validate_input(new_value):
if new_value == '' or new_value == 'e.g., 1, 2, 3, 4':
return True
return re.match(r'^[0-9.,\s-]*$', new_value) is not None
# =========================
# App Setup
# =========================
root = tk.Tk()
root.title("StatMate - Full Statistics Tool")
root.geometry("950x650")
root.minsize(950, 650)
sv_ttk.set_theme("light")
precision_var = tk.IntVar(value=4)
status_var = tk.StringVar(value="Ready")
ttk.Label(root, textvariable=status_var, anchor="w",
font=("Segoe UI", 10), padding=(10, 5)
).pack(side="bottom", fill="x")
main_frame = ttk.Frame(root, padding=20)
main_frame.pack(expand=True, fill="both")
ttk.Label(main_frame, text="StatMate",
font=("Segoe UI", 28, "bold")
).pack(anchor="w")
ttk.Label(main_frame, text="Full Statistics Calculator",
font=("Segoe UI", 14), foreground="#555"
).pack(anchor="w", pady=(0, 20))
input_card = ttk.LabelFrame(main_frame, text="Input Numbers & Precision", padding=20)
input_card.pack(fill="x", pady=(0, 20))
input_row = ttk.Frame(input_card)
input_row.pack(fill="x")
def on_entry_click(event):
if numbers_entry.get() == 'e.g., 1, 2, 3, 4':
numbers_entry.delete(0, "end")
numbers_entry.config(fg='black')
def on_focusout(event):
if numbers_entry.get() == '':
numbers_entry.insert(0, 'e.g., 1, 2, 3, 4')
numbers_entry.config(fg='grey')
ttk.Label(input_row, text="Numbers:").grid(row=0, column=0)
numbers_entry = tk.Entry(input_row, font=("Segoe UI", 14), width=40, fg='grey')
numbers_entry.insert(0, 'e.g., 1, 2, 3, 4')
numbers_entry.bind('', on_entry_click)
numbers_entry.bind('', on_focusout)
numbers_entry.grid(row=0, column=1, padx=(5, 20))
vcmd = (root.register(validate_input), '%P')
numbers_entry.config(validate='key', validatecommand=vcmd)
ttk.Label(input_row, text="Decimal Precision:").grid(row=0, column=2)
ttk.Spinbox(input_row, from_=0, to=10,
textvariable=precision_var, width=5
).grid(row=0, column=3, padx=(5, 0))
ttk.Button(main_frame, text="๐ Calculate Statistics",
command=calculate_stats,
style="Accent.TButton"
).pack(pady=20, ipadx=10, ipady=10)
result_card = ttk.LabelFrame(main_frame, text="Results", padding=20)
result_card.pack(fill="both", expand=True)
result_var = tk.StringVar(value="โ")
tk.Label(result_card, textvariable=result_var,
font=("Segoe UI", 16, "bold"),
justify="left", anchor="nw",
bd=2, relief="groove", padx=10, pady=10
).pack(fill="both", expand=True)
root.mainloop()
๐ Possible Improvements
- CSV๋ก ๊ฒฐ๊ณผ ๋ด๋ณด๋ด๊ธฐ
- ํ์คํ ๊ทธ๋จ ์๊ฐํ ์ถ๊ฐ
- ๋คํฌโ๋ชจ๋ ํ ๊ธ
- ํค๋ณด๋ ๋จ์ถํค
- ๋ฐ์ดํฐ์ ์ ์ฅ / ๋ถ๋ฌ์ค๊ธฐ
๐ฏ Final Thoughts
Tkinter๊ฐ ๊ตฌ์์ผ ํ์๋ ์์ต๋๋ค. ์ฝ๊ฐ์ ๊ตฌ์กฐ, ๊ฒ์ฆ, ๊ทธ๋ฆฌ๊ณ ํ๋์ ์ธ ํ ๋ง๋ฅผ ์ ์ฉํ๋ฉด Python์ด ์คํ๋๋ ์ด๋์๋ ์๋ํ๋ ์ธ๋ จ๋๊ณ ๊ธฐ๋ฅ์ ์ธ ํต๊ณ ๋๊ตฌ๋ฅผ ์ ๊ณตํ ์ ์์ต๋๋ค. ์ฆ๊ฑฐ์ด ์ฝ๋ฉ ๋์ธ์!
์ด ํ๋ก์ ํธ๊ฐ ๋ง์์ ๋ค์๋ค๋ฉด ํ์ฅํ๊ฑฐ๋ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ํต๊ณ ํดํท์ผ๋ก ์ ํํด ๋ณด์ธ์.
์ฆ๊ฑฐ์ด ๊ตฌ์ถ ๋์ธ์! ๐ง ๐
