TypeError: action() takes 1 positional argument but 2 were given

Question:

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.

Asked By: Valentin

||

Answers:

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.

Answered By: 3ddavies

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, _event = None):
    self.SOURCE_SHUTTER = self.listeCombo.get()
    print(self.SOURCE_SHUTTER)
Answered By: JRiggles
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.