By Marcel


2012-06-02 19:07:13 8 Comments

I am trying to create buttons in tkinter within a for loop. And with each loop pass the i count value out as an argument in the command value. So when the function is called from the command value I can tell which button was pressed and act accordingly. The problem is, lets say len is 3, it will create 3 buttons with titles "Game 1" through "Game 3" but when any of the buttons are pressed the printed value is always 2, the last iteration. So it appears the buttons are being made as separate entities, but the i value in the command arguments seem to be all the same. Here is the code:

def createGameURLs(self):
    self.button = []
    for i in range(3):
        self.button.append(Button(self, text='Game '+str(i+1),command=lambda:self.open_this(i)))
        self.button[i].grid(column=4, row=i+1, sticky=W)
def open_this(self, myNum):
    print(myNum)

Is there a way to get the current i value, upon each iteration, to stick with that particular button?

2 comments

@lukad 2012-06-02 19:13:41

This is how closures work in python. I ran into this problem myself once. You could use functools.partial for this.

for i in range(3):
    self.button.append(Button(self, text='Game '+str(i+1), command=partial(self.open_this, i)))

@BrenBarn 2012-06-02 19:15:21

Change your lambda to lambda i=i: self.open_this(i).

This may look magical, but here's what's happening. When you use that lambda to define your function, the open_this call doesn't get the value of the variable i at the time you define the function. Instead, it makes a closure, which is sort of like a note to itself saying "I should look for what the value of the variable i is at the time that I am called". Of course, the function is called after the loop is over, so at that time i will always be equal to the last value from the loop.

Using the i=i trick causes your function to store the current value of i at the time your lambda is defined, instead of waiting to look up the value of i later.

@Amen 2014-10-14 17:10:56

what if we wanted to pass two arguments to a function like open_this?

@BrenBarn 2014-10-14 18:06:02

@Amen: It depends what you want those arguments to be. If both are coming from some external loop and you want to "freeze" them both in way shown above, you would just do lambda x=x, y=y: self.open_this(x, y).

@Battleroid 2016-03-01 23:38:59

This is brilliant, straightforward and good explanation. This should be the answer.

@velpandian 2018-03-03 16:59:20

@BrenBarn Just now i came across same situation, its really amazing. I really weird how you all came across these kind of techniques. Thank you so much.

Related Questions

Sponsored Content

27 Answered Questions

[SOLVED] How to create an HTML button that acts like a link?

12 Answered Questions

[SOLVED] How to pass arguments to a Button command in Tkinter?

1 Answered Questions

[SOLVED] Tkinter button arguments

  • 2018-08-10 13:20:05
  • Dani Seidler
  • 32 View
  • 0 Score
  • 1 Answer
  • Tags:   python tkinter

2 Answered Questions

[SOLVED] ttk tkinter multiple frames/windows

2 Answered Questions

1 Answered Questions

0 Answered Questions

Python 2 : Tkinter Button Response is very Slow

1 Answered Questions

[SOLVED] tkinter >> how to get button references?

1 Answered Questions

[SOLVED] More on passing arguments to tkinter button command

  • 2013-10-23 11:15:09
  • nurdglaw
  • 878 View
  • 3 Score
  • 1 Answer
  • Tags:   python tkinter

Sponsored Content