Home > OS >  tkinter: How to pass arguments when <<ComboboxSelected>> is bound to a method?
tkinter: How to pass arguments when <<ComboboxSelected>> is bound to a method?

Time:08-05

I am a newbie with the tkinter library and I don't understand how to pass arguments from a combobox to a method I would like to bind on event action?

Example:

class Controller():
    def __init__(self):
        self.root = tk.Tk()
        self.view = View(self.root)
        self.view.sidepanel.motor_sel_combo.bind("<<ComboboxSelected>>",  lambda event, arg=self.view.sidepanel.motor_sel_combo.get(): self.motor_selection(event, arg))
    
    def motor_selection(self,event, mot_selected):
        #print(event) #--> would only print <VirtualEvent event x=0 y=0> #????
        #print(event.widget.get())
        print(mot_selected)

The output for print(mot_selected) is empty. The tkinter manual does not show a good example.

self.view.sidepanel.motor_sel_combo.get() contains the imho the selected value of the combobox.

I tried as well with functools.partial instead of lambda, but I don't get it to work. Could you show me the correct syntax with lambda and functools.partial, please? Thank you.

CodePudding user response:

You're calling self.view.sidepanel.motor_sel_combo.get() at the time you create the lambda, not at the time the combobox value has changed.

You should call the get() method inside the function instead of trying to pass it to the function. The use of lambda here does nothing but make the code unnecessarily complex.

class Controller():
    def __init__(self):
        self.root = tk.Tk()
        self.view = View(self.root)
        self.view.sidepanel.motor_sel_combo.bind("<<ComboboxSelected>>",  
            self.motor_selection)
    
    def motor_selection(self,event):
        mot_selected = self.view.sidepanel.motor_sel_combo.get()
        ...

It was pointed out in a comment by @acw1668 that the function should use event.widget.get() instead of self.view.sidepanel.motor_sel_combo.bet() and that is true. Since we want to call get() on the widget that was bound, using the shorter version is the better solution. It is better for two reasons: because it makes the function loosely coupled to the other code, and because it makes it more clear that the function is operating on the widget it was bound to. That means that the other code could rename that variable and the function will continue to work without having to also be modified.

For such a small program, whether the function is loosely coupled or tightly coupled isn't that big of a deal. I would argue, though, that using event.widget in a callback is a best practice that one should get in the habit of doing.

The OP indicated in comments they want to see a version that uses lambda. I don't recommend it, but the root of the problem with lambda is that the OP was calling the get() method when defining the lambda rather than when it is called. To call the get() method when the lambda runs, the call must be moved inside the body:

self.view.sidepanel.motor_sel_combo.bind(
    "<<ComboboxSelected>>",  
    lambda event: self.motor_selection(event, self.view.sidepanel.motor_sel_combo.get())
)

In this case, motor_selection must be like in the OP's original code and accept two parameters.

  • Related