循环创建同名tk.Button时,如何为命令绑定不同参数?
Tkinter循环创建按钮参数绑定问题解决办法
问题场景
通过for循环批量创建Tkinter按钮时,按钮显示的品牌名称正确,但点击任意按钮,回调函数接收的参数始终是循环最后一次的取值(如示例中的TCL),无法实现点击对应品牌按钮输出对应内容(比如点击GE按钮输出"This is GE")。
原始问题代码
import tkinter as tk from tkinter import Button root = tk.Tk() root.title("Root") root.configure(bg = "#333333") root.geometry("600x500") root.resizable(False, False) # 补充原代码缺失的frame定义,保证代码可运行 frame = tk.Frame(root, bg="#333333") frame.pack(fill="both", expand=True) def Category(category): y = 40 category_list = [['Refrigerator', 'Refrigerator', 'Refrigerator', 'Refrigerator', 'Refrigerator', 'Television', 'Television', 'Television', 'Television', 'Television'], ['GE', 'LG', 'Maytag', 'Samsung', 'Whirlpool', 'Hisense', 'LG', 'Samsung', 'Sony', 'TCL']] category_item = category_list[0] brand = category_list[1] for x in range(len(category_item)): if category_item[x] == category: if category == "Television": brand_button = Button(frame, text = brand[x], font = "Poppins, 10", bg = "#222222", fg = "#ffffff", width = 12, borderwidth = 0, highlightthickness = 0, activebackground = "#222222", activeforeground = "#ffffff", anchor = "w") brand_button.bind("<Button-1>", lambda e:getTV(brand[x])) brand_button.place(x = 40, y = y) y += 35 else: brand_button = Button(frame, text = brand[x], font = "Poppins, 10", bg = "#222222", fg = "#ffffff", width = 12, borderwidth = 0, highlightthickness = 0, activebackground = "#222222", activeforeground = "#ffffff", anchor = "w") brand_button.bind("<Button-1>", lambda e:getFridge(brand[x])) brand_button.place(x = 40, y = y) y += 35 def getFridge(user_input): record = ['LG', 'GE', 'Maytag', 'Samsung', 'Whirlpool'] for row in record: if user_input == row: print("This is", row) def getTV(user_input): record = ['Hisense', 'LG', 'Samsung', 'Sony', 'TCL'] for row in record: if user_input == row: print("This is", row) Category("Refrigerator") root.mainloop()
问题原因
这是lambda表达式的延迟绑定特性导致的:lambda内部的brand[x]不会在循环迭代时立即计算取值,而是等到按钮被点击触发回调时,才会去获取变量x的当前值。而循环结束后,x已经指向最后一次迭代的索引,因此所有按钮的回调都会使用同一个参数值。
修改后的代码
只需修改绑定lambda的部分,通过默认参数提前固定当前循环的品牌值:
import tkinter as tk from tkinter import Button root = tk.Tk() root.title("Root") root.configure(bg = "#333333") root.geometry("600x500") root.resizable(False, False) frame = tk.Frame(root, bg="#333333") frame.pack(fill="both", expand=True) def Category(category): y = 40 category_list = [['Refrigerator', 'Refrigerator', 'Refrigerator', 'Refrigerator', 'Refrigerator', 'Television', 'Television', 'Television', 'Television', 'Television'], ['GE', 'LG', 'Maytag', 'Samsung', 'Whirlpool', 'Hisense', 'LG', 'Samsung', 'Sony', 'TCL']] category_item = category_list[0] brand = category_list[1] for x in range(len(category_item)): if category_item[x] == category: if category == "Television": brand_button = Button(frame, text = brand[x], font = "Poppins, 10", bg = "#222222", fg = "#ffffff", width = 12, borderwidth = 0, highlightthickness = 0, activebackground = "#222222", activeforeground = "#ffffff", anchor = "w") # 添加默认参数b=brand[x],提前绑定当前循环的品牌值 brand_button.bind("<Button-1>", lambda e, b=brand[x]: getTV(b)) brand_button.place(x = 40, y = y) y += 35 else: brand_button = Button(frame, text = brand[x], font = "Poppins, 10", bg = "#222222", fg = "#ffffff", width = 12, borderwidth = 0, highlightthickness = 0, activebackground = "#222222", activeforeground = "#ffffff", anchor = "w") # 同样通过默认参数绑定当前品牌值 brand_button.bind("<Button-1>", lambda e, b=brand[x]: getFridge(b)) brand_button.place(x = 40, y = y) y += 35 def getFridge(user_input): record = ['LG', 'GE', 'Maytag', 'Samsung', 'Whirlpool'] for row in record: if user_input == row: print("This is", row) def getTV(user_input): record = ['Hisense', 'LG', 'Samsung', 'Sony', 'TCL'] for row in record: if user_input == row: print("This is", row) Category("Refrigerator") root.mainloop()
核心修改说明
在lambda表达式中新增一个带默认值的参数(如b=brand[x]):
- Python的函数默认参数会在函数定义时求值,而非调用时。
- 循环每迭代一次,都会创建一个新的lambda,并把当前循环的
brand[x]值赋值给默认参数b,相当于把当前品牌值“固定”在这个lambda里。 - 当按钮被点击时,lambda会使用提前绑定好的
b值作为参数传递给回调函数,从而实现点击对应按钮输出对应品牌内容的效果。
内容的提问来源于stack exchange,提问作者Ferdinand Jacques
相关产品推荐
相关产品推荐

