I want to create a grid of buttons that will toggle colour when they are clicked. At the moment every button will trigger the bottom right button instead. Below is the code. Two questions, why is it doing this and how do I correct it?
def main(self):
root = Tk()
frame=Frame(root)
frame.grid(row=0,column=0)
self.btn= [[0 for x in xrange(20)] for x in xrange(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")
print x,y
I figured it out. Replace:
self.btn[x][y] = Button(frame,command= lambda: self.color_change(x,y))
With:
self.btn[x][y] = Button(frame,command= lambda x1=x, y1=y: self.color_change(x1,y1))
Sorry if this was a nuisance to any one.
Related
So I know the problem is in the, "firstLabelList[2]['text'] = "Yay it worked!" line, but I don't know how to fix it.
from tkinter import *
class LabelLoop():
def __init__(self):
#create the window
window =Tk()
window.title("Tic Tack Toe! Yay Lets do it!")
window.geometry("350x450")
#window.columnconfigure((1, 2, 3,), weight=1)
#window.rowconfigure((1, 2, 3, 4), weight=1)
x=0
y=0
firstLabelList= [0]*3
#ok, so i have a proof of concept, I can create labels using a loop.
#The next thing I want to do is prove that I can add logic to the labels, so I want to make a button
#that changes the third one.
for i in range (3):
firstLabelList[i]=Label(window, text=f"label {i}").grid(row=x, column=y)
x+=1
def on_click():
firstLabelList[2]['text'] = "Yay it worked!"
changeBttn = Button(window, text="change", command=on_click).grid(row=5, column=0)
#Here is the problem, how do you fix this?
window.mainloop()
LabelLoop()
You're problem is that your are doing .grid() directly. First, create the Label if firstLabelList, then access it again and grid it. The .grid() does not return the object, so you will get None as the return result.
So, the code in the loop should change to:
for i in range (3):
firstLabelList[i]=Label(window, text=f"label {i}")
firstLabelList[i].grid(row=x, column=y)
x+=1
I use python to create a GUI for this game of quixo . but I dont need to play on the GUI . just to show the players movements on it
from tkinter import *
root = Tk()
root.title("QUIXO")
for i in range(1,6):
for j in range(1,6):
myButton = Button(root, text="0", padx = 20, pady = 20)
myButton.grid(row=i, column=j)
root.mainloop()
I have this code which make the 5x5 grid of buttons. I use buttons because I dont know what else to use .
but on this grid I need to add X and 0 depending on where they are on the quixo board .
essentially I have a matrix
as an example
[[-,-,-,-,X]
[-,-,-,-,X]
[-,-,-,0,-]
[0,-,-,0,X]
[0,-,0,0,-]]
how can I place this on that grid of buttons ? the buttons don't need to be clicked . the are just there to show info
This is how I would do it, might not be the most elegant solution:
from tkinter import *
root = Tk()
root.title("QUIXO")
buttonmatrix = [["" for i in range(5)] for i in range(5)] #Creates a 5x5 matrix with 0s as placeholders
#Then iterate through the matrix assigning buttons to each index and gridding them in the tkinter GUI
for i in range(5):
for j in range(5):
buttonmatrix[i][j] = Button(root, text="0", state = "disabled", padx = 20, pady = 20)
buttonmatrix[i][j].grid(row = i, column = j)
def updatebuttongrid(matrix): #A function that updates the text of the buttons in the grid to a matrix passed to it.
for i in range(5):
for j in range(5):
buttonmatrix[i][j].configure(text = str(matrix[i][j]))
example = [["-","-","-","-","X"],
["-","-","-","-","X"],
["-","-","-",0,"-"],
[0,"-","-","0","X"],
[0,"-",0,0,"-"]]
updatebuttongrid(example) #call the function whenever you want to update the GUI
root.mainloop()
Let me know if this helps :)
I am attempting to work with Tkinter, and the mouse events should change the text of the button clicked.
Through testing, this works with right click () and middle click (). However, (left click) uses a function, which should find the "coordinates" of the widget in the array (I need these numbers for later calculations). The bottom row of the grid works, but any other button leads to the bottom-right button being selected.
from tkinter import *
from random import *
win = Tk()
win.geometry("380x410")
win.grid()
buttons = []
def search(event):
for j in range(10):
for i in range(10):
#print(event.widget)
if buttons[j][i] == event.widget:
break
print(i,j)
buttons[j][i].config(text="1")
for y in range(10):
temp = []
for x in range(10):
button = Button(win,text="",width=4,height=2)
button.grid(row=y,column=x)
button.bind("<Button-1>",search)
button.bind("<Button-2>",lambda event: event.widget.config(text=""))
button.bind("<Button-3>",lambda event: event.widget.config(text="R"))
temp.append(button)
buttons.append(temp)
I have tried messing about with lambdas, but I believe the problem lies within the function.
Any help would be appreciated.
Question: Finding widgets in an array Tkinter
Simpler solution
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
for y in range(10):
for x in range(10):
button = tk.Button(self, text="", width=4, height=2)
button._coords = x, y
button.grid(row=y, column=x)
button.bind("<Button-1>", self.on_button_1_click)
def on_button_1_click(self, event):
print('on_button_1_click:{}'.format(event.widget._coords))
if __name__ == '__main__':
App().mainloop()
Maybe this is somewhat close to what you're looking for:
from tkinter import *
from random import *
win = Tk()
win.geometry("380x410")
win.grid()
buttons = []
def search(event):
i2 = 0
j2 = 0
for j in range(10):
for i in range(10):
#print(event.widget)
if buttons[j][i] == event.widget:
i2 = i
j2 = j
event.widget.config(text="1")
break
print(i2,j2)
#buttons[j][i].config(text="1")
for y in range(10):
temp = []
for x in range(10):
button = Button(win,text="",width=4,height=2)
button.grid(row=y,column=x)
button.bind("<Button-1>", lambda event: search(event))
button.bind("<Button-2>",lambda event: event.widget.config(text=""))
button.bind("<Button-3>",lambda event: event.widget.config(text="R"))
temp.append(button)
buttons.append(temp)
win.mainloop()
Are you sure you need coordinates or just the index of the Button in the buttons list?
If you just need to accurately interact with each button try using the index value in a lambda.
from tkinter import *
win = Tk()
win.geometry("380x410")
buttons = []
def search(btn, ndex):
if btn == '1':
buttons[ndex].config(text='1')
if btn == '2':
buttons[ndex].config(text='')
if btn == '3':
buttons[ndex].config(text='R')
for y in range(10):
for x in range(10):
buttons.append(Button(win, text="", width=4, height=2))
ndex = len(buttons) - 1
buttons[-1].bind("<Button-1>", lambda e, z=ndex: search('1', z))
buttons[-1].bind("<Button-2>", lambda e, z=ndex: search('2', z))
buttons[-1].bind("<Button-3>", lambda e, z=ndex: search('3', z))
buttons[-1].grid(row=y, column=x)
win.mainloop()
I am creating a set of buttons via this function:
from tkinter import *
from random import randint
window = Tk()
window.title("Test")
window.geometry('200x200')
color = ["red","blue","green","yellow","black","purple","orange"]
RandInt = 0
j = 0
h = 0
def ButtonDef(xvar = 0,yvar = 0):
btn = Button(command =lambda:[RandomColor()])
btn.grid()
btn.place(x = xvar*50, y = yvar*50, width = 50, height = 50)
def RandomColor():
RandInt = randint (0,6)
btn.configure(bg = color[RandInt])
while j in range (4):
i = 0
j += 1
while i in range (4):
ButtonDef(i,h)
i += 1
if i == 4:
h += 1
window.mainloop()
However, my RandomColor() function is changing only the color of the very button i have pressed - that is fun too, but i wonder how i can make it randomly change the color of all buttons. When being created by a function, i would have guessed that all buttons that were created are named "btn" since thats the only name i have given them.
How could i address all (or one specific) buttons, out of a group of created-by-function buttons?
Or to put it simply, what name do all of those buttons have? Do they share the name "btn"? Are they assigned a hidden ID?
The reason behind your problem:
The problem is that when this line is executed: btn = Button(command =lambda:[RandomColor()]) by the end of the loop, you get a reference only to the last button which is created. You loose reference to other buttons.
Solution:
You can overcome this problem if you rely on winfo_children().
You have to do 2 steps to fix your issue:
First of all, change: btn = Button(command =lambda:[RandomColor()]) to btn = Button(window, command=lambda:[RandomColor()]). That simply means we attach each created button to a parent widget called window.
Then all that you need to change is RandomColor() function body as follows:
def RandomColor():
for child in window.winfo_children():
child.configure(bg=color[randint(0,6)])
Demo:
This solves your problem but your code is not clean. You can ask for suggestions to improve your code on Code Review website.
EDIT:
Here is a solution for the scenario you described in the comment.
Note that I had to create code from scratch, cleaner one (I know you started only today, so I am not blaming you). In this code, I keep reference for every button I create:
import tkinter as tk
import random
class ChangeBottomRightColor(tk.Frame):
def __init__(self, master):
self.master = master
tk.Frame.__init__(self, self.master)
self.__colors = ["red","blue","green","yellow","black","purple","orange"]
self.configure_gui()
self.create_widgets()
def configure_gui(self):
pass
def create_widgets(self):
self.create_buttons()
def create_buttons(self):
self.buttons = {}
c = 0
for i in range(4):
for j in range(4):
self.buttons['button{}'.format(c)] = tk.Button(self.master)
self.buttons['button{}'.format(c)].grid(row=i, column=j)
self.buttons['button{}'.format(c)].config(width=3, height=3)
self.buttons['button{}'.format(c)].config(command=self.change_bottom_right_button_color)
c += 1
def get_random_color(self):
return random.choice(self.__colors)
def change_bottom_right_button_color(self):
self.buttons['button{}'.format(15)].config(bg=self.get_random_color())
if __name__ == '__main__':
root = tk.Tk()
main_app = ChangeBottomRightColor(root)
root.mainloop()
Demo:
Let try
btn = []
for i in range(16):
btn.append(Button(window))
it will create an array of button. So you can access by btn[i].configure(command=lambda:[RandomColor()]) or something else.
I'm trying to make the Minesweeper game. For each undifferentiated square I created a button.
my_list = [[0 for i in range(9)] for j in range(9)]
all_buttons = []
def creaMatriz():
for y, row in enumerate(my_list):
buttons_row = []
for x, element in enumerate(row):
boton2 = Button(root, text="", width=6, height=3, command=lambda a=x, b=y: onButtonPressed(a, b))
boton2.grid(row=y, column=x)
buttons_row.append(boton2)
all_buttons.append(buttons_row)
def onButtonPressed(x, y):
all_buttons[y][x]['text'] = str(qwer[x][y]) # Some action!!!
....
When i press the left mouse button on a undifferentiated square, I'm calling the function onButtonPressed(x, y), and a digit or a mine appears on the square.
How can I call another function when pressing the right mouse button on a undifferentiated square. I want see 'M' on the square.
full code: http://pastebin.com/cWGS4fBp
You need to bind keys you wish in order to get this functionality. Here's a simple concept:
from tkinter import *
root = Tk()
def left(event):
label.config(text="Left clicked")
def right(event):
label.config(text="Right clicked")
label = Label(root, text="Nothing")
label.pack()
label.focus_set()
label.bind("<1>", left)
label.bind("<3>", right)
Let us know if it is what you're looking for.
There's nothing special you need to do, you simply need to bind each mouse button separately rather than using the command attribute.
For example, let's create a callback for the left and right mouse buttons:
def onLeftClick(x, y):
print("you clicked on %x,%y" % (x, y))
def onRightClick(x, y):
print("you clicked on %x,%y" % (x, y))
Next, we can bind to each of those functions separately using the bind method. Since we're adding custom bindings, we do not want to set the command attribute of the button.
def creaMatriz():
for y, row in enumerate(my_list):
buttons_row = []
for x, element in enumerate(row):
button = Button(root, text="", width=6, height=3)
...
button.bind("<1>", lambda event, x=x, y=y: onLeftClick(x,y))
button.bind("<3>", lambda event, x=x, y=y: onRightClick(x,y))