I am developing a gallery display tool with Python's Tkinter which involves creating a lot of frames at once, each containing an image. This is achieved with 'while' or 'for' loops:
x = 0
while x < 10:
tk.Frame(r, width=50, height=50, borderwidth=1, relief='solid', bg='purple').pack()
x+=1
This works well since I don't need to store any of in it in a variable. However the issue is that I also want to bind an event to each one of these frames which doesn't work as intended:
import tkinter as tk
r = tk.Tk()
# This function switches the Frame's color
def change_color(wid):
# This print function shows that only '.!Frame10' is being affected
print(wid)
if wid['bg'] == 'purple':
wid['bg'] = 'green'
elif wid['bg'] == 'green':
wid['bg'] = 'purple'
# Frames are created by functions to allow binding & packing using a temporary reference
def frame():
main = tk.Frame(r, width=50, height=50, borderwidth=1, relief='solid', bg='purple')
# This bind triggers the function which affects the frame being left-clicked
main.bind_all('<Button-1>', lambda a: change_color(main) )
main.pack()
# This loop creates 10 instances of the frame
x = 0
while x < 10:
frame()
x+=1
r.mainloop()
The intended result is that each individual frame can be left-clicked to switch it's colors but instead only the last frame, dubbed ".!Frame10" switches color if any on the frames are clicked.
I read that you can sort of automate storing objects/functions in variables using dictionaries but it doesn't seem to work with me. This example I made returns " KeyError: '3' " which I don't understand.
import tkinter as tk
r = tk.Tk()
# This function switches the Frame's color
def change_color(x):
# This print function shows that only '.!Frame10' is being affected
print(x)
if x['bg'] == 'purple':
x['bg'] = 'green'
elif x['bg'] == 'green':
x['bg'] = 'purple'
dic = {}
# This loop creates frames and stores them in the dictionary above.
x = 1
while x < 3:
dic[str(x)] = tk.Frame(r, width=50, height=50, borderwidth=1, relief='solid', bg='purple')
dic[str(x)].bind_all('<Button-1>', lambda a: change_color(dic[str(x)]) )
dic[str(x)].pack()
x+=1
r.mainloop()
I did get it to work with dictionaries a few times but it gave the same result as above; only the last frame is changed.
Any help would be appreciated.
This will solve your problem. Explanations given as comments inside the code
import tkinter as tk
r = tk.Tk()
r.geometry('500x700')
# This function switches the Frame's color
def change_color(wid):
current_bg = wid.cget('bg') # use wid.cget()
if current_bg == 'purple':
wid.config(bg = 'green') # Use wid.config
elif current_bg == 'green': # use wid.cget()
wid.config(bg = 'purple') # Use wid.config
def create_frames():
for x in range(10):
frame = tk.Frame(r, width=50, height=50, borderwidth=1, relief='solid', bg='purple')
frame.pack()
# Pass the current frame as a default argument to fix late binding
frame.bind('<Button-1>', lambda event, current_frame=frame: change_color(current_frame))
create_frames()
r.mainloop()