۸. دکمه‌ها، جدول و اجرای برنامه

خط ۱۲۹ تا ۱۴۶

بخش آخر همه‌چیز را کنار هم می‌گذارد: هفت دکمه در یک قاب، جدول کالاها، تنظیم کشیده شدن پنجره، پر کردن اولیه‌ی جدول و در پایان حلقه‌ی اصلی که پنجره را باز نگه می‌دارد.

کد این بخش

shop_manager.py, lines 129–146
129buttons = [('افزودن کالا', add), ('ویرایش کالا', edit), ('حذف کالا', delete),130           ('ثبت فروش', sell), ('جست‌وجو', search), ('نمایش همه', refresh), ('گزارش فروش', report)]131frame = ttk.Frame(root)132frame.grid(row=4, column=0, columnspan=4, padx=10, pady=12)133for i, (label, action) in enumerate(buttons):134    ttk.Button(frame, text=label, command=lambda a=action: run(a)).grid(row=i // 4, column=i % 4, padx=4, pady=4)135 136columns = ('name', 'buy', 'sell', 'qty')137table = ttk.Treeview(root, columns=columns, show='headings', height=13)138for key, label in [('name', 'نام کالا'), ('buy', 'قیمت خرید'), ('sell', 'قیمت فروش'), ('qty', 'موجودی')]:139    table.heading(key, text=label)140    table.column(key, width=150, anchor='center')141table.grid(row=5, column=0, columnspan=4, padx=10, pady=10, sticky='nsew')142table.bind('<<TreeviewSelect>>', choose)143root.grid_rowconfigure(5, weight=1)144root.grid_columnconfigure(3, weight=1)145refresh()146root.mainloop()

روی شماره‌ی هر خط بزن تا به توضیحش بروی؛ روی «خط …» در توضیح بزن تا همان خط در کد روشن شود.

توضیح خط به خط

خط ۱۲۹buttons = [('افزودن کالا', add), ('ویرایش کالا', edit), ('حذف کالا', delete),
لیستی از جفت‌های (متن دکمه، تابع). توابع بدون پرانتز نوشته شده‌اند، یعنی خود تابع در لیست می‌رود و اجرا نمی‌شود.
خط ۱۳۰('ثبت فروش', sell), ('جست‌وجو', search), ('نمایش همه', refresh), ('گزارش فروش', report)]
ادامه‌ی همان لیست. پرانتز باز در خط قبل اجازه می‌دهد لیست در دو خط نوشته شود. «نمایش همه» مستقیم به refresh وصل است که بدون ورودی همه‌ی کالاها را نشان می‌دهد.
خط ۱۳۱frame = ttk.Frame(root)
یک قاب (Frame) داخل پنجره می‌سازد تا دکمه‌ها شبکه‌ی جدای خودشان را داشته باشند.
خط ۱۳۲frame.grid(row=4, column=0, columnspan=4, padx=10, pady=12)
قاب را در ردیف ۴ پنجره (زیر چهار ردیف فرم) می‌گذارد. columnspan=4 یعنی چهار ستون را پوشش می‌دهد.
خط ۱۳۳for i, (label, action) in enumerate(buttons):
حلقه روی دکمه‌ها؛ i شماره‌ی دکمه (۰ تا ۶) است.
خط ۱۳۴ttk.Button(frame, text=label, command=lambda a=action: run(a)).grid(row=i // 4, column=i % 4, padx=4, pady=4)
دکمه را می‌سازد و در قاب می‌گذارد. command تابعی است که با کلیک اجرا می‌شود. lambda a=action: run(a) یک تابع بی‌نام می‌سازد که run را با تابع همین دکمه صدا می‌زند. a=action مقدار فعلی را همان لحظه نگه می‌دارد؛ بدون آن، همه‌ی دکمه‌ها آخرین مقدار حلقه (report) را اجرا می‌کردند. i // 4 ردیف و i % 4 ستون را می‌دهد، پس چهار دکمه در ردیف اول و سه دکمه در ردیف دوم قرار می‌گیرند.
خط ۱۳۵
خط خالی.
خط ۱۳۶columns = ('name', 'buy', 'sell', 'qty')
شناسه‌ی چهار ستون جدول در یک تاپل.
خط ۱۳۷table = ttk.Treeview(root, columns=columns, show='headings', height=13)
جدول (Treeview) را می‌سازد. show='headings' ستون درختی پیش‌فرض (#0) را پنهان می‌کند و فقط ستون‌های ما می‌مانند. 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')
جدول را در ردیف ۵ و به پهنای چهار ستون می‌گذارد. sticky='nsew' یعنی به هر چهار جهت بچسبد و با بزرگ شدن خانه کشیده شود.
خط ۱۴۲table.bind('<<TreeviewSelect>>', choose)
رویداد مجازی <<TreeviewSelect>> را به choose وصل می‌کند. هر بار انتخاب جدول عوض شود، choose اجرا می‌شود.
خط ۱۴۳root.grid_rowconfigure(5, weight=1)
به ردیف ۵ (جدول) وزن ۱ می‌دهد: وقتی پنجره بلندتر شود، فضای اضافه به این ردیف می‌رسد.
خط ۱۴۴root.grid_columnconfigure(3, weight=1)
به ستون ۳ وزن ۱ می‌دهد تا فضای اضافه‌ی افقی به آن برسد و جدول (که تا ستون ۳ کشیده شده) پهن شود.
خط ۱۴۵refresh()
اولین بار جدول را با کالاهای خوانده‌شده از فایل پر می‌کند.
خط ۱۴۶root.mainloop()
حلقه‌ی اصلی tkinter را شروع می‌کند. برنامه همین‌جا منتظر کلیک و تایپ می‌ماند و تا بسته شدن پنجره از این خط رد نمی‌شود.

یادداشت

ترتیب اجرای کل فایل: تعریف کلاس ← ساخت shop و پنجره ← فرم ← تعریف توابع ← دکمه‌ها ← جدول ← refresh() ← mainloop(). توابع خط ۷۷ تا ۱۲۷ در زمان تعریف اجرا نمی‌شوند؛ فقط با کلیک دکمه‌ها یا در خط ۱۴۵ اجرا می‌شوند.

ارتباط با بقیه‌ی کد

همچنین ببینید

buttons
توابع آن در بخش ۶ تعریف شده‌اند: add خط ۹۳، edit خط ۹۶، delete خط ۹۹، sell خط ۱۰۲، search خط ۱۰۵، report خط ۱۰۹، و refresh در خط ۱۱۳.
run
در خط ۸۴ تعریف شده است.
table
در refresh (خط ۱۱۴، خط ۱۱۵، خط ۱۱۸) و choose (خط ۱۲۱، خط ۱۲۴) استفاده می‌شود.
choose
در خط ۱۲۰ تعریف شده است.
grid
فرم ردیف‌های ۰ تا ۳ را گرفته (خط ۷۲ و خط ۷۴)، دکمه‌ها ردیف ۴ و جدول ردیف ۵.

سؤال‌های تمرینی

اول خودت جواب بده، بعد جواب را باز کن.

  1. اگر در خط ۱۳۴ به‌جای a=action فقط lambda: run(action) نوشته شود چه می‌شود؟

    نمایش جواب

    لامبدا مقدار action را هنگام کلیک می‌خواند، نه هنگام ساخت؛ پس همه‌ی دکمه‌ها آخرین مقدار حلقه یعنی report را اجرا می‌کنند.

  2. دکمه‌ای که شماره‌اش ۵ است کجا قرار می‌گیرد؟

    نمایش جواب

    ردیف 5 // 4 = 1 و ستون 5 % 4 = 1: ردیف دوم، ستون دوم. همان دکمه‌ی «نمایش همه» است.

  3. چرا root.mainloop() باید آخرین خط باشد؟

    نمایش جواب

    این خط تا بسته شدن پنجره منتظر می‌ماند؛ کد بعد از آن فقط بعد از تمام شدن برنامه اجرا می‌شد.

  4. show='headings' در خط ۱۳۷ چه چیزی را پنهان می‌کند؟

    نمایش جواب

    ستون درختی پیش‌فرض #0، تا فقط چهار ستون داده دیده شوند.