Python Forum

Full Version: Returning data on button click by buttons created by a loop
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hi there,

Born again coding newb here .... (2 days in so far....)

I have a GUI that has buttons created based upon the number of lines from a csv file. That creation is no problem.

My issue is I'd like to pass on some form of useful information on a button click pertaining to each individual button.


Here is my simplified snippet of code:

# import
from tkinter import *

# window
root = Tk()
root.geometry('600x400')

def button_click(args):
	Label(root, text = args).pack()

for i in range(5):
	button = Button(root, text = "Button " + str(i), command=lambda: button_click([i]))
	button.pack()

# run
root.mainloop()
Regardless of which button clicked, it will return the last value of i in the loop (4) . I'd like it to return the value of the button as it was created (either as a string or integer)


Thanks!
lambda creates a closure, a combination of a function and a context. In your example the closer contains the function button_click() and the variable i. Each button calls a different lambda, but each lambda calls the same function and passes the same variable.

Enclosing a variable with a function is very powerful, but sometimes it just gets in the way. If you just want to bind a function call with an argument, a better tool is a partial.

https://docs.python.org/3/library/functools.html

This is your code using a partial instead of lambda.
import tkinter as tk
from functools import partial


def button_click(args):
    tk.Label(root, text=args).pack()


root = tk.Tk()
root.geometry("600x400")
for i in range(5):
    tk.Button(root, text=f"Button {i}", command=partial(button_click, i)).pack()
root.mainloop()
If you want to use lambdas, you can accomplish the same thing by creating a variable local to the lambda enclosure.
import tkinter as tk


def button_click(args):
    tk.Label(root, text=args).pack()


root = tk.Tk()
root.geometry("600x400")
for i in range(5):
    tk.Button(root, text=f"Button {i}", command=lambda x=i: button_click(x)).pack()
root.mainloop()
This code works because the lambda expression creates a variable "x" that only exists in the closure created by the lambda. Like before each button calls a different lambda and each lambda calls button_click. The difference is each lambda enclosure as its own variable x instead of a shared variable i.