I am trying to generate a variable number of checkboxes and pass in a unique set of arguments for the command call function. At present all checkboxes when clicked only pass on
You need to "bind" the value of the variables to the lambda at the time you define the lambda. You do it by passing the variables to the lambda function like so:
..., command=lambda id=conditionID[i], data=checkData[i]: onCheck(id, data), ...)
Note, however, that if you change conditionID
or checkData
later, this command won't notice that change. Arguably, a better solution is to do the lookup at runtime, by passing in i
only:
..., command=lambda i=i: onCheck(conditionID[i], checkData[i]), ...
This is a common problem in python and it is not directly related to tkinter:
Let's look this code :
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
In your case, this is the same problem but with lambda (and lambdas are functions) The evaluation of "i" in the lambda is made when at the execution of the lambda (when all checkbuttons are created and i==9)
So you have to define your command argument this way:
l = ttk.Checkbutton(root, text ="",variable=checkData[i],command=lambda i=i: onCheck(conditionID[i],checkData[i]), onvalue=True, offvalue=False)
or if you want to be more explicit:
l = ttk.Checkbutton(root, text ="",variable=checkData[i],command=lambda index=i: onCheck(conditionID[index],checkData[index]), onvalue=True, offvalue=False)
or more over:
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)
As the content of the lists don't change* (in the code you provide), you could also write:
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()
* The BooleanVar's in the list will change but it is the same booleanVar object, so from the list't point of view, values don't change.