سطح مقدماتی (Beginner Level)
کتابخانه Tkinter (تیکینتر) رابط استاندارد پایتون برای بسته ابزار گرافیکی Tcl/Tk است. این کتابخانه به شما اجازه میدهد تا به سادگی و سرعت، برنامههای دسکتاپ با رابط کاربری گرافیکی (GUI) بسازید. از آنجا که Tkinter به صورت پیشفرض با نصب پایتون همراه است، نیازی به نصب پکیجهای اضافی ندارید و انتخابی عالی برای شروع برنامهنویسی گرافیکی است.
در این سطح، با ساختار پنجرهها، ویجتهای اصلی (دکمه، برچسب، ورودی) و نحوه چیدمان آنها آشنا میشویم.
۱. ساخت اولین پنجره (The Main Window)
هر برنامه Tkinter با یک پنجره اصلی (Root Window) شروع میشود. این پنجره ظرفی است که تمام اجزای دیگر برنامه درون آن قرار میگیرند. برای نمایش پنجره، باید حلقه اصلی رویدادها (mainloop) را اجرا کنیم که برنامه را باز نگه میدارد.
مثال اول: سادهترین پنجره ممکن
این کد یک پنجره خالی ایجاد میکند و منتظر تعامل کاربر میماند.
# Static Code: Requires a display environment to render the window
import tkinter as tk
# ایجاد پنجره اصلی
root = tk.Tk()
# اجرای حلقه اصلی (برنامه اینجا متوقف میشود تا پنجره بسته شود)
root.mainloop()
مثال دوم: تنظیمات پنجره (عنوان و اندازه)
در این مثال، عنوان پنجره و ابعاد آن را مشخص میکنیم و از تغییر اندازه آن توسط کاربر جلوگیری میکنیم.
# Static Code: GUI Script
import tkinter as tk
root = tk.Tk()
# تنظیم عنوان پنجره
root.title("خانه پایتون - اولین برنامه")
# تنظیم ابعاد: عرضxارتفاع
root.geometry("400x300")
# جلوگیری از تغییر سایز (False برای عرض و ارتفاع)
root.resizable(False, False)
root.mainloop()
۲. ویجتهای پایه (Basic Widgets)
ویجتها (Widgets) اجزای سازنده رابط کاربری هستند. پرکاربردترین آنها عبارتند از:
- Label: برای نمایش متن یا تصویر ثابت.
- Button: دکمهای که کاربر میتواند روی آن کلیک کند.
- Entry: کادری برای دریافت متن تکخطی از کاربر.
مثال اول: استفاده از Label و Button
در این مثال یک متن نمایش میدهیم و یک دکمه که تابعی را در کنسول اجرا میکند.
# Static Code: GUI Logic with Console Output
import tkinter as tk
def on_click():
print("دکمه کلیک شد!")
root = tk.Tk()
root.geometry("300x200")
# ساخت برچسب
label = tk.Label(root, text="سلام به خانه پایتون!", font=("Arial", 14))
label.pack() # نمایش در پنجره
# ساخت دکمه
btn = tk.Button(root, text="کلیک کنید", command=on_click)
btn.pack()
root.mainloop()
مثال دوم: دریافت ورودی با Entry
در اینجا نام کاربر را دریافت کرده و با زدن دکمه، آن را چاپ میکنیم.
# Static Code: GUI Input Handling
import tkinter as tk
def show_name():
# دریافت متن از ویجت Entry
name = name_entry.get()
print(f"کاربر وارد شده: {name}")
root = tk.Tk()
root.geometry("300x150")
tk.Label(root, text="نام خود را وارد کنید:").pack()
name_entry = tk.Entry(root)
name_entry.pack()
submit_btn = tk.Button(root, text="تایید", command=show_name)
submit_btn.pack()
root.mainloop()
۳. مدیریت چیدمان (Geometry Managers)
در Tkinter، صرفاً ساختن ویجت کافی نیست؛ باید به پایتون بگویید آن را کجای پنجره قرار دهد. سه متد اصلی وجود دارد:
- pack(): چیدمان جعبهای (سادهترین روش، ویجتها را پشت سر هم میچیند).
- grid(): چیدمان جدولی (سطر و ستون).
- place(): مختصات دقیق پیکسلی (کمترین انعطافپذیری).
مثال اول: استفاده از Pack (چیدمان ساده)
ویجتها به ترتیب عمودی یا افقی اضافه میشوند.
# Static Code: Pack Layout
import tkinter as tk
root = tk.Tk()
# fill=tk.X باعث میشود ویجت عرض پنجره را پر کند
tk.Label(root, text="Header", bg="red", fg="white").pack(fill=tk.X)
tk.Label(root, text="Content", bg="blue", fg="white").pack(fill=tk.BOTH, expand=True)
tk.Label(root, text="Footer", bg="green", fg="white").pack(fill=tk.X)
root.mainloop()
مثال دوم: استفاده از Grid (چیدمان فرممانند)
مناسب برای فرمهای ورود اطلاعات.
# Static Code: Grid Layout
import tkinter as tk
root = tk.Tk()
tk.Label(root, text="نام کاربری:").grid(row=0, column=0)
tk.Entry(root).grid(row=0, column=1)
tk.Label(root, text="رمز عبور:").grid(row=1, column=0)
tk.Entry(root, show="*").grid(row=1, column=1)
tk.Button(root, text="ورود").grid(row=2, column=0, columnspan=2)
root.mainloop()
سطح پیشرفته (Professional Level)
در سطح حرفهای، از کدنویسی اسکریپتی فاصله میگیریم و به سراغ ساختار شیگرا (OOP)، مدیریت پیشرفته رویدادها، متغیرهای کنترلی Tkinter و ظاهر مدرنتر با ttk میرویم. همچنین بحث مهم "فریز نشدن رابط کاربری" (Threading) را بررسی میکنیم.
۱. ساختار شیگرا (OOP Structure)
نوشتن کدهای GUI به صورت اسکریپتی (Global Scope) در پروژههای بزرگ باعث شلوغی و غیرقابل مدیریت شدن کد میشود. روش استاندارد، ارثبری از کلاس tk.Tk یا tk.Frame است.
مثال اول: کلاس اصلی برنامه
در این روش، کل برنامه در یک کلاس کپسوله میشود.
# Static Code: OOP Structure in Tkinter
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title("OOP Tkinter App")
self.geometry("400x300")
# مقداردهی اولیه UI
self.create_widgets()
def create_widgets(self):
self.label = tk.Label(self, text="برنامه شیگرا")
self.label.pack(pady=20)
self.btn = tk.Button(self, text="خروج", command=self.quit)
self.btn.pack()
if __name__ == "__main__":
app = App()
app.mainloop()
مثال دوم: ماژولار کردن با Frame
میتوان بخشهای مختلف برنامه (مثل سایدبار یا فرم اصلی) را به کلاسهای جداگانه تقسیم کرد.
# Static Code: Modular Frame Components
import tkinter as tk
class LoginForm(tk.Frame):
def __init__(self, parent):
super().__init__(parent, padx=10, pady=10, borderwidth=2, relief="groove")
self.pack()
tk.Label(self, text="Username:").pack()
self.user_entry = tk.Entry(self)
self.user_entry.pack()
class MainApp(tk.Tk):
def __init__(self):
super().__init__()
self.title("Modular App")
# استفاده از کامپوننت لاگین
self.login = LoginForm(self)
tk.Button(self, text="Check", command=self.check_login).pack(pady=10)
def check_login(self):
print(f"Checking: {self.login.user_entry.get()}")
if __name__ == "__main__":
app = MainApp()
app.mainloop()
۲. متغیرهای کنترلی و بایندینگ (Variables & Binding)
برای اتصال منطق برنامه به رابط کاربری به صورت پویا، از متغیرهای Tkinter (StringVar, IntVar) و سیستم رویداد bind استفاده میکنیم. این متغیرها قابلیت "Trace" دارند، یعنی با تغییر مقدار متغیر، ویجت متصل به آن خودکار آپدیت میشود.
مثال اول: آپدیت خودکار متن (StringVar)
در این مثال، هر چه در Entry بنویسید، همزمان در Label نمایش داده میشود.
# Static Code: Dynamic String Variable
import tkinter as tk
root = tk.Tk()
# تعریف متغیر مخصوص Tkinter
text_var = tk.StringVar()
text_var.set("اینجا بنویسید...")
entry = tk.Entry(root, textvariable=text_var)
entry.pack()
# برچسبی که به همان متغیر متصل است
label = tk.Label(root, textvariable=text_var, fg="blue")
label.pack()
root.mainloop()
مثال دوم: مدیریت رویدادهای ماوس و کیبورد (Bind)
متد bind به شما اجازه میدهد هر رویدادی (کلیک راست، دکمه اینتر، حرکت ماوس) را به یک تابع متصل کنید.
# Static Code: Event Binding
import tkinter as tk
def on_enter_key(event):
print("دکمه Enter فشرده شد!")
def on_left_click(event):
print(f"کلیک چپ در موقعیت: {event.x}, {event.y}")
root = tk.Tk()
lbl = tk.Label(root, text="اینجا کلیک کنید یا Enter بزنید")
lbl.pack(padx=50, pady=50)
# اتصال کلید اینتر به پنجره
root.bind('<Return>', on_enter_key)
# اتصال کلیک چپ به برچسب خاص
lbl.bind('<Button-1>', on_left_click)
root.mainloop()
۳. ظاهر مدرن با tkinter.ttk
ویجتهای استاندارد Tkinter ظاهر قدیمی (ویندوز 95) دارند. ماژول ttk (Themed Tk) ویجتهایی با ظاهر بومی سیستم عامل و قابلیت استایلدهی پیشرفته ارائه میدهد.
مثال اول: مقایسه دکمه ساده و مدرن
# Static Code: TTK vs Standard Widgets
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.geometry("300x100")
# دکمه قدیمی
btn_old = tk.Button(root, text="Standard Button", bg="gray")
btn_old.pack(pady=5)
# دکمه مدرن (TTK)
btn_new = ttk.Button(root, text="Themed Button")
btn_new.pack(pady=5)
root.mainloop()
مثال دوم: استایلدهی در TTK
برخلاف Tkinter استاندارد که رنگ را مستقیم در ویجت میدهید، در ttk باید از Style استفاده کنید.
# Static Code: TTK Styling
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
style = ttk.Style()
# تعریف یک استایل جدید برای دکمه
style.configure("Danger.TButton", foreground="red", font=("Helvetica", 12, "bold"))
btn = ttk.Button(root, text="دکمه خطر", style="Danger.TButton")
btn.pack(padx=20, pady=20)
root.mainloop()
۴. جلوگیری از هنگ کردن UI (Threading)
اگر عملیات سنگینی (مثل دانلود فایل یا محاسبه طولانی) را در ترد اصلی (Main Thread) اجرا کنید، رابط کاربری فریز میشود. برای حل این مشکل باید از ماژول threading استفاده کرد.
مثال اول: مشکل فریز شدن (کد بد)
در این کد، هنگام اجرای heavy_task، پنجره پاسخگو نیست و نمیتوانید آن را تکان دهید.
# Static Code: Blocking UI (Do not use in production)
import tkinter as tk
import time
def heavy_task():
time.sleep(5) # شبیهسازی کار سنگین ۵ ثانیهای
print("کار تمام شد")
root = tk.Tk()
tk.Button(root, text="شروع کار سنگین (Freeze)", command=heavy_task).pack()
root.mainloop()
مثال دوم: راه حل با Threading (کد حرفهای)
عملیات سنگین در یک ترد جداگانه اجرا میشود و رابط کاربری روان باقی میماند.
# Static Code: Implementing Threading in Tkinter
import tkinter as tk
import threading
import time
def run_heavy_task():
# غیرفعال کردن دکمه برای جلوگیری از کلیک مجدد
btn.config(state="disabled")
def task():
time.sleep(3) # شبیهسازی
print("Task Done")
# برگرداندن دکمه به حالت فعال (باید در ترد اصلی انجام شود؟ در Tkinter ساده معمولا مشکلی نیست اما امنتر است از after استفاده شود)
btn.config(state="normal")
# ساخت و شروع ترد جدید
threading.Thread(target=task, daemon=True).start()
root = tk.Tk()
btn = tk.Button(root, text="Start Async Task", command=run_heavy_task)
btn.pack(pady=20)
root.mainloop()