Python Forum
[Tkinter] problem with button events - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: GUI (https://python-forum.io/forum-10.html)
+--- Thread: [Tkinter] problem with button events (/thread-6636.html)



problem with button events - Lubik_ - Dec-01-2017

Hi,
I have a part of code, that creates 10 buttons:

for i in range(10):
    b = tkinter.Button(root,text = i, command = lambda: click(i))
... and event click:

def click(num):
   print(num)
How is it possible, that every button click prints number 9? How can I solve this problem? I need the same event for all buttons and recognize them by parameter...(so after clicking on first button, i need to have set num to 0, on the second to 1, etc.

Thank you for help...
L.


RE: problem with button events - Larz60+ - Dec-01-2017

I added code tags to your code.

**Important** You need to do this on future posts, read BBCODE section under help/rules

you are creating 10 buttons but each new one is overwriting the previous which has been assigned to b
the only one that remains is the last one (number 9).
in addition, you need to create your geometry, wither grid, pack, or place for each button

create an array for your buttons, and append each button to the array
as each button is created, don't forget to add a grid, pack, or place command for each button


RE: problem with button events - Windspar - Dec-01-2017

I get the same results
import tkinter as tk
def click(data):
    print("clickevent", data)

def other():
    app = tk.Tk()
    frame = tk.Frame(app)
    frame.pack()

    buttons = []
    for x in range(10):
        b = tk.Button(frame, text='Button ' + str(x), command=lambda: click(x))
        b.pack()
        buttons.append(b)

    app.mainloop()

other()
I thought of 2 ways. Make custom button or make own callback
Here a solution
import tkinter as tk
class Callback:
    callback = {}

    def __init__(self, name, callback, pydata):
        self.callback = callback
        self.pydata = pydata
        Callback.callback[name] = self.click

    def click(self):
        self.callback(self.pydata)

def click(data):
    print("clickevent", data)

def create_buttons(parent):
    for i in range(10):
        cname = "button " + str(i)
        Callback(cname, click, i)
        b = tk.Button(parent, text=cname, command=Callback.callback[cname])
        b.pack()

def main():
    app = tk.Tk()
    frame = tk.Frame(app)
    frame.pack()
    create_buttons(frame)
    app.mainloop()

main()



RE: problem with button events - nilamo - Dec-01-2017

The event handler doesn't have any concept of which i it should be referring to.  It doesn't capture the value when you create it, and instead uses whatever i is when the event handler fires off, which is 9 (unless you click it really fast, before all of them exist).

You can get around that by having a local variable for the event handler which is unique to each event handler.  As was shown above, one way to do that is with a class.  Another is to build a new event handler each time, like so:
# this function is unchanged from your code...
def click(num):
    print(num)

def build_click_handler(num):
    return lambda: click(num)

for i in range(10):
    b = tkinter.Button(root, text = i, command = build_click_handler(i))



RE: problem with button events - Windspar - Dec-01-2017

Another way.
for i in range(10):
        callback = lambda n: lambda: click(n)
        b = tk.Button(frame, text='Button ' + str(i), command=callback(i))
        b.pack()