4

I am trying to print out the button number when i click each buttons that are created by for loop. The following is what i have tried.

import Tkinter as tk
root=tk.Tk()

def myfunction(a):
        print a

for i in range(10):
    tk.Button(root,text='button'+str(i),command=lambda:myfunction(i)).place(x=10,y=(10+(25*i)))
root.mainloop()

But instead of printing out each button number, it actually giving me the last button number everytime. Is there anything i can do so that when i click button 1, it will print 1,2 for 2 ,and so on?

Chris Aung
  • 9,152
  • 33
  • 82
  • 127
  • jared's code is a little hack but this is caused by the lambda function , if you work with classes you will not have this problem , command=self.command and the properties are unique to the objects of classes. – Sandiph Bamrel Aug 03 '20 at 08:31

4 Answers4

9

Easy fix is to initialize the lambda function with the current value of i each time the lambda function is created. This can be done using Python default values for another dummy variable j.

command = lambda j=i: myfunction(j)
lindyblackburn
  • 1,655
  • 16
  • 9
4

Blender's answer is a clever solution but in case you get thrown off by the function abstraction, here is another possible way to do it. It really just creates a mapping, saved in buttons, from Button widgets to their proper numbers.

import Tkinter as tk
root = tk.Tk()

def myfunction(event):
    print buttons[event.widget]

buttons = {}
for i in range(10):
    b = tk.Button(root, text='button' + str(i))
    buttons[b] = i # save button, index as key-value pair
    b.bind("<Button-1>", myfunction)
    b.place(x=10,y=(10+(25*i)))
root.mainloop()
Jared
  • 25,627
  • 7
  • 56
  • 61
1

The problem is that the Button command in tk ignores any parameters so insomething like mycommand(data) the data is ignored.

I use buttons quite a bit and decided to subclass a tk Button to include data. I called it a DataButton and added a data member ( in this case an index number). That way when clicked it passes back its data. ( index number) Now I use the DataButton whenever I want it to hold information like an index or even a message.

Arkie
  • 11
  • 1
  • 1
    It would help python beginners (like me) to see the implementation code of your new class. – Mark T Jan 17 '21 at 17:38
0

It's because i in your anonymous function refers to the counter variable, not to the value:

from __future__ import print_function

x = [lambda: print(i) for i in range(10)]

for f in x:
    f()

This produces an output of 10 consecutive 9s.

To get around this, you'd have to use two lambdas and shadow i with a second function (which creates your first function):

from __future__ import print_function

x = [(lambda i: (lambda: print(i)))(i) for i in range(10)]

for f in x:
    f()

Although at that point, you'd be better off just making a named function:

def my_command(i):
    def inner_function():
        return my_function(i)

    return inner_function

And using it like this:

tk.Button(root, text='button' + str(i), command=my_command(i))
Blender
  • 289,723
  • 53
  • 439
  • 496