ساخت ماشین حساب گرافیکی با Python و Tkinter

آموزش کامل ساخت ماشین حساب گرافیکی با Python و Tkinter؛ از پنجره و نمایشگر تا دکمهها، منطق محاسبه و مدیریت خطا، به همراه کد کامل و لینک سورس در گیتهاب.
ساخت ماشین حساب گرافیکی با Python و Tkinter
مقدمه
در مقاله قبلی با Tkinter آشنا شدید و یک رابط گرافیکی ساده (مبدل دما) ساختید. در این مقاله همان مفاهیم را در یک پروژه واقعیتر و کاملتر به کار میگیریم: یک ماشین حساب گرافیکی با چهار عمل اصلی (جمع، تفریق، ضرب، تقسیم)، پشتیبانی از اعداد اعشاری و مدیریت خطا.
این پروژه برای کسانی مناسب است که مفاهیم پایه Tkinter — یعنی پنجره، Widget، چیدمان و رویداد — را میشناسند و میخواهند آنها را در یک برنامه واقعیتر ببینند. در پایان مقاله، دقیقاً همین ماشین حساب را ساختهاید و میفهمید هر خط از کدش چه کاری انجام میدهد.
Tkinter چیست؟
Tkinter کتابخانه استاندارد Python برای ساخت رابط گرافیکی (GUI) است. با آن میتوانید پنجره، دکمه، فیلد ورودی و سایر عناصر بصری بسازید، بدون اینکه نیاز به نصب کتابخانه جانبی داشته باشید. اگر با مفاهیم پایهاش آشنا نیستید، پیشنهاد میکنم اول آموزش Tkinter و ساخت رابط گرافیکی در Python را بخوانید؛ این مقاله از همانجا ادامه پیدا میکند.
پیشنیازها
برای دنبال کردن این آموزش به این دو مورد نیاز دارید:
- Python 3 نصبشده روی سیستم (اگر هنوز نصب نکردهاید، راهنمای نصب Python در ویندوز را ببینید).
- Tkinter، که در نصب معمول Python از قبل در دسترس است و نیازی به نصب جداگانه ندارد.
ساخت پنجره اصلی
اولین قدم، ساخت پنجرهای است که کل ماشین حساب داخلش قرار میگیرد:
import tkinter as tk
window = tk.Tk()
window.title("Python Calculator")
window.geometry("350x500")
window.resizable(False, False)
tk.Tk()پنجره اصلی برنامه را میسازد.title()عنوان پنجره را روی «Python Calculator» تنظیم میکند.geometry("350x500")اندازه پنجره را ۳۵۰ در ۵۰۰ پیکسل مشخص میکند — اندازهای که برای چیدمان عمودی دکمههای ماشین حساب مناسب است.resizable(False, False)تغییر اندازه پنجره را هم در عرض و هم در ارتفاع غیرفعال میکند. این کار برای ماشین حساب منطقی است، چون چیدمان دکمهها طوری طراحی نشده که با تغییر اندازه پنجره هم زیبا بماند.
ساخت نمایشگر ماشین حساب
نمایشگر همان جایی است که عددها و نتیجه محاسبه نشان داده میشود. برایش از Widget آشنای Entry استفاده میکنیم:
display = tk.Entry(
window,
font=("Arial", 28),
justify="right",
bd=0
)
display.pack(
padx=15,
pady=20,
fill="x",
ipady=15
)
font=("Arial", 28)اندازه فونت نمایشگر را بزرگ میکند تا اعداد بهخوبی خوانده شوند.justify="right"متن را از سمت راست نمایشگر میچیند — دقیقاً رفتاری که در ماشین حسابهای واقعی هم میبینید.bd=0حاشیه (border) پیشفرض دور Entry را حذف میکند تا ظاهر تمیزتری داشته باشد.- در
pack()، پارامترfill="x"باعث میشود نمایشگر تمام عرض پنجره را بگیرد وipady=15هم ارتفاع داخلی آن را افزایش میدهد تا فضای بیشتری برای نمایش عدد داشته باشد.
ساخت دکمهها
بهجای اینکه هر دکمه را تکتک و جداگانه بنویسیم، دکمهها را در قالب یک لیست دوبعدی تعریف میکنیم — هر ردیف داخلی، یک ردیف از دکمههای ماشین حساب را نشان میدهد:
buttons = [
["7", "8", "9", "÷"],
["4", "5", "6", "×"],
["1", "2", "3", "-"],
[".", "0", "C", "+"],
]
چرا این کار را به این شکل انجام میدهیم؟ اگر بخواهیم هر دکمه را با یک خط کد جداگانه بسازیم، برای ۱۶ دکمه باید ۱۶ بار کد تقریباً یکسان تکرار کنیم. با تعریف دکمهها بهصورت لیست، میتوانیم با یک حلقه ساده همهشان را بسازیم — کد کوتاهتر، خواناتر و قابلنگهداریتر میشود.
حالا با یک حلقه تودرتو، برای هر ردیف یک Frame میسازیم و دکمههای همان ردیف را داخلش قرار میدهیم:
for row in buttons:
frame = tk.Frame(window)
frame.pack(
expand=True,
fill="both"
)
for value in row:
tk.Button(
frame,
text=value,
font=("Arial", 18),
command=lambda v=value: button_click(v)
).pack(
side="left",
expand=True,
fill="both",
padx=3,
pady=3
)
نکتههای مهم این بخش:
- هر ردیف از دکمهها داخل یک
Frameجداگانه قرار میگیرد؛ این کار چیدمان دکمهها را بهصورت شبکهای (چند ردیف، چند ستون) منظم میکند، بدون اینکه مستقیماً ازgrid()استفاده کنیم. command=lambda v=value: button_click(v)بخشی است که خیلی از مبتدیها گیج میکند. تابعlambdaیک تابع کوچک و بدوننام است؛ اینجا از آن استفاده میکنیم چونcommandباید یک تابع بدون آرگومان صدا بزند، ولی ما میخواهیم بدانیم دقیقاً کدام دکمه کلیک شده. باlambda v=value: button_click(v)، مقدار همان دکمه (value) را در لحظه ساختن دکمه ثبت میکنیم و بهbutton_clickپاس میدهیم.- اگر بهجای
lambdaمستقیم بنویسیمcommand=button_click(value)، دقیقاً مثل مشکلی که در مقاله قبلی هم دیدیم (پرانتز یعنی اجرای فوری)، تابع همان لحظه اجرا میشود، نه با کلیک روی دکمه.
نوشتن منطق ماشین حساب
حالا وقتش رسیده منطق واقعی ماشین حساب را بنویسیم — یعنی چهار تابعی که تعیین میکنند با کلیک روی هر دکمه چه اتفاقی بیفتد.
add_to_display
def add_to_display(value):
display.insert(tk.END, value)
این تابع مقدار دریافتی (یک عدد یا یک عملگر) را به انتهای متن فعلی نمایشگر اضافه میکند. tk.END یعنی «انتهای متن»، پس هر کاراکتر جدید همیشه بعد از کاراکترهای قبلی نوشته میشود.
clear_display
def clear_display():
display.delete(0, tk.END)
این تابع تمام محتوای نمایشگر — از کاراکتر شماره صفر تا انتها — را پاک میکند. از این تابع هم برای دکمه «C» و هم قبل از نمایش نتیجه یا خطا استفاده میشود.
calculate
def calculate():
expression = display.get()
if not expression:
return
try:
result = eval(expression, {"__builtins__": None}, {})
clear_display()
display.insert(0, result)
except (SyntaxError, TypeError, ZeroDivisionError):
clear_display()
display.insert(0, "Error")
این تابع قلب ماشین حساب است. عبارت نوشتهشده در نمایشگر را میخواند (مثلاً "12+5") و با تابع آماده پایتون eval() آن را محاسبه میکند. اگر نمایشگر خالی باشد، تابع بدون انجام کاری تمام میشود. اگر محاسبه با موفقیت انجام شود، نمایشگر پاک شده و نتیجه در آن نوشته میشود؛ در غیر این صورت (مثلاً عبارت نامعتبر یا تقسیم بر صفر)، بهجای کرش کردن برنامه، پیام «Error» نمایش داده میشود — درباره این بخش در ادامه بیشتر توضیح میدهیم.
button_click
def button_click(value):
if value == "C":
clear_display()
elif value == "=":
calculate()
else:
if display.get() == "Error":
clear_display()
value = value.replace("×", "*").replace("÷", "/")
add_to_display(value)
این تابع تصمیم میگیرد با کلیک روی هر دکمه چه کاری انجام شود:
- اگر دکمه «C» زده شود، نمایشگر پاک میشود.
- اگر دکمه «=» زده شود، تابع
calculate()صدا زده میشود. - برای بقیه دکمهها (اعداد و عملگرها)، ابتدا بررسی میشود که آیا نمایشگر در حال حاضر «Error» نشان میدهد یا نه — اگر بله، ابتدا پاک میشود تا کاربر مجبور نباشد دستی آن را پاک کند. سپس نمادهای نمایشی
×و÷به معادل واقعیشان در پایتون (*و/) تبدیل میشوند، چون تابعeval()این نمادهای ریاضی را نمیشناسد. در نهایت مقدار به نمایشگر اضافه میشود.
مدیریت خطا
بخش try/except داخل تابع calculate() یکی از مهمترین قسمتهای این پروژه است:
try:
result = eval(expression, {"__builtins__": None}, {})
clear_display()
display.insert(0, result)
except (SyntaxError, TypeError, ZeroDivisionError):
clear_display()
display.insert(0, "Error")
بدون این بخش، اگر کاربر عبارتی نامعتبر بنویسد (مثلاً "5+" یا "5/0")، پایتون یک خطا (Exception) صادر میکند که اگر مدیریت نشود، کل برنامه با یک پیام خطای زشت در ترمینال متوقف میشود. با try/except، این خطاها را میگیریم و بهجای بستن برنامه، فقط پیام «Error» را در نمایشگر نشان میدهیم — کاربر میتواند بلافاصله دکمه بعدی را بزند و ادامه دهد.
سه نوع خطا اینجا مدیریت میشوند:
SyntaxErrorوقتی عبارت از نظر نحوی ناقص یا نادرست باشد (مثلاً"5+").TypeErrorوقتی نوع داده با عملگر سازگار نباشد.ZeroDivisionErrorدقیقاً همان چیزی است که از اسمش پیداست: تقسیم بر صفر — خطایی که تقریباً هر کاربری در ماشین حساب زودتر یا دیرتر با آن مواجه میشود.
کد کامل پروژه
این کد کامل و نهایی ماشین حساب است؛ همان چیزی که در Repository پروژه هم قرار دارد:
import tkinter as tk
def add_to_display(value):
display.insert(tk.END, value)
def clear_display():
display.delete(0, tk.END)
def calculate():
expression = display.get()
if not expression:
return
try:
result = eval(expression, {"__builtins__": None}, {})
clear_display()
display.insert(0, result)
except (SyntaxError, TypeError, ZeroDivisionError):
clear_display()
display.insert(0, "Error")
def button_click(value):
if value == "C":
clear_display()
elif value == "=":
calculate()
else:
if display.get() == "Error":
clear_display()
value = value.replace("×", "*").replace("÷", "/")
add_to_display(value)
window = tk.Tk()
window.title("Python Calculator")
window.geometry("350x500")
window.resizable(False, False)
display = tk.Entry(
window,
font=("Arial", 28),
justify="right",
bd=0
)
display.pack(
padx=15,
pady=20,
fill="x",
ipady=15
)
buttons = [
["7", "8", "9", "÷"],
["4", "5", "6", "×"],
["1", "2", "3", "-"],
[".", "0", "C", "+"],
]
for row in buttons:
frame = tk.Frame(window)
frame.pack(
expand=True,
fill="both"
)
for value in row:
tk.Button(
frame,
text=value,
font=("Arial", 18),
command=lambda v=value: button_click(v)
).pack(
side="left",
expand=True,
fill="both",
padx=3,
pady=3
)
tk.Button(
window,
text="=",
font=("Arial", 20),
command=calculate
).pack(
fill="both",
padx=15,
pady=10,
ipady=8
)
window.mainloop()
اجرای پروژه
برای اجرای ماشین حساب، فایل بالا را با نام calculator.py ذخیره کنید و در ترمینال این دستور را بزنید:
python calculator.py
با اجرای این دستور، پنجره ماشین حساب باز میشود و میتوانید بلافاصله با کلیک روی دکمهها محاسبات ساده انجام دهید.
لینک سورس کد
سورس کامل این پروژه، به همراه فایل README و ساختار پوشهبندی، در گیتهاب در دسترس است:
github.com/taha-heidaryfard/python-tkinter-calculator
جمعبندی
در این مقاله یک ماشین حساب گرافیکی واقعی با Python و Tkinter ساختید: از پنجره اصلی و نمایشگر شروع کردید، دکمهها را با یک لیست و حلقه ساختید، منطق محاسبه را با چهار تابع کوچک پیاده کردید، و در نهایت با try/except جلوی بسته شدن ناگهانی برنامه در برابر خطاهایی مثل تقسیم بر صفر را گرفتید. همین الگو — پنجره، Widget، رویداد، مدیریت خطا — پایه تقریباً هر برنامه گرافیکی دیگری است که با Tkinter بسازید.
با این مقاله، مسیر مقدماتی این مجموعه هم کامل شد: از پایتون چیست؟ و راهنمای نصب پایتون در ویندوز تا آموزش Tkinter و همین ماشینحساب گرافیکی. اگر جایی از مسیر جا ماندید یا خواستید دوباره مرورش کنید، هر مقاله در دسترس است.
سوالات متداول
Tkinter چیست؟
Tkinter کتابخانه استاندارد Python برای ساخت رابط گرافیکی (GUI) است که همراه نصب معمول Python در دسترس قرار میگیرد.
آیا Tkinter همراه Python نصب میشود؟
بله، در نصب معمول Python (چه با نصبکننده رسمی و چه با winget)، Tkinter از قبل در دسترس است و نیازی به نصب جداگانه ندارید.
چگونه یک ماشین حساب با Python بسازیم؟
با استفاده از Tkinter میتوانید یک پنجره، یک Entry برای نمایشگر و چند Button برای اعداد و عملگرها بسازید؛ سپس با یک تابع ساده (مثل calculate در همین مقاله) عبارت واردشده را محاسبه کنید.
چگونه دکمه در Tkinter ایجاد کنیم؟
با کلاس tk.Button، متن دکمه را با پارامتر text و تابعی که باید با کلیک اجرا شود را با پارامتر command مشخص میکنید.
چگونه خطاهای ماشین حساب را مدیریت کنیم؟
با قرار دادن کد محاسبه داخل بلوک try/except و گرفتن خطاهایی مثل ZeroDivisionError، SyntaxError و TypeError؛ به این ترتیب بهجای بسته شدن برنامه، یک پیام خطای قابلفهم (مثل «Error») به کاربر نمایش داده میشود.