Tkinter Python: How to pass more than one argument through a lambda function in an OptionMenu
The command
function for an OptionMenu
widget takes only one argument: the selected item
.
- Are you calling the
command
function? Answer: No. - Who is calling the
command
function? Answer: python.
Python stores the function you specify somewhere, then at some time in the future after you make a selection in the OptionsMenu, python calls the command
function. So python gets to decide how many arguments it will pass to the command
function, and it turns out that python calls the command
function with one argument:
command(selection)
How do you know that python calls the
command
function with one argument? Answer: You check the docs.What happens if you can't find anything in the docs that describes the type of function you need to assign to
command
? Answer: You test it out.
First:
......def do_stuff(x): print(x)tk.OptionMenu( root, str_var, *OPTIONS, command=do_stuff).pack()......--output:--Storm
Next:
......def do_stuff(x, y): print(x, y)tk.OptionMenu( root, str_var, *OPTIONS, command=do_stuff).pack()......--output:--> TypeError: do_stuff() missing 1 required positional argument: 'y'
There are various ways to solve that problem...
Use python's scoping rules:
import tkinter as tkOPTIONS = [ "Fire", "Ice", "Storm", "Life", "Myth", "Death", "Balance"]root = tk.Tk()root.title("Hello")root.geometry("300x200+10+100")frame = [1, 2, 3]def do_stuff(selection): print(selection, frame) #frame is visible inside the def.str_var = tk.StringVar(root)str_var.set(OPTIONS[0]) # default valuetk.OptionMenu( root, str_var, *OPTIONS, command=do_stuff).pack()root.mainloop()
But it's not really a good idea to have functions manipulating global variables, so you can...
Use a wrapper function:
import tkinter as tkOPTIONS = [ "Fire", "Ice", "Storm", "Life", "Myth", "Death", "Balance"]root = tk.Tk()root.title("Hello")root.geometry("300x200+10+100")def wrapper(other): def do_stuff(selection): print(selection, other) return do_stuffstr_var = tk.StringVar(root)str_var.set(OPTIONS[0]) # default valuetk.OptionMenu( root, str_var, *OPTIONS, command=wrapper('xxxx') #This actually executes wrapper(), and a #function call in your code is replaced #by its return value--which happens to #be the name of a function that takes one #argument. The one arg function name is #then assigned to command.).pack()root.mainloop()
Use a default value for your extra parameter variable:
frame = [1, 2, 3]def do_stuff(selection, other=frame): #<****HERE**** print(selection, other)str_var = tk.StringVar(root)str_var.set(OPTIONS[0]) # default valuetk.OptionMenu( root, str_var, *OPTIONS, command=do_stuff).pack()
Warning: Default values for parameter variables have their own issues. The default value is assigned once when the function is defined. Subsequently, no matter how many times you execute the function the same value is used for the default. That means that if the default value is a list, and the first time you call the function you change that list, then the next time you call the function the default value will be the changed list.