在循环中生成ttk复选框并传递参数的问题

时间:2014-01-30 06:15:59

标签: python tkinter

我正在尝试生成一个可变数量的复选框,并为命令调用函数传入一组唯一的参数。目前,单击时所有复选框仅传递最后生成的复选框的属性(请参阅下面的代码)。您愿意提供的任何帮助或建议都会很精彩。谢谢!

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()

2 个答案:

答案 0 :(得分:3)

在定义lambda时,需要将变量的值“绑定”到lambda。你可以通过将变量传递给lambda函数来完成它,如下所示:

..., command=lambda id=conditionID[i], data=checkData[i]: onCheck(id, data), ...)

但请注意,如果您稍后更改conditionIDcheckData,此命令将不会注意到此更改。可以说,更好的解决方案是在运行时进行查找,只传入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对象,因此从列表的角度来看,值不会改变。