0

I used for loop to create buttons in tkinter. And wanna apply an event on all the created button. Event is like, when I click on button its bg color should change and won't back to normal until I click back on it. But in my code color of last button is changing only.

def button_Clicked(e):
    bij['bg'] = 'red'
for i in range (0,8):
    for j in range (0,8):
        bij = tk.Button(compFrame, width = 10, height=4)
        bij.grid(row = i, column = j, padx = 5, pady = 5)
        bij.bind("<Button-1>", button_Clicked)

Is there any way that I can connect all the buttons with event that if I clicked on any button then only its color should change not of the last one or any others.

MD DANISH
  • 39
  • 7

1 Answers1

0

Updated Answer:

As @Thingamabobs pointed out, it's possible to get the button via e.widget in button_Clicked function via the event object e. Thus, it's not necessary to pass the button object to the function.

def button_Clicked(e):
    button = e.widget

    # toggle between 'red' and default color
    if button['bg'] != 'red':
        button['bg'] = 'red'
    else:
        button['bg'] = 'SystemButtonFace'


for i in range(0, 8):
    for j in range(0, 8):
        bij = tk.Button(compFrame, width=10, height=4)
        bij.grid(row=i, column=j, padx=5, pady=5)
        bij.bind("<Button-1>", button_Clicked)

root.mainloop()

Old Answer:

  1. You need to bind the event inside the loop. Right now, bij is outside the loop, so it's only the last button.
  2. To make sure the event changes color for the correct button, you need to add an argument to button_Clicked
  3. To change toggle the background color on each click, it's necessary to add a condition in button_Clicked
def button_Clicked(e, button):
    # toggle between 'red' and default color
    if button['bg'] != 'red':
        button['bg'] = 'red'
    else:
        button['bg'] = 'SystemButtonFace'
for i in range (0,8):
    for j in range (0,8):
        bij = tk.Button(compFrame, width = 10, height=4)
        bij.grid(row = i, column = j, padx = 5, pady = 5)
        bij.bind("<Button-1>", lambda event, bij=bij: button_Clicked(event, bij))

As @BryanOakley rightly point out, it's necessary to use the trick

lambda event, bij=bij: button_Clicked(event, bij)

to make sure the lambda closure uses the current bij value in the loop.

Alternatively, it's possibly cleaner to use functools.partial for the last line

from functools import partial

...
        bij.bind("<Button-1>", partial(button_Clicked, button=bij))
Tim
  • 3,178
  • 1
  • 13
  • 26
  • But still it is not working. only changing color of last button. – MD DANISH Aug 31 '22 at 17:34
  • 1
    You're making a very common mistake. See https://stackoverflow.com/questions/10865116/ – Bryan Oakley Aug 31 '22 at 17:40
  • Thank you so much sir. I got it now. – MD DANISH Aug 31 '22 at 18:03
  • @BryanOakley well caught, answer updated. It's tested and working now. – Tim Aug 31 '22 at 18:04
  • @MDDANISH if someone took the time to help you out, it is good practice to *reward* them by [accept](https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work/5235#5235) their answers. We feel valued helping you gain progress. All of your questions have no accepted answers. You may reread those answers too. – Thingamabobs Aug 31 '22 at 18:31
  • 1
    @Tim parsing the button through is kind of redundant, since you could just do `e.widget` to get the button. – Thingamabobs Sep 01 '22 at 04:56