برگ تقلب tkinter پایتون

مرجع کامل برای مرور tkinter پایتون

برگ تقلب 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 پایتونی روش بنا می‌شه.