I have a for loop initiating buttons in a grid, and I want the buttons state to togglable, instead of turning off once the mouse button is no longer held. I also need that to know which button called the buttons' function, because I'm initializing a number of them in the same for loop, meaning they all call the same function once activated, anyone know how to help me?
EDIT:
minimal working example:
import tkinter as gui
def createWindow():
window = gui.Tk()
window.title("GoL")
icon = gui.PhotoImage(file='Not showing ya\'ll my files.png')
window.iconphoto(True, icon)
window.config(background="black")
label = gui.Label(window,\
text="Generation:",\
bg="black",\
fg="white",\
font=("Consolas",20))
label.pack()
return window
def newBoard(x = 10,y = 10):
window = createWindow()
for i in range(0, y):
for j in range(1, x+1):
button = gui.Button(window,bg="black",height=1,width=2,command=changeState)
button.place(x=23*(j-1),y=23*(i+2))
window.mainloop()
what I want is the function changeState to change the
You can achieve this by keeping a list of buttons (buttons), then using a lambda function to pass the row/column number to the button command function, which then allows you to change the properties of the selected button. In this case it toggles between black and white background colours.
def changeState(i, j):
global buttons
b = buttons[i][j]
b.config(bg = "white") if b.cget("bg") != "white" else b.config(bg = "black")
def newBoard(x = 10,y = 10):
global buttons
window = createWindow()
buttons = []
for i in range(0, y):
row = []
for j in range(1, x+1):
button = gui.Button(window,bg="black",height=1,width=2,command=lambda i=i, j=j-1: changeState(i,j))
row.append(button)
button.place(x=23*(j-1),y=23*(i+2))
buttons.append(row)
window.mainloop()
Related
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.
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:
You need to bind the event inside the loop. Right now, bij is outside the loop, so it's only the last button.
To make sure the event changes color for the correct button, you need to add an argument to button_Clicked
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))
I am creating a GUI with a ".grid" of buttons. And I want to make each of those buttons display a different image on press. So when I click button 1, it will bring up "image1", on the bottom of the buttons. When I click button 2, it will bring up "image2" and etc.
Through some research, I was able to make the buttons run a function that takes in a parameter through the method below. However, I can't seem to make the button display the image. rather, it just makes an empty white space underneath the buttons, when I press any buttons.
Disclaimer:
- I do not want there to be loads of images, there will only be 1 image, and it will change depending on what button i press.
Here's the code:
from tkinter import *
def funct(numimg):
image = PhotoImage(file="image"+str(numimg)+".png")
label = Label(image=image)
label.grid(row = row_no+1, column = 0, columnspan = num_of_cols)
def make_funct(number):
return (lambda: funct(number))
root= Tk()
row_no = -1
buttons = []
num_of_cols = 3
root.resizable(0, 0)
numfiles = 6
for x in range(0, numfiles):
if(x % num_of_cols is 0):
row_no+=1
buttons.append(Button(root, text = "Button "+str(x), bg = '#4098D3', width = 30,height = 13,command = make_funct(x)))
buttons[x].grid(row = row_no, column = x % num_of_cols)
root.mainloop()
So my question is, how do you make each individual button, display a different image when it is pressed? this program right here just leaves an empty white space in replace of the image, and the image is not shown.
There are two major problems with the code you posted.
The first is basically the same as in this question: Why does Tkinter image not show up if created in a function?. You should keep a reference to the PhotoImage object, else it will be garbage collected and it will not show.
The second is that you create a new Label on every button click. You should only make one Label and change the image with the label.config() method.
I would (without wrapping your GUI in a class, which might be a nicer solution) load all images on initialization, save them in a list as attribute of the label and only change the image upon a button click.
I also removed your make_funct function and replaced it with a lambda, which is the most used way to pass variables to a function on callback.
from tkinter import *
def funct(numimg):
label.config(image=label.images[numimg])
root= Tk()
row_no = -1
buttons = []
num_of_cols = 3
root.resizable(0, 0)
numfiles = 3
for x in range(0, numfiles):
if(x % num_of_cols is 0):
row_no+=1
buttons.append(Button(root, text = "Button "+str(x), bg = '#4098D3', width = 30,height = 13, command = lambda n=x: funct(n)))
buttons[x].grid(row = row_no, column = x % num_of_cols)
label = Label(root)
label.grid(row = row_no+1, column = 0, columnspan = num_of_cols)
label.images=[]
for x in range(0, numfiles):
label.images.append(PhotoImage(file="image"+str(x)+".png"))
root.mainloop()
I'm using tkinter to create a 8x8 button matrix, which when the individual buttons are pressed add to a final list (eg finalList = ((0,0),(5,7),(6,6), ...), allowing me to quickly create 8x8 (x,y) co-ordinate images. I have created the window with the buttons but now have issues trying to reference these buttons in a function to add to a list or even change the colour of the button
I have read that once the button is created and you create another it moves to that button reference. I suspect I need to use a dict or 2D array to store all these reference of buttons but am struggling to figure out a solution.
from tkinter import *
class App:
def updateChange(self):
'''
-Have the button change colour when pressed
-add coordinate to final list
'''
x , y = self.xY
self.buttons[x][y].configure(bg="#000000")
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.buttons = [] # Do I need to create a dict of button's so I can reference the particular button I wish to update?
for matrixColumn in range(8):
for matrixRow in range(8):
self.xY = (matrixColumn,matrixRow)
stringXY = str(self.xY)
self.button = Button(frame,text=stringXY, fg="#000000", bg="#ffffff", command = self.updateChange).grid(row=matrixRow,column=matrixColumn)
self.buttons[matrixColumn][matrixRow].append(self.button)
root = Tk()
app = App(root)
root.mainloop()
Example of the 8x8 Matrix
Below are 2 examples, the first is if you just want to change the colour and nothing else then you can do it without using a list. The second involves using a list and demonstrates what Delioth has pointed out
class App(object):
def __init__(self, master):
self._master = master
for col in range(8):
for row in range(8):
btn = tk.Button(master, text = '(%d, %d)' % (col, row), bg = 'white')
btn['command'] = lambda b = btn: b.config(bg = 'black')
btn.grid(row = row, column = col)
class App(object):
def __init__(self, master):
self._master = master
self._btn_matrix = []
for col in range(8):
row_matrix = []
for row in range(8):
btn = tk.Button(master, text = '(%d, %d)' % (col, row), bg = 'white',
command = lambda x = row, y = col: self.update(x, y))
btn.grid(row = row, column = col)
row_matrix.append(btn)
self._btn_matrix.append(row_matrix)
def update(self, row, col):
self._btn_matrix[col][row].config( bg = 'black' )
if __name__ == '__main__':
root = tk.Tk()
app = App(root)
root.mainloop()
self.xY is set to 7,7 in your double for loop and never changed. If you want it to be different for each button, you may want to change updateChange to take two parameters (x,y), and pass them in as the command for the button using something like; lambda x=matrixColumn y=matrixRow: self.updateChange(x,y)
Example updateChange
def updateChange(self, x, y):
'''...'''
self.buttons[x][y].configure(bg="black")
I understand that you can make a button that can do some actions when clicked with Tkinter, but how can I just make a button that turns from one color to another when clicked? Then, from that, how do I replicate that button to make a grid of them? I would also settle for a grid of buttons that just change from one character to another.
import Tkinter
color="red"
default_color="white"
def main(n=10):
window = Tkinter.Tk()
last_clicked = [None]
for x in range(n):
for y in range(n):
b = Tkinter.Button(window, bg=default_color, activebackground=default_color)
b.grid(column=x, row=y)
# creating the callback with "b" as the default parameter bellow "freezes" its value pointing
# to the button created in each run of the loop.
b["command"] = lambda b=b: click(b, last_clicked)
return window
def click(button, last_clicked):
if last_clicked[0]:
last_clicked[0]["bg"] = default_color
last_clicked[0]["activebackground"] = default_color
button["bg"] = color
button["activebackground"] = color
last_clicked[0] = button
w = main()
Tkinter.mainloop()
In Tkinter how can I correct this loop/function so that each button changes a value to the value of the button?
This is a simplified version of my code, at the moment each button changes the value of size to 15 rather than the number on the button. I was wondering if there was anyway of fixing this loop without printing each individual button and value without a loop?
from Tkinter import *
size = 7
def AI():
AIBoard = Tk()
AIBoard.title("Board Select")
BoardSize = Label(AIBoard, text = "Please pick a board size: ", font = ('Helvetica',20))
BoardSize.pack(side = 'top')
for a in range(5,16,1):
sizeBut = Button(AIBoard, text = a, width = 5, command = lambda: inputBoardSize(a))
sizeBut.pack(side = 'left')
AIBoard.mainloop()
def inputBoardSize(x):
size = x
print size
AI()
Thanx
Change your lambda to bind the value at the time the anonymous function is created.
lambda a=a: inputBoardSize(a)