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。我的最后一个代码命题与列表完全无关。