ساخت پروژه ۲: پنجره، دکمهها و جدول
- کلاس قدم قبل را در یک فایل واقعی بگذاری
- فرم، توابع دکمهها، جدول و دکمهها را به ترتیب اضافه کنی
- بعد از هر قدم برنامه را اجرا و چک کنی
- با یک چکلیست همهی حالتها را تست کنی
این درس در VS Code انجام میشود، چون tkinter داخل مرورگر اجرا نمیشود. هر قدم را اول خودت بنویس، اجرا کن، و فقط بعد کد آماده را باز کن و مقایسه کن.
قدم ۰: فایل
در پوشهی python-learning فایل shop_manager.py بساز. اول سه خط import و خط FILE را بنویس، بعد کلاس Shop که در درس قبل از حفظ نوشتی را زیرش بگذار.
import json
import os
import tkinter as tk
from tkinter import ttk, messagebox
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'shop_data.json')
# class Shop: ... (از درس قبل)
قدم ۱: پنجره و فرم
زیر کلاس: یک شیء shop بساز، پنجره با عنوان «مدیریت فروشگاه - Python» و اندازهی 760x560، و چهار ردیف برچسب + جعبه با حلقه که در fields ذخیره شوند. فعلاً آخر فایل root.mainloop() بگذار و اجرا کن.
کد آمادهی قدم ۱ (بعد از نوشتن خودت)
shop = Shop()
root = tk.Tk()
root.title('مدیریت فروشگاه - Python')
root.geometry('760x560')
fields = {}
for row, (key, label) in enumerate([('name', 'نام کالا'), ('buy', 'قیمت خرید'), ('sell', 'قیمت فروش'), ('qty', 'تعداد')]):
ttk.Label(root, text=label).grid(row=row, column=0, padx=8, pady=5, sticky='e')
entry = ttk.Entry(root, width=28)
entry.grid(row=row, column=1, padx=8, pady=5)
fields[key] = entry
قدم ۲: خواندن فرم و تابع run
values() چهار جعبه را بخواند و (name, buy, sell, qty) برگرداند (نام با strip، قیمتها float، تعداد int). run(action) تابع را اجرا کند، اگر موفق بود جدول را refresh کند (بهجز برای جستوجو)، و ValueError/TypeError و OSError را با messagebox.showerror نشان دهد.
منطق values (در مرورگر)
بهجای جعبهها، دیکشنری form از متنها داری. تابع values(form) بنویس که (name, buy, sell, qty) را با نوع درست برگرداند.
دیدن راهحل (اول خودت تلاش کن)
def values(form): name = form['name'].strip() buy = float(form['buy']) sell = float(form['sell']) qty = int(form['qty']) return name, buy, sell, qty
کد آمادهی قدم ۲
def values():
name = fields['name'].get().strip()
buy = float(fields['buy'].get())
sell = float(fields['sell'].get())
qty = int(fields['qty'].get())
return name, buy, sell, qty
def run(action):
try:
action()
if action is not search: refresh()
except (ValueError, TypeError) as exc:
messagebox.showerror('خطا', str(exc))
except OSError as exc:
messagebox.showerror('خطای فایل', str(exc))
قدم ۳: توابع دکمهها
شش تابع کوتاه: add، edit، delete، sell، search و report. هر کدام فقط فرم را میخواند و متد مناسب shop را صدا میزند. report نتیجه را با f-string و {income:,.0f} در messagebox.showinfo نشان میدهد.
کد آمادهی قدم ۳
def add():
shop.add(*values())
def edit():
shop.edit(*values())
def delete():
shop.delete(fields['name'].get().strip())
def sell():
shop.sell(fields['name'].get().strip(), int(fields['qty'].get()))
def search():
name = fields['name'].get().strip().lower()
refresh(name)
def report():
count, income = shop.report()
messagebox.showinfo('گزارش فروش', f'تعداد فروختهشده: {count}\nدرآمد کل فروش: {income:,.0f}')
قدم ۴: refresh و choose
refresh(query='') جدول را خالی و از نو پر کند. choose(event) ردیف انتخابشده را در جعبهها بنویسد. (هنوز table نساختی؛ اشکالی ندارد، چون تابع تا صدا زده نشود اجرا نمیشود.)
کد آمادهی قدم ۴
def refresh(query=''):
for item in table.get_children():
table.delete(item)
for name, p in shop.products.items():
if query in name.lower():
table.insert('', 'end', values=(name, p['buy'], p['sell'], p['qty']))
def choose(event):
selection = table.selection()
if not selection:
return
name, buy, sell_price, qty = table.item(selection[0], 'values')
for key, value in [('name', name), ('buy', buy), ('sell', sell_price), ('qty', qty)]:
fields[key].delete(0, tk.END)
fields[key].insert(0, value)
قدم ۵: دکمهها، جدول و اجرا
لیست buttons، قاب و حلقهی ساخت دکمهها با lambda a=action: run(a)؛ جدول با چهار ستون؛ bind به choose؛ وزن ردیف ۵ و ستون ۳؛ refresh() و در آخر root.mainloop().
کد آمادهی قدم ۵
buttons = [('افزودن کالا', add), ('ویرایش کالا', edit), ('حذف کالا', delete),
('ثبت فروش', sell), ('جستوجو', search), ('نمایش همه', refresh), ('گزارش فروش', report)]
frame = ttk.Frame(root)
frame.grid(row=4, column=0, columnspan=4, padx=10, pady=12)
for i, (label, action) in enumerate(buttons):
ttk.Button(frame, text=label, command=lambda a=action: run(a)).grid(row=i // 4, column=i % 4, padx=4, pady=4)
columns = ('name', 'buy', 'sell', 'qty')
table = ttk.Treeview(root, columns=columns, show='headings', height=13)
for key, label in [('name', 'نام کالا'), ('buy', 'قیمت خرید'), ('sell', 'قیمت فروش'), ('qty', 'موجودی')]:
table.heading(key, text=label)
table.column(key, width=150, anchor='center')
table.grid(row=5, column=0, columnspan=4, padx=10, pady=10, sticky='nsew')
table.bind('<<TreeviewSelect>>', choose)
root.grid_rowconfigure(5, weight=1)
root.grid_columnconfigure(3, weight=1)
refresh()
root.mainloop()
چکلیست تست برنامهی کامل
برنامه را اجرا کن و همهی اینها را امتحان کن. هر کدام درست بود تیک بزن:
اگر چیزی کار نکرد، کدت را با مستندات خط به خط پروژه مقایسه کن و درس باگگیری را ببین.
آزمون این درس
برای گذراندن این درس حداقل ۸۰٪ جواب درست لازم است. هر چند بار خواستی میتوانی دوباره امتحان بدهی.