Hi!
I'm working on program with a lot of buttons and I would like to use For loop to make all of them instead of writing every each one of them. so, can anybody help me?
here is example of code

import tkinter as tk

def klik_1():
    button1.config(image=s1)
def klik_2():
    button2.config(image=s2)
def klik_3():
    button3.config(image=s3)
def klik_4():
    button4.config(image=s4)

root = tk.Tk()
 
frame1 = tk.Frame(root)
frame1.pack(side=tk.TOP, fill=tk.X)

karirano = tk.PhotoImage(file="kari.GIF")
s1 = tk.PhotoImage(file="1.GIF")
s2 = tk.PhotoImage(file="2.GIF")
s3 = tk.PhotoImage(file="3.GIF")
s4 = tk.PhotoImage(file="4.GIF")


button1 = tk.Button(frame1, image=karirano, command=klik_1)
button1.grid(row=0,column=0)
button2 = tk.Button(frame1, image=karirano, command=klik_2)
button2.grid(row=0,column=1)
button3 = tk.Button(frame1, image=karirano, command=klik_3)
button3.grid(row=0,column=2)
button4 = tk.Button(frame1, image=karirano, command=klik_4)
button4.grid(row=0,column=3)



root.mainloop()

It looks easy, replace all the v1, v2, v3 .. variables with arrays v[0], v[1], v[2], something like

import tkinter as tk
from functools import partial

def klik(n):
    button[n].config(image=s[n])

root = tk.Tk()
 
frame1 = tk.Frame(root)
frame1.pack(side=tk.TOP, fill=tk.X)

karirano = tk.PhotoImage(file="kari.GIF")
s = list(tk.PhotoImage(file="%d.GIF" % (i+1)) for i in range(4))
button = list()
for i in range(4):
    button.append(tk.Button(frame1, image=karirano, command=partial(klik, i)))
    button[-1].grid(row=0,column=i)

root.mainloop()

You can also use a dictionary approach as this example shows:

# create multiple Tkinter buttons using a dictionary:

import Tkinter as tk

def text_update(animal):
    text.delete(0, tk.END)
    text.insert(0, animal) 

root = tk.Tk()

text = tk.Entry(root, width=35, bg='yellow')
text.grid(row=0, column=0, columnspan=5) 

btn_dict = {}
col = 0 
words = ["Dog", "Cat", "Pig", "Cow", "Rat"] 
for animal in words:
    # pass each button's text to a function
    action = lambda x = animal: text_update(x)
    # create the buttons and assign to animal:button-object dict pair
    btn_dict[animal] = tk.Button(root, text=animal, command=action) 
    btn_dict[animal].grid(row=1, column=col, pady=5) 
    col += 1 

# run the GUI event loop
root.mainloop()

Bumsfeld, you saved me!

I encountered the following problem in my code that displays the date in boxes for each day of the current week, and allows scheduling events for each day.

for i in range(7):
    date = getDate(i) #This returns the date for each box in the row. 
    button = tk.Button(row) 
    button.configure(text=date)#This works fine for assigning text to the buttons. Each date value is unique for each button.
    button.configure(command=lambda: createEvent(date)) #This does not work, because the function does not pass unique date values in the argument. Every button passes the same date value.
    button.pack()

The createEvent()creates a new window to enter in event details for a particular date. However, createEvent(date) always passes the last date value for every box. Thus, even though I clicking on Monday's box (3/5/2012), the createEvent() still uses Sunday's date (3/11/2012). Instead of creating new function objects, tkinter always refers back to the original function object and updates it. This is not expected behavior, especially since the "command" attribute is the only tkinter attribute I have found that behaves like this.

However, it is solvable by borrowing from bumsfeld. Create the lambda argument outside of the tkinter argument:

action = lambda: createEvent(date)  #Does not work
action = lambda x = date: createEvent(x) #This does work
button.configure(command=action)

As a follow up, can anyone point me to a resource that explains why Tkinter/Python behaves like this?

As a follow up, can anyone point me to a resource that explains why Tkinter/Python behaves like this?

The reason is that when you write

action = lambda x = date: createEvent(x)

the value of the variable 'date' is stored in the anonymous function 'action' and reused when this function is called. On the other hand, when you write

f = lambda: createEvent(date)
...
g = lambda: createEvent(date)

then f and g both use the same variable date. More precisely, they use something that is called the closure of the variable. It means the variable in a given execution frame (which is the same here for f and g).

The best solution is to use curryfication

from functools import partial
action = partial(createEvent, date)

Here again, the value of the variable date is stored in the partial object for later use. This is faster than a lambda form, and python does not need to byte-compile a new function.

Edited 4 Years Ago by Gribouillis: n/a

To understand more about closures, study this code which shows that python creates an object called a 'cell' for each closure, this cell contains the variable's value, but 2 functions created in the same scope share the same cells.

def f():
    date = "Monday"
    def g():
        return date
    print "g:", g.__closure__
    date = "Tuesday"
    def h():
        return date
    print "g:", g.__closure__
    print "h:", h.__closure__
    return g, h
        
print "first call"
g, h = f()
print "second call"
g, h = f()

print g.__code__.co_freevars

""" my output -->
first call
g: (<cell at 0x7fe6970616a8: str object at 0x7fe697062e40>,)
g: (<cell at 0x7fe6970616a8: str object at 0x7fe697062e70>,)
h: (<cell at 0x7fe6970616a8: str object at 0x7fe697062e70>,)
second call
g: (<cell at 0x7fe6970617f8: str object at 0x7fe697062e40>,)
g: (<cell at 0x7fe6970617f8: str object at 0x7fe697062e70>,)
h: (<cell at 0x7fe6970617f8: str object at 0x7fe697062e70>,)
('date',)
"""
This question has already been answered. Start a new discussion instead.