0

When searching for the answer to this I found a similar post online, which I will link bellow, and their code is what I have been working from.

Array of buttons in python

After reading this I decided I would copy it in to a file and try get it to work and I ended up with this...

from tkinter import *

class testClass:
    def main(self):
        root = Tk()
        frame=Frame(root)
        frame.grid(row=0,column=0)

        self.btn=  [[0 for x in range(20)] for x in range(60)] 
        for x in range(60):
            for y in range(20):
                self.btn[x][y] = Button(frame,command= lambda: self.color_change(x,y))
                self.btn[x][y].grid(column=x, row=y)

        root.mainloop()

    def color_change(self,x,y):
        self.btn[x][y].config(bg="red")

testMain = testClass()

testMain.main()

However this has taken me back to a very similar place to where I was before. I can only change the color of the most recently made button. How would I fix this?

Here is my old code before I found the last post encase that helps too:

from tkinter import *

main = Tk()

wd = Frame(main, width = "500", height = "500", bg = "brown")
wd.pack( padx = 5, pady = 5)

tile = []

def onClick():
    tile[-1].config(bg = "green") 
#I have no idea how I would find where the button I clicked is in the array. Might not be possible the way I have done it.

for index in range(8):
    for counter in range(8):
        if index in range(2, 5):
            tile.append(Button(wd, width = 2, height = 1, command = onClick))
            tile[-1].grid(padx = 0.5, pady = 0.5, row = index, column = counter)
            
        elif index in range(0, 2):
            tile.append(Button(wd, width = 2, height = 1, text = "O", fg = "red", command = onClick))
            tile[-1].grid(padx = 0.5, pady = 0.5, row = index, column = counter)
            
        elif index in range(6, 8):
            tile.append(Button(wd, width = 2, height = 1, text = "O", fg = "blue", command = onClick))
            tile[-1].grid(padx = 0.5, pady = 0.5, row = index, column = counter)
            
main = mainloop()
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
  • try using `from functools import partial`; `command = partial(self.color_change, x, y)` instead of lambda function for command argument of a button. – matszwecja Jun 21 '22 at 12:22
  • Also, if you're looking at the question, consider also looking at its answers as well. Questions usually contain incorrect code, while answers usually contain correct one. – matszwecja Jun 21 '22 at 12:24
  • will do. think I maybe should have scrolled more before posting this then – Arran McLoughlin Jun 21 '22 at 12:26
  • 1
    Your issue is about lambda's closure. When you do `command= lambda: self.color_change(x,y)` the `x` and `y` are bound to the ***global*** values which will only have the final values of the loop. If you do `command= lambda x=x, y=y: self.color_change(x,y)` you create a ***closure*** on the values of each iteration and that's the minimal change you can do to solve your problem (checked it) – Tomerikoo Jun 21 '22 at 12:27
  • 1
    Which is exactly what @matszwecja's solution achieves in another way (`partial` creates a closure on the values) – Tomerikoo Jun 21 '22 at 12:28
  • What does closure mean exactly? I understand if a variable is global the entire script can access it so does closure mean its specific to that function or class??? Thanks for trying to explain this, good to understand the code you use ig – Arran McLoughlin Jun 21 '22 at 12:32
  • https://www.programiz.com/python-programming/closure – matszwecja Jun 21 '22 at 12:40

1 Answers1

0

try using from functools import partial; command = partial(self.color_change, x, y) instead of lambda function for command argument of a button. – matszwecja 1 min ago

Yup This works! Thanks so much!