Python: Assign a function to a dynamically created radio button
Question:
I am new to python and have a task to take input from a listbox and create radiobuttons for each entry. In my code I am able to create the radio buttons but they don’t function when I click on them i.e. in this case they don’t print “hello” and the number i. Here’s the code:
def generateGraph():
w = Toplevel(bg = "grey")
w.resizable(0,0)
frameData = Frame(w, bg="grey", padx=10, pady=10)
frameData.grid(row = 0, column=0, pady = 1, padx = 1, sticky = N+E+S+W)
InputLabel = Label(frameData, text="Inputs:", bg="grey")
InputLabel.grid(row=1, column=0, padx=10, sticky=N+E+S+W)
OutputLabel = Label(frameData, text="Outputs:", bg="grey")
OutputLabel.grid(row=1, column=1, padx=10, sticky=N+E+S+W)
i=0
c=[]
inputVar = IntVar()
while(InputBox.get(i)):
c.append(Radiobutton(frameData, text=InputBox.get(i), variable=inputVar, value = i, background="grey", command= hello(i)))
c[i].grid(row = i+2, column = 0, sticky = W)
i=i+1
if makemodal:
w.focus_set()
w.grab_set()
w.wait_window()
def hello(i):
print("hello %d" %i)
Please help and thanks in advance.
Answers:
The problem is that you’re calling hello(i)
at the time of construction of the Radiobutton
, not storing something to be called later:
c.append(Radiobutton(frameData, text=InputBox.get(i), variable=inputVar, value = i, background="grey", command= hello(i)))
Since hello
returns None
, you’re effectively storing command=None
.
You need to store a callable here, like hello
itself, (or a lambda
or partial
, or whatever), not the result of calling it.
For example:
c.append(Radiobutton(frameData, text=InputBox.get(i),
variable=inputVar, value = i, background="grey",
command=functools.partial(hello, i)))
Since you asked in the comments: Note that I used partial
rather than lambda
, because I want to bind in the value of i
, not close over the variable i
. Otherwise, you’d end up with, e.g., 5 radio buttons all bound to the same variable i
with the value 4
. There are other ways around this—use an explicit factory, do lambda x=i: hello(x)
instead of lambda: hello(i)
, etc. To me, partial
seems like the clearest and most explicit, but your mileage may vary. Anyway, there are dozens of questions on SO about this, but the answer to Scope of python lambda functions and their parameters seems particularly clear.
I am new to python and have a task to take input from a listbox and create radiobuttons for each entry. In my code I am able to create the radio buttons but they don’t function when I click on them i.e. in this case they don’t print “hello” and the number i. Here’s the code:
def generateGraph():
w = Toplevel(bg = "grey")
w.resizable(0,0)
frameData = Frame(w, bg="grey", padx=10, pady=10)
frameData.grid(row = 0, column=0, pady = 1, padx = 1, sticky = N+E+S+W)
InputLabel = Label(frameData, text="Inputs:", bg="grey")
InputLabel.grid(row=1, column=0, padx=10, sticky=N+E+S+W)
OutputLabel = Label(frameData, text="Outputs:", bg="grey")
OutputLabel.grid(row=1, column=1, padx=10, sticky=N+E+S+W)
i=0
c=[]
inputVar = IntVar()
while(InputBox.get(i)):
c.append(Radiobutton(frameData, text=InputBox.get(i), variable=inputVar, value = i, background="grey", command= hello(i)))
c[i].grid(row = i+2, column = 0, sticky = W)
i=i+1
if makemodal:
w.focus_set()
w.grab_set()
w.wait_window()
def hello(i):
print("hello %d" %i)
Please help and thanks in advance.
The problem is that you’re calling hello(i)
at the time of construction of the Radiobutton
, not storing something to be called later:
c.append(Radiobutton(frameData, text=InputBox.get(i), variable=inputVar, value = i, background="grey", command= hello(i)))
Since hello
returns None
, you’re effectively storing command=None
.
You need to store a callable here, like hello
itself, (or a lambda
or partial
, or whatever), not the result of calling it.
For example:
c.append(Radiobutton(frameData, text=InputBox.get(i),
variable=inputVar, value = i, background="grey",
command=functools.partial(hello, i)))
Since you asked in the comments: Note that I used partial
rather than lambda
, because I want to bind in the value of i
, not close over the variable i
. Otherwise, you’d end up with, e.g., 5 radio buttons all bound to the same variable i
with the value 4
. There are other ways around this—use an explicit factory, do lambda x=i: hello(x)
instead of lambda: hello(i)
, etc. To me, partial
seems like the clearest and most explicit, but your mileage may vary. Anyway, there are dozens of questions on SO about this, but the answer to Scope of python lambda functions and their parameters seems particularly clear.