You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tkinter循环创建按钮绑定命令异常:所有按钮仅执行最后一个函数

解决Tkinter循环绑定按钮命令时所有按钮执行同一操作的问题

问题根源

你当前的代码中所有按钮都绑定了同一个new()函数,且即便尝试用lambda,也会因为Python闭包的延迟绑定特性,导致所有按钮最终调用的是循环变量最后一个值对应的操作。

解决方案

我们需要为每个按钮绑定对应的帧切换函数,并通过functools.partial或带默认参数的lambda避免闭包延迟绑定问题。

步骤1:定义每个菜品对应的帧切换函数

为每个菜品创建独立的帧切换逻辑(可根据需求扩展函数内的帧操作):

def show_salad():
    # 显示沙拉菜单帧,隐藏饮品帧
    frame_left.grid(row=0, column=0, rowspan=3, sticky="WENS")
    frame_drinks.grid_forget()

def show_meal():
    # 显示餐点菜单帧,隐藏左侧帧
    frame_drinks.grid(row=1, column=1, sticky="WENS")
    frame_left.grid_forget()

def show_soda():
    # 汽水菜单的帧切换逻辑
    frame_drinks.grid(row=1, column=1, sticky="WENS")
    frame_left.grid_forget()

def show_drinks():
    # 饮品菜单的帧切换逻辑
    frame_drinks.grid(row=1, column=1, sticky="WENS")
    frame_left.grid_forget()

def show_dessert():
    # 甜点菜单的帧切换逻辑
    frame_drinks.grid(row=1, column=1, sticky="WENS")
    frame_left.grid_forget()

步骤2:创建操作函数列表与图片列表对应

将每个图片对应的操作函数按顺序存入列表:

action_list = [show_salad, show_meal, show_soda, show_drinks, show_dessert]

步骤3:循环绑定按钮命令(二选一)

方法1:使用functools.partial

先导入partial:

from functools import partial

修改循环代码:

for i, (item, action) in enumerate(zip(item_photo_list, action_list)):
    # 用partial绑定当前迭代的操作函数
    menu_button = tkinter.Button(frame_drinks, image=item, bg="yellow", command=partial(action))
    row, column = divmod(i, COLUMNS)
    menu_button.grid(row=row, column=column, sticky="news", padx=20, pady=20)

方法2:使用带默认参数的lambda

无需额外导入,直接修改循环:

for i, (item, action) in enumerate(zip(item_photo_list, action_list)):
    # 通过默认参数捕获当前迭代的action,避免闭包延迟绑定
    menu_button = tkinter.Button(frame_drinks, image=item, bg="yellow", command=lambda act=action: act())
    row, column = divmod(i, COLUMNS)
    menu_button.grid(row=row, column=column, sticky="news", padx=20, pady=20)

修改后的完整代码

import tkinter
from tkinter import PhotoImage, NW, NO, RIGHT, LEFT, CENTER, TOP, ttk, NE, W
from PIL import Image, ImageTk
from functools import partial  # 方法1需要导入,方法2可注释

root = tkinter.Tk()
root.title("Order System")
root.geometry("960x500")
root.config(background="#088a88")

frame_drinks = tkinter.Frame(root, background="red")
frame_drinks.grid(row=1, column=1, sticky="WENS")

frame_left = tkinter.Frame(root, background="lightblue")
frame_left.grid(row=0, column=0, rowspan=3, sticky="WENS")

# 定义各菜品对应的帧切换函数
def show_salad():
    frame_left.grid(row=0, column=0, rowspan=3, sticky="WENS")
    frame_drinks.grid_forget()

def show_meal():
    frame_drinks.grid(row=1, column=1, sticky="WENS")
    frame_left.grid_forget()

def show_soda():
    frame_drinks.grid(row=1, column=1, sticky="WENS")
    frame_left.grid_forget()

def show_drinks():
    frame_drinks.grid(row=1, column=1, sticky="WENS")
    frame_left.grid_forget()

def show_dessert():
    frame_drinks.grid(row=1, column=1, sticky="WENS")
    frame_left.grid_forget()

# 布局配置
root.columnconfigure(0, weight=1)
root.columnconfigure(1, weight=3)
root.columnconfigure(2, weight=1)

root.rowconfigure(0, weight=1)
root.rowconfigure(1, weight=5)
root.rowconfigure(2, weight=1)

frame_drinks.columnconfigure(0, weight=1)
frame_drinks.columnconfigure(1, weight=1)
frame_drinks.columnconfigure(2, weight=1)
frame_drinks.columnconfigure(3, weight=1)

frame_drinks.rowconfigure(0, weight=1)
frame_drinks.rowconfigure(1, weight=1)
frame_drinks.rowconfigure(2, weight=1)

frame_left.columnconfigure(0, weight=1)

frame_left.rowconfigure(0, weight=1)
frame_left.rowconfigure(1, weight=1)
frame_left.rowconfigure(2, weight=1)
frame_left.rowconfigure(3, weight=1)
frame_left.rowconfigure(4, weight=4)

# 图片加载
COLUMNS = 4

photo_salad = Image.open("salad.png")
photo_meal = Image.open("meal.png")
photo_soda = Image.open("soda.png")
photo_drinks = Image.open("drinks.png")
photo_dessert = Image.open("dessert.png")

photo_salad = photo_salad.resize((105, 85))
photo_meal = photo_meal.resize((105, 85))
photo_soda = photo_soda.resize((105, 85))
photo_drinks = photo_drinks.resize((105, 85))
photo_dessert = photo_dessert.resize((105, 85))

photo_salad = ImageTk.PhotoImage(photo_salad)
photo_meal = ImageTk.PhotoImage(photo_meal)
photo_soda = ImageTk.PhotoImage(photo_soda)
photo_drinks = ImageTk.PhotoImage(photo_drinks)
photo_dessert = ImageTk.PhotoImage(photo_dessert)

item_photo_list = [photo_salad, photo_meal, photo_soda, photo_drinks, photo_dessert]
# 对应图片的操作函数列表
action_list = [show_salad, show_meal, show_soda, show_drinks, show_dessert]

# 循环创建按钮(方法1:partial)
for i, (item, action) in enumerate(zip(item_photo_list, action_list)):
    menu_button = tkinter.Button(frame_drinks, image=item, bg="yellow", command=partial(action))
    row, column = divmod(i, COLUMNS)
    menu_button.grid(row=row, column=column, sticky="news", padx=20, pady=20)

# 方法2:带默认参数的lambda,替换上面的循环即可
# for i, (item, action) in enumerate(zip(item_photo_list, action_list)):
#     menu_button = tkinter.Button(frame_drinks, image=item, bg="yellow", command=lambda act=action: act())
#     row, column = divmod(i, COLUMNS)
#     menu_button.grid(row=row, column=column, sticky="news", padx=20, pady=20)

# 账单树视图
bill = ttk.Treeview(frame_left, selectmode="browse")
bill.grid(row=4, column=0, rowspan=3, sticky="WENS")

bill["columns"] = ("1", "2")
bill["show"] = "headings"

bill.column("#0", width=100)
bill.column("1", minwidth=50, width=100)
bill.column("2", minwidth=50, width=100)

bill.heading("#0", text="")
bill.heading("1", text="Table")
bill.heading("2", text="Bill")

bill.insert("", "end", values=("name", "price"))
bill.insert("", "end", values=("name", "price"))

root.mainloop()

内容的提问来源于stack exchange,提问作者a.c_hairy_limon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 10:40:45