1

I'm trying to dynamically build some buttons in tkinter from a set of data stored in list form. The thing I can't work out is how to put an argument in the callback function. As the code in the callback isn't executed until the time of the callback, the variable used in the callback has changed value by this point.

Here's a (very) simplified version of the code:

from Tkinter import *
from ttk import *

mylist = ['a','b','c','d','e']


class App:

    def __init__(self, master):

        self.frame = Frame(master)
        self.frame.pack()

        for i in range (0,len(mylist)):
            setattr(self, 'button' + str(i), Button(self.frame, text=mylist[i], command= lambda: self.buttoncall(i)))
            getattr(self, 'button' + str(i)).pack(side=LEFT)

    def buttoncall(self,input):
        print mylist[input]

root = Tk()

app = App(root)

root.mainloop()
user1379351
  • 723
  • 1
  • 5
  • 18
  • related: [Why results of map() and list comprehension are different?](http://stackoverflow.com/q/139819/4279) – jfs May 26 '13 at 22:46
  • Aside from the linked duplicate, [please just use a list](https://stackoverflow.com/questions/1373164/) to hold the buttons instead of dynamically creating attribute names and using `setattr` / `getattr`. – Karl Knechtel Aug 16 '22 at 01:33

1 Answers1

6

Set a default value for i:

command=lambda i=i: self.buttoncall(i)

When the callback is called with no arguments, the default value for i will be bound to the local variable i. Since the default value is fixed at the time the lambda is defined, the value of i will be the desired one.

Without the default value for i, when the callback is called i is not a local variable. Python instead finds the value of i in the enclosing scope. By the time the callback is called, however, the for-loop has completed and i is equal to len(mylist)-1. So all the callbacks use the same value of i.

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677