How Can I Identify Buttons, Created In A Loop?
Solution 1:
While creating buttons in a loop, we can create (actually get) the unique identity.
For example: if we create a button:
button = Button(master, text="text")
we can identify it immediately:
print(button)
> <tkinter.Buttonobject .140278326922376>
If we store this identity into a list and asign a command to the button(s), linked to their index during creation, we can get their specific identity when pressed.
The only thing we have to to then is to fetch the button's identity by index, once the button is pressed.
To be able to set a command for the buttons with the index as argument, we use functools
' partial
.
Simplified example (python3
)
In the simplified example below, we create the buttons in a loop, add their identities to the list (button_identities
). The identity is fetched by looking it up with: bname = (button_identities[n])
.
Now we have the identity, we can subsequently make the button do anything, including editing- or killing itself, since we have its identity.
In the example below, pressing the button will change its label to "clicked"
from tkinter import *
from functools import partial
win = Tk()
button_identities = []
defchange(n):
# function to get the index and the identity (bname)print(n)
bname = (button_identities[n])
bname.configure(text = "clicked")
for i inrange(5):
# creating the buttons, assigning a unique argument (i) to run the function (change)
button = Button(win, width=10, text=str(i), command=partial(change, i))
button.pack()
# add the button's identity to a list:
button_identities.append(button)
# just to show what happens:print(button_identities)
win.mainloop()
Or if we make it destroy the buttons once clicked:
from tkinter import *
from functools import partial
win = Tk()
button_identities = []
defchange(n):
# function to get the index and the identity (bname)print(n)
bname = (button_identities[n])
bname.destroy()
for i inrange(5):
# creating the buttons, assigning a unique argument (i) to run the function (change)
button = Button(win, width=10, text=str(i), command=partial(change, i))
button.place(x=0, y=i*30)
# add the button's identity to a list:
button_identities.append(button)
# just to show what happens:print(button_identities)
win.mainloop()
Simplified code for your matrix (python3):
In the example below, I used itertools's product() to generate the coordinates for the matrix.
#!/usr/bin/env python3from tkinter import *
from functools import partial
from itertools import product
# produce the set of coordinates of the buttons
positions = product(range(10), range(10))
button_ids = []
defchange(i):
# get the button's identity, destroy it
bname = (button_ids[i])
bname.destroy()
win = Tk()
frame = Frame(win)
frame.pack()
for i inrange(10):
# shape the grid
setsize = Canvas(frame, width=30, height=0).grid(row=11, column=i)
setsize = Canvas(frame, width=0, height=30).grid(row=i, column=11)
for i, item inenumerate(positions):
button = Button(frame, command=partial(change, i))
button.grid(row=item[0], column=item[1], sticky="n,e,s,w")
button_ids.append(button)
win.minsize(width=270, height=270)
win.title("Too many squares")
win.mainloop()
More options, destroying a button by coordinates
Since product()
also produces the x,y coordinates of the button(s), we can additionally store the coordinates (in coords
in the example), and identify the button's identity by coordinates.
In the example below, the function hide_by_coords():
destroys the button by coordinates, which can be useful in minesweeper
-like game. As an example, clicking one button als destroys the one on the right:
#!/usr/bin/env python3from tkinter import *
from functools import partial
from itertools import product
positions = product(range(10), range(10))
button_ids = []; coords = []
defchange(i):
bname = (button_ids[i])
bname.destroy()
# destroy another button by coordinates# (next to the current one in this case)
button_nextto = coords[i]
button_nextto = (button_nextto[0] + 1, button_nextto[1])
hide_by_coords(button_nextto)
defhide_by_coords(xy):
# this function can destroy a button by coordinates# in the matrix (topleft = (0, 0). Argument is a tupletry:
index = coords.index(xy)
button = button_ids[index]
button.destroy()
except (IndexError, ValueError):
pass
win = Tk()
frame = Frame(win)
frame.pack()
for i inrange(10):
# shape the grid
setsize = Canvas(frame, width=30, height=0).grid(row=11, column=i)
setsize = Canvas(frame, width=0, height=30).grid(row=i, column=11)
for i, item inenumerate(positions):
button = Button(frame, command=partial(change, i))
button.grid(column=item[0], row=item[1], sticky="n,e,s,w")
button_ids.append(button)
coords.append(item)
win.minsize(width=270, height=270)
win.title("Too many squares")
win.mainloop()
Solution 2:
If you just want to destroy the Button widget, the simple way is to add the callback after you create the button. Eg,
import Tkinter as tkgrid_size=10
root = tk.Tk()
blank = " " * 3for y in range(grid_size):
for x in range(grid_size):
b = tk.Button(root, text=blank)
b.config(command=b.destroy)
b.grid(column=x, row=y)
root.mainloop()
However, if you need to do extra processing in your callback, like updating your grid of buttons, it's convenient to store the Button's grid indices as an attribute of the Button object.
from __future__ import print_function
import Tkinter as tk
classButtonDemo(object):
def__init__(self, grid_size):
self.grid_size = grid_size
self.root = tk.Tk()
self.grid = self.button_grid()
self.root.mainloop()
defbutton_grid(self):
grid = []
blank = " " * 3for y inrange(self.grid_size):
row = []
for x inrange(self.grid_size):
b = tk.Button(self.root, text=blank)
b.config(command=lambda widget=b: self.delete_button(widget))
b.grid(column=x, row=y)
#Store row and column indices as a Button attribute
b.position = (y, x)
row.append(b)
grid.append(row)
return grid
defdelete_button(self, widget):
y, x = widget.position
print("Destroying", (y, x))
widget.destroy()
#Mark this button as invalid
self.grid[y][x] = None
ButtonDemo(grid_size=10)
Both of these scripts are compatible with Python 3, just change the import line to
import tkinter as tk
Solution 3:
Try modifying your code as below:
self.b=[[0for x inrange(10)] for y inrange(10)] #The 2 dimensional list
xp = yp = 0for i inrange(10):
for j inrange(10):
self.b[i][j]=tkinter.Button(root,text=" ",command=lambda i=i,j=j: self.delete(i,j)) # creating the button
self.b[i][j].place(x=xp,y=yp) # placing the button
xp+=26#because the width and height of the button is 26
yp+=26
xp=0
and:
defdelete(self, i, j):
self.b[i][j].destroy()
Solution 4:
The following code generates 12 buttons ,4 in each row. The particular button required to be edited is called similar to calling a matrix element. As an example button[1,1] has been edited for background color and button[2,2] has been edited for foreground color and text. The programme is tested on on python3.6 pycharm console
from tkinter import *
root=Tk()
Buts={}
for r inrange(3):
for c inrange(4):
Buts[(r,c)]=Button(root,text='%s/%s'%(r,c),borderwidth=10)
Buts[r,c].grid(row=r,column=c)
Buts[1,1]['bg']='red'
Buts[2,2]['text']=['BUTTON2']
Buts[2,2]['fg']=['blue']
root.mainloop()
Solution 5:
there is a way to pass arguments to a function that is executed when a button is pressed:
from tkinter import *
from functools import partial
root = Tk()
root.geometry("300x200")
b = Button(root, text = "some text", command=partial(yourfunc, argument))
b.pack()
root.mainloop()
Post a Comment for "How Can I Identify Buttons, Created In A Loop?"