我正在尝试生成一个可变数量的复选框,并为命令调用函数传入一组唯一的参数。目前,单击时所有复选框仅传递最后生成的复选框的属性(请参阅下面的代码)。您愿意提供的任何帮助或建议都会很精彩。谢谢!
from Tkinter import *
import tkMessageBox
import ttk
root = Tk()
checkData = []
conditionID = []
def onCheck(conditionID,checkData):
print checkData.get()
print conditionID
for i in range(0,10):
checkData.append(BooleanVar())
conditionID.append(i)
l = ttk.Checkbutton(root, text ="",variable=checkData[i],command=lambda: onCheck(conditionID[i],checkData[i]), onvalue=True, offvalue=False)
w=Message(root,background='ivory',text="test" + str(i),width=60)
l.grid(column=1,row=i+1)
w.grid(column=2,row=i+1)
root.mainloop()
答案 0 :(得分:3)
在定义lambda时,需要将变量的值“绑定”到lambda。你可以通过将变量传递给lambda函数来完成它,如下所示:
..., command=lambda id=conditionID[i], data=checkData[i]: onCheck(id, data), ...)
但请注意,如果您稍后更改conditionID
或checkData
,此命令将不会注意到此更改。可以说,更好的解决方案是在运行时进行查找,只传入i
:
..., command=lambda i=i: onCheck(conditionID[i], checkData[i]), ...
答案 1 :(得分:2)
这是python中的一个常见问题,它与tkinter没有直接关系:
让我们看看这段代码:
i = 0
def foo():
# here i will be lookup in the global namespace *when foo will be executed*
print i
foo() # => 0
i = 1
foo() # => 1
# if we want to force the "evaluation" of i at function definition,
# we have to put it in the function definition
def bar(i=i):
# here i is the function argument, and it default value is the value of the "global i" at the time the function was defined
print i
bar() # => 1
i=2
bar() # => 1
foo() # => 2
在你的情况下,这是同样的问题,但使用lambda(和lambdas是函数) 评估" i"在lambda中执行lambda时(当创建所有的checkbuttons并且i == 9时)
所以你必须这样定义你的命令参数:
l = ttk.Checkbutton(root, text ="",variable=checkData[i],command=lambda i=i: onCheck(conditionID[i],checkData[i]), onvalue=True, offvalue=False)
或者如果你想更明确:
l = ttk.Checkbutton(root, text ="",variable=checkData[i],command=lambda index=i: onCheck(conditionID[index],checkData[index]), onvalue=True, offvalue=False)
或更多:
for i in range(0,10):
checkData.append(BooleanVar())
conditionID.append(i)
# we define a new function (10 times)
# i is resolve at function definition (now)
def call_onCheck(index=i):
# the arguments of onCheck are resolved at function execution.
# it will take what is in the lists at the execution time.
# it may change or not (in your case : not)
return onCheck(conditionID[index], checkData[index])
l = ttk.Checkbutton(root, text ="",variable=checkData[i],command=call_onCheck, onvalue=True, offvalue=False)
w=Message(root,background='ivory',text="test" + str(i),width=60)
l.grid(column=1,row=i+1)
w.grid(column=2,row=i+1)
由于列表的内容不会改变*(在您提供的代码中),您还可以写:
from Tkinter import *
import tkMessageBox
import ttk
root = Tk()
# Those lists are not strictly necessary, but you may want to check this list from other function, So I keep it
checkData = []
# Here you store i the the i index of the list. I'm pretty sure this list is not necessary
conditionID = []
def onCheck(conditionID,checkData):
print checkData.get()
print conditionID
for i in range(0,10):
boolVar = BooleanVar()
checkData.append(boolVar)
conditionID.append(i)
l = ttk.Checkbutton(root,
text ="",
variable=boolVar,
# we don't need to add a "resolution step" at execution as the values we will need are already known at lambda definition
command=lambda boolVar=boolVar, i=i : onCheck(i, boolVal)),
onvalue=True, offvalue=False)
w=Message(root,background='ivory',text="test" + str(i),width=60)
l.grid(column=1,row=i+1)
w.grid(column=2,row=i+1)
root.mainloop()
*列表中的BooleanVar将会改变,但它是相同的booleanVar对象,因此从列表的角度来看,值不会改变。