برگ تقلب tkinter پایتون
اگر پایتون بلدی ولی هر بار که میخوای یه پنجره ساده بسازی باید نصف روزت رو صرف گشتن توی مستندات کنی، این مقاله دقیقا برای توئه. tkinter کتابخانهای هستش که از قبل توی پایتون نصبه و بدون هیچ نصب اضافهای میتونی باهاش رابط کاربری گرافیکی بسازی. ساده، سریع، و همیشه دمدسته. این مقاله یه مرجع کامل و عملیه که همه چیز مهم tkinter رو از ویجتها گرفته تا layout و event handling پوشش میده.
شروع با tkinter
اولین قدم اینه که بدونیم چطور یه پنجره پایه بسازیم. ساختار کلی هر برنامه tkinter از همین چند خط شروع میشه:
import tkinter as tk
root = tk.Tk()
root.title("اسم برنامه")
root.geometry("800x600") # عرض x ارتفاع
root.resizable(True, True) # قابل تغییر اندازه بودن در محور x و y
root.mainloop()
متد mainloop() حلقه اصلی رویداد برنامه رو اجرا میکنه و باید همیشه آخرین خط باشه. این حلقه منتظر میمونه که کاربر کاری بکنه (کلیک، تایپ، بستن پنجره) و بهش واکنش نشون میده.
چند تا تنظیم مفید که اول کار باهاشون آشنا بشیم:
root.configure(bg="#f0f0f0") # رنگ پسزمینه پنجره اصلی
root.minsize(400, 300) # حداقل اندازه پنجره
root.maxsize(1200, 900) # حداکثر اندازه پنجره
root.attributes("-fullscreen", True) # تمامصفحه
root.iconbitmap("icon.ico") # آیکون پنجره (فقط ویندوز)
ویجتهای اصلی tkinter
Label — نمایش متن و تصویر
Label سادهترین ویجته. فقط یه متن یا تصویر نشون میده و کاربر نمیتونه باهاش تعامل مستقیم داشته باشه.
label = tk.Label(
root,
text="سلام دنیا",
font=("Arial", 14, "bold"),
fg="#333333", # رنگ متن
bg="#ffffff", # رنگ پسزمینه
width=20, # عرض به تعداد کاراکتر
height=2, # ارتفاع به تعداد خط
anchor="center", # جهتگیری متن (n, s, e, w, center, ...)
justify="left", # تراز متن چندخطی (left, right, center)
wraplength=200, # حداکثر عرض قبل از شکست خط (پیکسل)
relief="flat", # حاشیه (flat, raised, sunken, groove, ridge)
padx=10, # فاصله افقی داخلی
pady=5, # فاصله عمودی داخلی
cursor="hand2", # شکل موس روی ویجت
)
label.pack()
برای نمایش تصویر با Label:
from PIL import Image, ImageTk # نیاز به نصب Pillow داره
img = Image.open("photo.png")
img = img.resize((200, 150))
photo = ImageTk.PhotoImage(img)
label_img = tk.Label(root, image=photo)
label_img.image = photo # این خط مهمه! بدون این تصویر garbage collect میشه
label_img.pack()
Button — دکمه
def on_click():
print("دکمه کلیک شد")
btn = tk.Button(
root,
text="کلیک کن",
command=on_click,
font=("Arial", 12),
fg="white",
bg="#4CAF50",
activeforeground="white", # رنگ متن هنگام کلیک
activebackground="#45a049", # رنگ پسزمینه هنگام کلیک
width=15,
height=2,
relief="raised",
state="normal", # normal, disabled, active
cursor="hand2",
)
btn.pack(pady=10)
غیرفعال و فعال کردن دکمه:
btn.config(state="disabled") # غیرفعال کردن
btn.config(state="normal") # فعال کردن
Entry — فیلد ورودی تکخطی
entry = tk.Entry(
root,
font=("Arial", 12),
fg="#333",
bg="white",
width=30,
show="", # برای پسورد: show="*"
relief="sunken",
bd=2, # ضخامت حاشیه
state="normal", # normal, disabled, readonly
justify="left", # تراز متن داخل فیلد
)
entry.pack(pady=5)
# گرفتن مقدار
value = entry.get()
# تنظیم مقدار
entry.delete(0, tk.END) # پاک کردن محتوا
entry.insert(0, "مقدار پیشفرض") # درج متن از ابتدا
Text — فیلد ورودی چندخطی
text_widget = tk.Text(
root,
font=("Arial", 12),
fg="#333",
bg="white",
width=50,
height=10,
wrap="word", # شکست خط (none, char, word)
relief="sunken",
bd=2,
state="normal", # normal, disabled
undo=True, # فعال کردن Ctrl+Z
maxundo=20, # حداکثر تعداد undo
spacing3=5, # فاصله بین پاراگرافها
)
text_widget.pack()
# گرفتن تمام محتوا
content = text_widget.get("1.0", tk.END)
# گرفتن محتوا از خط 2، کاراکتر 0 تا آخر خط 2
line2 = text_widget.get("2.0", "2.end")
# درج متن
text_widget.insert(tk.END, "متن جدید")
text_widget.insert("1.0", "ابتدای متن")
# پاک کردن محتوا
text_widget.delete("1.0", tk.END)
# غیرفعال کردن (فقط خواندنی)
text_widget.config(state="disabled")
Checkbutton — چکباکس
var = tk.BooleanVar() # یا tk.IntVar() که 0 و 1 برمیگردونه
check = tk.Checkbutton(
root,
text="موافقم",
variable=var,
font=("Arial", 11),
fg="#333",
bg="#f0f0f0",
activebackground="#f0f0f0",
command=lambda: print(var.get()),
onvalue=True, # مقدار وقتی تیک خورده
offvalue=False, # مقدار وقتی تیک نخورده
)
check.pack()
# گرفتن و تنظیم مقدار
print(var.get()) # True یا False
var.set(True) # تیک زدن
var.set(False) # برداشتن تیک
Radiobutton — دکمه رادیویی
choice = tk.StringVar(value="option1")
for text, value in [("گزینه اول", "option1"), ("گزینه دوم", "option2"), ("گزینه سوم", "option3")]:
rb = tk.Radiobutton(
root,
text=text,
variable=choice,
value=value,
font=("Arial", 11),
command=lambda: print(choice.get()),
)
rb.pack(anchor="w")
# گرفتن مقدار انتخابشده
print(choice.get())
Combobox و Listbox
Listbox برای نمایش لیستی از گزینهها استفاده میشه:
listbox = tk.Listbox(
root,
font=("Arial", 11),
width=30,
height=8,
selectmode="single", # single, multiple, browse, extended
bg="white",
fg="#333",
selectbackground="#4CAF50",
activestyle="dotbox",
)
listbox.pack()
# اضافه کردن آیتمها
items = ["پایتون", "جاوااسکریپت", "راست", "گو"]
for item in items:
listbox.insert(tk.END, item)
# گرفتن آیتم انتخابشده
selected = listbox.curselection()
if selected:
value = listbox.get(selected[0])
# حذف یه آیتم
listbox.delete(0) # حذف اولین آیتم
listbox.delete(0, tk.END) # حذف همه آیتمها
برای Combobox باید از ttk استفاده کرد:
from tkinter import ttk
combo = ttk.Combobox(
root,
values=["پایتون", "جاوااسکریپت", "راست"],
state="readonly", # فقط انتخاب (نه تایپ)
width=20,
font=("Arial", 11),
)
combo.set("پایتون") # مقدار پیشفرض
combo.pack(pady=5)
# گرفتن مقدار
print(combo.get())
# رویداد تغییر
combo.bind("<<ComboboxSelected>>", lambda e: print(combo.get()))
Scale — اسلایدر
scale = tk.Scale(
root,
from_=0,
to=100,
orient="horizontal", # horizontal یا vertical
length=300, # طول اسلایدر به پیکسل
resolution=1, # گام تغییر
tickinterval=25, # فاصله بین نشانههای عددی
label="مقدار:",
font=("Arial", 10),
command=lambda v: print(f"مقدار: {v}"),
)
scale.set(50) # مقدار پیشفرض
scale.pack()
print(scale.get()) # گرفتن مقدار فعلی
Spinbox — ورودی عددی با کنترل
spinbox = tk.Spinbox(
root,
from_=1,
to=100,
width=10,
font=("Arial", 12),
increment=1,
wrap=False, # آیا بعد از max به min برگرده
state="normal",
)
spinbox.pack()
print(spinbox.get())
Scrollbar — نوار اسکرول
معمولا Scrollbar به تنهایی استفاده نمیشه و باید به یه ویجت دیگه مثل Text یا Listbox متصل بشه:
frame = tk.Frame(root)
frame.pack()
scrollbar = tk.Scrollbar(frame, orient="vertical")
scrollbar.pack(side="right", fill="y")
text_widget = tk.Text(
frame,
yscrollcommand=scrollbar.set,
width=40,
height=10,
)
text_widget.pack(side="left")
scrollbar.config(command=text_widget.yview)
مدیریت Layout
این بخش یکی از مهمترین بخشهای tkinter هستش. سه geometry manager داریم که هر کدوم برای موقعیتدهی ویجتها توی پنجره استفاده میشن.
pack — سادهترین روش
pack ویجتها رو یکی یکی کنار هم یا زیر هم قرار میده.
btn1 = tk.Button(root, text="دکمه ۱")
btn2 = tk.Button(root, text="دکمه ۲")
btn3 = tk.Button(root, text="دکمه ۳")
btn1.pack(side="top", pady=5) # بالا (پیشفرض)
btn2.pack(side="left", padx=5) # چپ
btn3.pack(side="right", padx=5) # راست
# گزینههای مهم pack:
label = tk.Label(root, text="متن")
label.pack(
side="top", # جهت قرارگیری: top, bottom, left, right
fill="x", # پر کردن فضا: none, x, y, both
expand=True, # آیا با تغییر اندازه پنجره گسترش پیدا کنه
padx=10, # فاصله افقی خارجی
pady=5, # فاصله عمودی خارجی
ipadx=5, # فاصله افقی داخلی
ipady=3, # فاصله عمودی داخلی
anchor="center", # جهتگیری داخل فضای اختصاصدادهشده
)
grid — چینش شبکهای
grid برای چینشهای پیچیدهتر بهتره. ویجتها رو توی یه جدول ردیف/ستون قرار میده.
tk.Label(root, text="نام:").grid(row=0, column=0, sticky="e", padx=5, pady=5)
tk.Entry(root).grid(row=0, column=1, padx=5, pady=5)
tk.Label(root, text="ایمیل:").grid(row=1, column=0, sticky="e", padx=5, pady=5)
tk.Entry(root).grid(row=1, column=1, padx=5, pady=5)
tk.Button(root, text="ثبت").grid(row=2, column=0, columnspan=2, pady=10)
# گزینههای مهم grid:
widget.grid(
row=0, # شماره ردیف (از 0 شروع میشه)
column=0, # شماره ستون (از 0 شروع میشه)
rowspan=1, # تعداد ردیفهایی که ویجت اشغال میکنه
columnspan=2, # تعداد ستونهایی که ویجت اشغال میکنه
sticky="nsew", # چسبیدن به کدام سمتها (n, s, e, w یا ترکیبشون)
padx=5,
pady=5,
ipadx=3,
ipady=3,
)
# تنظیم وزن ردیف/ستون برای responsive بودن
root.columnconfigure(1, weight=1) # ستون 1 با تغییر اندازه گسترش پیدا کنه
root.rowconfigure(0, weight=1)
place — موقعیتدهی دقیق
place برای موقعیتدهی دقیق با مختصات استفاده میشه. برای اکثر پروژهها توصیه نمیشه چون responsive نیست، ولی بعضی وقتا لازم میشه.
btn = tk.Button(root, text="دکمه")
# موقعیتدهی با پیکسل
btn.place(x=100, y=50)
# موقعیتدهی نسبی (بین 0 و 1)
btn.place(relx=0.5, rely=0.5, anchor="center") # وسط پنجره
# ترکیبی
btn.place(
x=10,
y=10,
relwidth=0.5, # عرض نسبی
relheight=0.1, # ارتفاع نسبی
anchor="nw", # نقطه لنگر ویجت
)
نکته مهم: مخلوط نکردن pack و grid
یکی از اشتباههای رایجی که همه باهاش مواجه میشن اینه که pack و grid رو توی یه container (پنجره یا Frame) با هم مخلوط کنن. این کار باعث میشه برنامه hang کنه. راهحل استفاده از Frame هستش:
# این کد crash میکنه:
# tk.Label(root, text="اول").pack()
# tk.Button(root, text="دوم").grid(row=0, column=0) # خطا!
# راهحل درست: هر Frame فقط یه geometry manager داشته باشه
top_frame = tk.Frame(root)
top_frame.pack(fill="x")
bottom_frame = tk.Frame(root)
bottom_frame.pack(fill="both", expand=True)
tk.Label(top_frame, text="هدر").pack() # pack توی top_frame
tk.Button(bottom_frame, text="دکمه").grid(row=0, column=0) # grid توی bottom_frame
Frame و LabelFrame — ظرفهای ویجت
Frame یه ظرف شفافه که ویجتها رو داخلش سازماندهی میکنیم:
frame = tk.Frame(
root,
bg="#e8e8e8",
bd=2,
relief="groove",
padx=10,
pady=10,
)
frame.pack(fill="both", expand=True, padx=10, pady=10)
# ویجتها داخل Frame
tk.Label(frame, text="داخل فریم", bg="#e8e8e8").pack()
tk.Button(frame, text="دکمه داخل فریم").pack(pady=5)
LabelFrame مثل Frame هستش ولی یه عنوان داره:
lf = tk.LabelFrame(
root,
text=" اطلاعات شخصی ",
font=("Arial", 10, "bold"),
fg="#333",
bg="#f5f5f5",
padx=15,
pady=10,
relief="groove",
)
lf.pack(fill="x", padx=10, pady=10)
tk.Label(lf, text="نام:", bg="#f5f5f5").grid(row=0, column=0, sticky="e")
tk.Entry(lf).grid(row=0, column=1)
Tkinter Variables — متغیرهای ردیاب
tk.StringVar، tk.IntVar، tk.DoubleVar و tk.BooleanVar متغیرهایی هستن که وقتی مقدارشون تغییر میکنه، ویجتهای مرتبط با اونها هم بهروز میشن. این یه ویژگی خیلی مفیده:
name_var = tk.StringVar(value="پیشفرض")
age_var = tk.IntVar(value=0)
price_var = tk.DoubleVar(value=0.0)
agreed_var = tk.BooleanVar(value=False)
# متصل کردن به ویجت
entry = tk.Entry(root, textvariable=name_var)
entry.pack()
label = tk.Label(root, textvariable=name_var)
label.pack()
# حالا هر تغییری توی entry، label رو هم آپدیت میکنه!
# trace — اجرای تابع با هر تغییر متغیر
def on_change(*args):
print(f"مقدار جدید: {name_var.get()}")
name_var.trace_add("write", on_change) # "write", "read", "unset"
Event Handling — مدیریت رویداد
bind — اتصال رویداد به ویجت
def on_click(event):
print(f"کلیک در مختصات: {event.x}, {event.y}")
widget.bind("<Button-1>", on_click) # کلیک چپ ماوس
widget.bind("<Button-2>", on_click) # کلیک وسط ماوس
widget.bind("<Button-3>", on_click) # کلیک راست ماوس
widget.bind("<Double-Button-1>", on_click) # دابلکلیک چپ
widget.bind("<MouseWheel>", on_scroll) # اسکرول ماوس
widget.bind("<Enter>", on_enter) # ماوس وارد ویجت شد
widget.bind("<Leave>", on_leave) # ماوس از ویجت خارج شد
widget.bind("<Motion>", on_move) # حرکت ماوس روی ویجت
widget.bind("<Key>", on_key) # هر کلید
widget.bind("<Return>", on_enter_key) # Enter
widget.bind("<Escape>", on_escape) # Escape
widget.bind("<BackSpace>", on_backspace) # Backspace
widget.bind("<Tab>", on_tab) # Tab
widget.bind("<space>", on_space) # فاصله
widget.bind("<Control-s>", on_save) # Ctrl+S
widget.bind("<Control-z>", on_undo) # Ctrl+Z
widget.bind("<Shift-Return>", on_shift_enter) # Shift+Enter
widget.bind("<FocusIn>", on_focus) # ویجت فوکوس گرفت
widget.bind("<FocusOut>", on_blur) # ویجت فوکوس از دست داد
widget.bind("<Configure>", on_resize) # اندازه ویجت تغییر کرد
ویژگیهای شیء event
def handler(event):
event.x # مختصات x ماوس نسبت به ویجت
event.y # مختصات y ماوس نسبت به ویجت
event.x_root # مختصات x ماوس نسبت به صفحه
event.y_root # مختصات y ماوس نسبت به صفحه
event.keysym # نام کلید فشردهشده (مثلا "Return", "a", "F1")
event.char # کاراکتر کلید فشردهشده
event.keycode # کد عددی کلید
event.num # شماره دکمه ماوس (1, 2, 3)
event.delta # مقدار اسکرول ماوس
event.width # عرض جدید ویجت (در رویداد Configure)
event.height # ارتفاع جدید ویجت (در رویداد Configure)
event.widget # خود ویجتی که رویداد روش اتفاق افتاده
حذف bind
widget.unbind("<Button-1>") # حذف یه رویداد خاص
widget.unbind_all("<Button-1>") # حذف از همه ویجتها
Dialogs — پنجرههای محاورهای
messagebox
from tkinter import messagebox
messagebox.showinfo("عنوان", "پیام اطلاعات")
messagebox.showwarning("عنوان", "پیام هشدار")
messagebox.showerror("عنوان", "پیام خطا")
result = messagebox.askquestion("عنوان", "آیا مطمئنی؟") # yes/no
result = messagebox.askyesno("عنوان", "ادامه بدم؟") # True/False
result = messagebox.askokcancel("عنوان", "ذخیره کنم؟") # True/False
result = messagebox.askretrycancel("عنوان", "دوباره تلاش؟") # True/False
result = messagebox.askyesnocancel("عنوان", "ذخیره کنم؟") # True/False/None
filedialog
from tkinter import filedialog
# باز کردن یه فایل
path = filedialog.askopenfilename(
title="انتخاب فایل",
initialdir="/home/user",
filetypes=[("فایلهای متنی", "*.txt"), ("همه فایلها", "*.*")],
)
# باز کردن چند فایل
paths = filedialog.askopenfilenames(
title="انتخاب چند فایل",
filetypes=[("تصاویر", "*.png *.jpg *.jpeg"), ("همه فایلها", "*.*")],
)
# ذخیره فایل
save_path = filedialog.asksaveasfilename(
title="ذخیره فایل",
defaultextension=".txt",
filetypes=[("فایلهای متنی", "*.txt")],
initialfile="output.txt",
)
# انتخاب پوشه
folder = filedialog.askdirectory(title="انتخاب پوشه")
colorchooser و simpledialog
from tkinter import colorchooser, simpledialog
# انتخاب رنگ
color = colorchooser.askcolor(title="انتخاب رنگ", color="#ff0000")
# برمیگردونه: ((r, g, b), "#rrggbb") یا (None, None)
if color[1]:
print(f"رنگ انتخابشده: {color[1]}")
# ورودی ساده
name = simpledialog.askstring("ورودی", "اسمت چیه؟", initialvalue="پیشفرض")
age = simpledialog.askinteger("ورودی", "چند سالته؟", minvalue=1, maxvalue=120)
price = simpledialog.askfloat("ورودی", "قیمت چنده؟", minvalue=0.0)
Menu — منوها
menubar = tk.Menu(root)
root.config(menu=menubar)
# منوی File
file_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="فایل", menu=file_menu)
file_menu.add_command(label="جدید", command=new_file, accelerator="Ctrl+N")
file_menu.add_command(label="باز کردن", command=open_file, accelerator="Ctrl+O")
file_menu.add_separator()
file_menu.add_command(label="ذخیره", command=save_file, accelerator="Ctrl+S")
file_menu.add_separator()
file_menu.add_command(label="خروج", command=root.quit)
# زیرمنو
submenu = tk.Menu(file_menu, tearoff=0)
file_menu.add_cascade(label="صادرکردن", menu=submenu)
submenu.add_command(label="به PDF", command=export_pdf)
submenu.add_command(label="به CSV", command=export_csv)
# منوی راستکلیک (Context Menu)
context_menu = tk.Menu(root, tearoff=0)
context_menu.add_command(label="کپی")
context_menu.add_command(label="پیست")
def show_context(event):
context_menu.tk_popup(event.x_root, event.y_root)
root.bind("<Button-3>", show_context)
Toplevel — پنجرههای جدید
def open_window():
top = tk.Toplevel(root)
top.title("پنجره جدید")
top.geometry("400x300")
top.transient(root) # پنجره فرزند root باشه
top.grab_set() # فوکوس رو قفل کنه (مثل modal)
top.resizable(False, False)
tk.Label(top, text="این یه پنجره جدیده").pack(pady=20)
tk.Button(top, text="بستن", command=top.destroy).pack()
root.wait_window(top) # صبر کن تا این پنجره بسته بشه
btn = tk.Button(root, text="باز کردن پنجره", command=open_window)
btn.pack()
Canvas — رسم گرافیکی
Canvas یه سطح رسم آزاده که میتونی روش شکلهای مختلف بکشی:
canvas = tk.Canvas(
root,
width=500,
height=400,
bg="white",
relief="sunken",
bd=2,
)
canvas.pack()
# رسم مستطیل
rect = canvas.create_rectangle(50, 50, 200, 150, fill="#4CAF50", outline="#333", width=2)
# رسم بیضی/دایره
oval = canvas.create_oval(220, 50, 350, 150, fill="#2196F3", outline="")
# رسم خط
line = canvas.create_line(50, 200, 450, 200, fill="#f44336", width=3, dash=(5, 3))
# رسم مثلث (polygon)
triangle = canvas.create_polygon(250, 220, 200, 350, 300, 350, fill="#FF9800", outline="#333")
# نوشتن متن
text_id = canvas.create_text(250, 380, text="tkinter Canvas", font=("Arial", 14, "bold"), fill="#333")
# جابجا کردن شکل
canvas.move(rect, 10, 0) # جابجایی نسبی (x, y)
canvas.coords(rect, 60, 60, 210, 160) # تنظیم مختصات جدید
# تغییر رنگ شکل
canvas.itemconfig(oval, fill="#9C27B0")
# حذف شکل
canvas.delete(triangle)
canvas.delete("all") # حذف همه
# bind کردن رویداد به شکل
canvas.tag_bind(rect, "<Button-1>", lambda e: print("مستطیل کلیک شد"))
ttk — ویجتهای پیشرفتهتر
ماژول ttk ویجتهایی با ظاهر بهتر و سازگارتر با سیستمعامل داره:
from tkinter import ttk
# Style — تنظیم ظاهر ttk
style = ttk.Style()
style.theme_use("clam") # تمهای موجود: clam, alt, default, classic
style.configure("TButton",
font=("Arial", 11),
padding=8,
background="#4CAF50",
)
style.map("TButton",
background=[("active", "#45a049"), ("disabled", "#cccccc")],
foreground=[("disabled", "#999999")],
)
# ویجتهای ttk
ttk.Button(root, text="دکمه ttk").pack(pady=5)
ttk.Label(root, text="لیبل ttk").pack()
ttk.Entry(root).pack(pady=5)
ttk.Checkbutton(root, text="چکباکس ttk").pack()
ttk.Radiobutton(root, text="رادیو ttk").pack()
ttk.Scale(root, from_=0, to=100, orient="horizontal").pack()
ttk.Progressbar(root, length=300, mode="determinate").pack(pady=5)
ttk.Separator(root, orient="horizontal").pack(fill="x", pady=5)
# Notebook — تبها
notebook = ttk.Notebook(root)
notebook.pack(fill="both", expand=True)
tab1 = ttk.Frame(notebook)
tab2 = ttk.Frame(notebook)
notebook.add(tab1, text="تب اول")
notebook.add(tab2, text="تب دوم")
tk.Label(tab1, text="محتوای تب اول").pack(pady=20)
tk.Label(tab2, text="محتوای تب دوم").pack(pady=20)
# Treeview — جدول و درخت
tree = ttk.Treeview(root, columns=("نام", "سن", "شهر"), show="headings")
tree.heading("نام", text="نام")
tree.heading("سن", text="سن")
tree.heading("شهر", text="شهر")
tree.column("نام", width=150, anchor="center")
tree.column("سن", width=80, anchor="center")
tree.column("شهر", width=120, anchor="center")
tree.insert("", tk.END, values=("علی رضایی", "28", "تهران"))
tree.insert("", tk.END, values=("سارا محمدی", "32", "اصفهان"))
tree.pack(fill="both", expand=True)
# انتخاب در Treeview
def on_select(event):
selected = tree.selection()
if selected:
item = tree.item(selected[0])
print(item["values"])
tree.bind("<<TreeviewSelect>>", on_select)
Progressbar — نوار پیشرفت
from tkinter import ttk
# حالت determinate (مقدار مشخص)
progress = ttk.Progressbar(root, length=300, mode="determinate", maximum=100)
progress.pack(pady=10)
progress["value"] = 0
def update_progress():
if progress["value"] < 100:
progress["value"] += 10
root.after(200, update_progress) # بعد از 200ms دوباره اجرا میشه
# حالت indeterminate (انیمیشن بیپایان)
busy = ttk.Progressbar(root, length=300, mode="indeterminate")
busy.pack(pady=5)
busy.start(10) # شروع انیمیشن با سرعت 10ms
busy.stop() # توقف انیمیشن
after — زمانبندی و حلقه رویداد
متد after برای اجرای کد بعد از یه تاخیر مشخص یا به صورت دورهای استفاده میشه. این روش درست برای کارهای async توی tkinter هستش چون مستقیم با حلقه رویداد کار میکنه:
# اجرای یه بار بعد از 1000ms (1 ثانیه)
root.after(1000, lambda: print("یه ثانیه گذشت"))
# اجرای دورهای
def update_clock():
import datetime
now = datetime.datetime.now().strftime("%H:%M:%S")
clock_label.config(text=now)
root.after(1000, update_clock) # خودش رو بعد از 1 ثانیه دوباره صدا میزنه
clock_label = tk.Label(root, font=("Arial", 24))
clock_label.pack()
update_clock()
# لغو کردن after
job_id = root.after(5000, some_function)
root.after_cancel(job_id) # لغو قبل از اجرا
رویداد Protocol — بستن پنجره
def on_close():
from tkinter import messagebox
if messagebox.askokcancel("خروج", "مطمئنی میخوای خارج بشی؟"):
root.destroy()
root.protocol("WM_DELETE_WINDOW", on_close)
یه مثال کامل: فرم ثبتنام
در پایان، یه مثال کامل که اکثر مفاهیم بالا رو با هم نشون میده:
import tkinter as tk
from tkinter import ttk, messagebox
class RegistrationForm:
def __init__(self, root):
self.root = root
self.root.title("فرم ثبتنام")
self.root.geometry("480x520")
self.root.resizable(False, False)
self.root.configure(bg="#f5f5f5")
self.name_var = tk.StringVar()
self.email_var = tk.StringVar()
self.gender_var = tk.StringVar(value="male")
self.skill_var = tk.StringVar(value="Python")
self.agree_var = tk.BooleanVar()
self.build_ui()
self.root.protocol("WM_DELETE_WINDOW", self.on_close)
def build_ui(self):
main_frame = tk.Frame(self.root, bg="#f5f5f5", padx=30, pady=20)
main_frame.pack(fill="both", expand=True)
tk.Label(main_frame, text="فرم ثبتنام", font=("Arial", 18, "bold"),
bg="#f5f5f5", fg="#333").grid(row=0, column=0, columnspan=2, pady=(0, 20))
tk.Label(main_frame, text="نام و نامخانوادگی:", bg="#f5f5f5").grid(
row=1, column=0, sticky="e", pady=8)
tk.Entry(main_frame, textvariable=self.name_var, width=28,
font=("Arial", 11)).grid(row=1, column=1, sticky="w", padx=(10, 0))
tk.Label(main_frame, text="ایمیل:", bg="#f5f5f5").grid(
row=2, column=0, sticky="e", pady=8)
tk.Entry(main_frame, textvariable=self.email_var, width=28,
font=("Arial", 11)).grid(row=2, column=1, sticky="w", padx=(10, 0))
tk.Label(main_frame, text="جنسیت:", bg="#f5f5f5").grid(
row=3, column=0, sticky="e", pady=8)
gender_frame = tk.Frame(main_frame, bg="#f5f5f5")
gender_frame.grid(row=3, column=1, sticky="w", padx=(10, 0))
tk.Radiobutton(gender_frame, text="مرد", variable=self.gender_var,
value="male", bg="#f5f5f5").pack(side="left")
tk.Radiobutton(gender_frame, text="زن", variable=self.gender_var,
value="female", bg="#f5f5f5").pack(side="left", padx=10)
tk.Label(main_frame, text="مهارت اصلی:", bg="#f5f5f5").grid(
row=4, column=0, sticky="e", pady=8)
ttk.Combobox(main_frame, textvariable=self.skill_var, state="readonly",
values=["Python", "JavaScript", "Java", "C++"], width=25).grid(
row=4, column=1, sticky="w", padx=(10, 0))
tk.Label(main_frame, text="توضیحات:", bg="#f5f5f5").grid(
row=5, column=0, sticky="ne", pady=8)
self.bio_text = tk.Text(main_frame, width=28, height=4, font=("Arial", 11))
self.bio_text.grid(row=5, column=1, sticky="w", padx=(10, 0))
tk.Checkbutton(main_frame, text="قوانین و مقررات را میپذیرم",
variable=self.agree_var, bg="#f5f5f5").grid(
row=6, column=0, columnspan=2, pady=15)
btn_frame = tk.Frame(main_frame, bg="#f5f5f5")
btn_frame.grid(row=7, column=0, columnspan=2)
tk.Button(btn_frame, text="ثبتنام", command=self.submit,
bg="#4CAF50", fg="white", font=("Arial", 11, "bold"),
width=12, cursor="hand2").pack(side="left", padx=5)
tk.Button(btn_frame, text="پاک کردن", command=self.clear,
bg="#f44336", fg="white", font=("Arial", 11),
width=12, cursor="hand2").pack(side="left", padx=5)
self.status_label = tk.Label(main_frame, text="", bg="#f5f5f5", fg="green",
font=("Arial", 10))
self.status_label.grid(row=8, column=0, columnspan=2, pady=10)
def submit(self):
name = self.name_var.get().strip()
email = self.email_var.get().strip()
if not name:
messagebox.showwarning("خطا", "نام را وارد کنید.")
return
if not email or "@" not in email:
messagebox.showwarning("خطا", "ایمیل معتبر وارد کنید.")
return
if not self.agree_var.get():
messagebox.showwarning("خطا", "باید قوانین را بپذیرید.")
return
self.status_label.config(text=f"✓ ثبتنام {name} با موفقیت انجام شد!", fg="green")
def clear(self):
self.name_var.set("")
self.email_var.set("")
self.skill_var.set("Python")
self.agree_var.set(False)
self.bio_text.delete("1.0", tk.END)
self.status_label.config(text="")
def on_close(self):
if messagebox.askokcancel("خروج", "میخوای از برنامه خارج بشی؟"):
self.root.destroy()
if __name__ == "__main__":
root = tk.Tk()
app = RegistrationForm(root)
root.mainloop()
سخن پایانی
tkinter با وجود اینکه ابزار پیچیدهای نیست، ولی برای ساخت ابزارهای داخلی، اسکریپتهای کوچک با رابط گرافیکی، و پروتوتایپهای سریع گزینهای واقعا کارآمده. بزرگترین مزیتش اینه که همراه پایتون میاد و نیاز به هیچ نصب اضافهای نداره.
مهمترین چیزی که باید همیشه یادت باشه اینه که geometry managerها رو توی یه container مخلوط نکنی، از متغیرهای tk.StringVar و tk.IntVar برای مدیریت دادهها استفاده کنی، و برای کارهای تاخیری همیشه از after بهجای time.sleep استفاده کنی چون time.sleep حلقه رویداد رو مسدود میکنه و برنامه فریز میشه.
اگر پروژهات بزرگتر شد و نیاز به ظاهر مدرنتری داشتی، میتونی به کتابخانههایی مثل CustomTkinter نگاه کنی که روی tkinter ساخته شده و ویجتهای با ظاهر خیلی بهتری داره. ولی قبل از اون، یه tkinter خوب بلد بودن پایهی محکمیه که هر GUI پایتونی روش بنا میشه.