Issue with generating ttk checkboxes in loops and passing arguments

前端 未结 2 693
别跟我提以往
别跟我提以往 2021-01-28 11:57

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

相关标签:
2条回答
  • 2021-01-28 12:27

    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]), ...
    
    0 讨论(0)
  • 2021-01-28 12:36

    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.

    0 讨论(0)
提交回复
热议问题