2

I used to work with this way of coding and it worked fine, but after going back to it a few weeks later, it does not anymore. I simplidied my code so it is easy to type here.

import tkinter as tk
from tkinter import ttk

class wind(tk.Tk):

    def __init__(self):
        tk.Tk.__init__(self)
        
        # id shutter
        self.SOURCE_SHUTTER = "/dev/ttyUSB0"

        # menu deroulant
        self.listeFlux = ["/dev/ttyUSB0", "/dev/ttyUSB1", "/dev/ttyUSB2", "/dev/ttyUSB3"]
        self.listeCombo = ttk.Combobox(self, values=self.listeFlux)
        self.listeCombo.current(0)
        self.listeCombo.bind("<<ComboboxSelected>>", self.action)
        self.listeCombo.pack(side="top")
        
     def action(self):
        self.SOURCE_SHUTTER = self.listeCombo.get()
        print(self.SOURCE_SHUTTER)

if __name__ == "__main__":
    win = wind()
    win.geometry("800x600")
    win.mainloop()

This code gives me the error : TypeError: action() takes 1 positional argument but 2 were given. Does someone know why ? I have seen people make this mistake but their error was that a parameter was missing "self" somewhere in their code, which I don't think I am forgetting here.

Thanks a lot for your help. Valentin

I tried looking in another topic that had the same problem but mine seems different here.

m4lou
  • 47
  • 1
  • 10
  • Do the answers to this [question](https://stackoverflow.com/questions/7299955/tkinter-binding-a-function-with-arguments-to-a-widget) help at all? – quamrana Jan 10 '23 at 19:59
  • Yes it did work in the end with a bit of manipulation but it is not convenient at all, the solution with *args provided below worked in a better way. Thanks anyways ! – m4lou Jan 10 '23 at 20:10

2 Answers2

2

A quick and dirty fix would be to change the definition of action to:

def action(self, *args):

The underlying issue here is that <<ComboboxSelected>> seems to return a tuple, so when the event is triggered, both the self object and the tuple are being passed to the action function, which is causing the error.

The above solution is a "dirty fix" because it will allow the function to take any number of arguments greater than one.

3ddavies
  • 546
  • 2
  • 19
  • This solution works fine, I see what you mean by "dirty fix" but thanks, it helped more than fine. – m4lou Jan 10 '23 at 20:09
  • Glad to hear it works @Valentin. If this is the solution you intend to go with, please mark it as the accepted answer. – 3ddavies Jan 10 '23 at 20:12
2

Event bindings in tkinter will inherently pass an event parameter to the bound function, so you'll need deal with it one way or another

Option 1

Add an _event parameter to the action method.

The leading underscore _ is convention to let people know that the value is unused by the function, and event is the conventional name for the event argument taken by event-driven functions in tkinter. The default value of None isn't strictly necessary, but it's good practice.

def action(self, _event=None):
    self.SOURCE_SHUTTER = self.listeCombo.get()
    print(self.SOURCE_SHUTTER)
Option 2

Use *_args in your method definition to allow it to accept any number of arguments (as suggested in @3ddavies' answer!). Again, the _ is convention for unused values, and args is convention for this type of parameter. As has been mentioned, the caveat here is that now your action method will accept any number of arguments - this is unlikely to be an issue in this particular case, but keep it in mind!

def action(self, *_args):
    self.SOURCE_SHUTTER = self.listeCombo.get()
    print(self.SOURCE_SHUTTER)
Option 3

Use a lambda to absorb the event and call action as an anonymous function

def __init__(self):
    ...  # code omitted for brevity
    self.listeCombo.bind("<<ComboboxSelected>>", lambda _event: self.action)
    ...

def action(self):  # NOTE: the 'event' parameter is no longer required here
    self.SOURCE_SHUTTER = self.listeCombo.get()
    print(self.SOURCE_SHUTTER)
JRiggles
  • 4,847
  • 1
  • 12
  • 27