Python 在循环中生成ttk复选框和传递参数时出现问题
我试图生成数量可变的复选框,并为命令调用函数传入一组唯一的参数。目前,单击时所有复选框仅传递最后生成的复选框的属性(请参见下面的代码)。你愿意提供的任何帮助或建议都将非常好。谢谢Python 在循环中生成ttk复选框和传递参数时出现问题,python,tkinter,Python,Tkinter,我试图生成数量可变的复选框,并为命令调用函数传入一组唯一的参数。目前,单击时所有复选框仅传递最后生成的复选框的属性(请参见下面的代码)。你愿意提供的任何帮助或建议都将非常好。谢谢 from Tkinter import * import tkMessageBox import ttk root = Tk() checkData = [] conditionID = [] def onCheck(conditionID,checkData): print checkData.get()
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()
在定义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]), ...
在定义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]), ...
这是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(lambda是函数)
lambda中的“i”的计算是在执行lambda时进行的(当创建了所有检查按钮且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对象,因此从列表的角度来看,值不会更改。这是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(lambda是函数)
lambda中的“i”的计算是在执行lambda时进行的(当创建了所有检查按钮且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对象,因此从列表的角度来看,值不会更改。您必须遵守此解决方案:由于参数在lambda定义处解析,因此无论追加什么,它都不会更改。这在这里起作用,因为booleanVar是可变的,并且仍然存在“无向性”。但是,如果更改conditionID列表的值,onCheck()将永远看不到它。@mgautier:true。更好的解决方案是传入
i
,并在事件触发时进行查找。我已经编辑了我的答案。感谢您指出这一点。您必须认真对待这个解决方案:因为参数是在lambda定义下解析的,所以无论附加什么,它都不会改变。这在这里起作用,因为booleanVar是可变的,并且仍然存在“无向性”。但是,如果更改conditionID列表的值,onCheck()将永远看不到它。@mgautier:true。更好的解决方案是传入i
,并在事件触发时进行查找。我已经编辑了我的答案。谢谢你们指出这一点,谢谢你们的回答。我对Python非常陌生,这种与函数的交互对我来说是新的。我只是想澄清一下,当我给每个lambda赋值时,我实际上设置了10个函数,每个函数都带有与“I”关联的硬设置/锁定值,conditionID[I]成为I=1的conditionID[1],传递到lambda的任何值都是conditionID[1]的值,并且不再连接到列表conditionID[1]?(我已经更新了答案。)是的,您创建了10个函数,不再连接到i,但仍然连接到列表conditionID。我的最后一个代码命题与列表完全无关。谢谢大家的回答。我对Python非常陌生,这种与函数的交互对我来说是新的。我只是想澄清一下,当我给每个lambda赋值时,我实际上设置了10个函数,每个函数都带有与“I”关联的硬设置/锁定值,conditionID[I]成为I=1的conditionID[1],传递到lambda的任何值都是conditionID[1]的值,并且不再连接到列表conditionID[1]?(我已经更新了答案。)是的,您创建了10个函数,不再连接到i,但仍然连接到列表conditionID。我的最后一个代码命题与列表完全无关。