I am creating a memory game and am running into a problem with the buttons. I create the buttons using a for loop and place them into an array, but I have no idea how to call a definition OnButtonClick for each one of them. Each button should have a random picture, chosen from eight options, with no more than two duplicates.
from Tkinter import *
import Tkinter
import random
root = Tkinter.Tk()
poop=PhotoImage(file="poop.gif")
apple=PhotoImage(file="apple.gif")
earth=PhotoImage(file="earth.gif")
fish=PhotoImage(file="fish.gif")
frowny=PhotoImage(file="frowny.gif")
heart=PhotoImage(file="heart.gif")
smiley=PhotoImage(file="images.gif")
water=PhotoImage(file="water.gif")
back=PhotoImage(file="card back.gif")
images = [poop,apple,earth,fish,frowny,heart,smiley,water]
row = 1
column = 0
buttonList=[]
def OnButtonClick():
self.Button.config(image=random.choice(images))
for i in range(16):
buttonList.append(Button(root, image=back, width=150,height=250,command=OnButtonClick()))
buttonList[i].grid(row = row,column = column)
column += 1
if column == 4:
column = 0
row += 1
root.mainloop()
How would I go about changing the picture when the buttons are pressed?
I didn't check if your code is working properly but if it works than you have to do something like this:
def OnButtonClick(number):
buttonList[number].config(image=random.choice(images))
for i in range(16):
buttonList.append(Button(root, image=back,width=150,height=250,command=lambda e=i: OnButtonClick(e)))
buttonList[i].grid(row=row, column=column)
Related
Trying my best at learning Tkinter and failing miserably every step of the way.
I'm trying to write a GUI that creates me either a drop-down list, either a button grid, based on row entries in a .csv file that my script imports.
Thing is, both attempts went up in flames, as I am new to this.
Can anyone link me to a resource/ give me the steps for achieving what I want?
Here is the solution. Next time please paste some code you've written so we know what exactly you've tried.
import tkinter as tk
dataMatrix = [] #this will be 2d list containing csv table elements as strings
with open("filename.csv") as file:
for row in file:
dataMatrix.append(row[:-1].split(";"))
mainWindow = tk.Tk()
######## EITHER THIS
optionMenus = []
strVars = []
for i in range(len(dataMatrix)):
strVars.append(tk.StringVar(mainWindow, dataMatrix[i][0]))
#variable containing currently selected value in i-th row, initialised to first element
optionMenus.append(tk.OptionMenu(mainWindow, strVars[-1], *dataMatrix[i])) #drop-down list for i-th row
optionMenus[-1].grid(row = 0, column = i) #placing i-th OptionMenu in window
######## OR THIS
for r in range(len(dataMatrix)):
for c in range(len(dataMatrix[r])):
b = tk.Button(mainWindow, text = dataMatrix[r][c], width = 10)
#creating button of width 10 characters
b.grid(row = r, column = c)
OptionMenu
Control Variables
Generally infohost.nmt.edu and effbot.org are the best resources for learning tkinter.
This question already has answers here:
tkinter creating buttons in for loop passing command arguments
(3 answers)
Closed 4 years ago.
I am new to coding in general (Python is my first language) and I ran into this problem while learning tkinter. My code is as follows:
from tkinter import *
window = Tk()
buttons = []
def position(pos):
print(pos)
for i in range(7):
buttons.append(Button(window, width = 10, height = 5, bg = "red", command = position(i)).grid(row = 0, column = i))
window.mainloop()
This does not work. I want to print the index of the button when that button is clicked. I have tried a few methods to accomplish this, but with no success. The buttons do not necessarily have to be in a list, however the first button must return 0, the second return 1, the third 2 etc. What is the simplest way to do this?
See this:
from tkinter import *
root = Tk()
files = [] #creates list to replace your actual inputs for troubleshooting purposes
btn = [] #creates list to store the buttons ins
for i in range(50): #this just popultes a list as a replacement for your actual inputs for troubleshooting purposes
files.append("Button"+str(i))
for i in range(len(files)): #this says for *counter* in *however many elements there are in the list files*
#the below line creates a button and stores it in an array we can call later, it will print the value of it's own text by referencing itself from the list that the buttons are stored in
btn.append(Button(root, text=files[i], command=lambda c=i: print(btn[c].cget("text"))))
btn[i].pack() #this packs the buttons
root.mainloop()
Taken from: How can I get the button id when it is clicked?
So I am making a "game", where there is a grid with random number from 1 to 9 in each cell. And when you move the cursor on a cell, and press the number on your keyboard which is written in the cell, it should disappear, and another cell should reappear. I am trying to use grid_remove() for hide the object, and grid() to show it, but it doesn't work as I would like to it. Firstly I make a 3x3 grid, and i remove some of its cells, but the shown cells can be shifted to the place of a hidden one and I would avoid of this. In this code, I write the index in the each cell (instead of a random number 1-9). I highlight a part of the code in the beginning, where I remove the cells at the program starting, then i past the whole code.
Choosing which indexes will be removed (I think it should be ok):
exist=[]
t=0
for k in range(0,int((2*size*size)/3)):
while t in exist:
t=randint(0,size*size-1)
print(t)
exist.append(t)
Removing some of the cells, right after making them:
button[index].grid(row=i, column=j)
if index in exist:
button[index].grid_remove()
And my whole code finally:
from random import randint
import tkinter
from tkinter import Button
from tkinter import font
top = tkinter.Tk()
top.title("Game")
top.geometry("500x500")
class table:
def __init__(self,size):
self.size=size
index=0
def onClick(index):
if button[index].cget("bg")=="Red":
button[index].config(bg="Blue")
else:
button[index].config(bg="Red")
def key(event):
if event.widget.cget("text")==event.char:
event.widget.config(text=str(randint(1,9)))
event.widget.show=False
f=randint(0,size*size-1)
if not button[f].winfo_exists():
button[f].grid() #row=int(f/size), column=f%size
def enter(event):
event.widget.focus_set()
def leave(event):
event.widget.config(bg="Red")
button=[]
exist=[]
t=0
for k in range(0,int((2*size*size)/3)):
while t in exist:
t=randint(0,size*size-1)
print(t)
exist.append(t)
for i in range(0, size):
for j in range (0,size):
button.append(Button(top,command=lambda index=index: onClick(index),height=5,width=10,bg="Red",font=5,text=str(index)))
button[index].bind("<Key>",key)
button[index].bind("<Enter>",enter)
button[index].bind("<Leave>",leave)
button[index].grid(row=i, column=j)
if index in exist:
button[index].grid_remove()
index=index+1
racs=table(3)
top.mainloop()
I am beginning GUI in Python 3.5, and I am trying to setup a simple qwerty keyboard. Based on the examples, I tried the following code
from tkinter import Tk, Label, RAISED, Button, Entry
self.window = Tk()
#Keyboard
labels = [['q','w','e','r','t','y','u','i','o','p'],
['a','s','d','f','g','h','j','k','l'],
['z','x','c','v','b','n','m','<']]
n = 10
for r in range(3):
for c in range(n):
n -= 1
label = Label(self.window,
relief=RAISED,
text=labels[r][c])
label.grid(row=r,column=c)
continue
This gives me the first row, but it does not return anything else. I tried simply using 10 as the range, which created the first two rows of the keyboard, but it still did not continue onto the last row.
Your issue is in the line n -= 1. Every time a label is created, you make n one less- after the first whole row, n==0, and thus the range is 0>0, and ranges never include the high bound- for c in range(0) will just drop from the loop (as it has looped through all the nonexistent contents).
A better solution involves iterating through the lists instead of through the indexes- for loops take any iterable (list, dictionary, range, generator, set, &c.);
for lyst in labels:
# lyst is each list in labels
for char in lyst:
# char is the character in that list
label = Label(... text=char) # everything else in the Label() looks good.
label.grid(...) # You could use counters for this or use ennumerate()-ask if you need.
# The continue here was entirely irrelevant.
Is this what you want it to do? Let me know if you need me to explain it further but basically what I'm doing is first filling the columns in each row. So row remains 0 and then as I loop through the column (the inner list) I fill in each of the keys, then on to the next row and etc.
from tkinter import Tk, Label, RAISED, Button, Entry
window = Tk()
#Keyboard
labels = [['q','w','e','r','t','y','u','i','o','p'],
['a','s','d','f','g','h','j','k','l'],
['z','x','c','v','b','n','m','<']]
for r in labels:
for c in r:
label = Label(window, relief=RAISED, text=str(c))
label.grid(row=labels.index(r), column=r.index(c))
window.mainloop()
I am getting problems with Tkinter after() method.
Actually, what I want to do is to change the background colour of some entry boxes as soon as times passes. Let's take this piece of code (which is different from the script I'm working on, but the situation described is the same):
import Tkinter as tk
root = tk.Tk()
root.option_add("*Entry.Font","Arial 32 bold")
emptyLabel=tk.Label()
emptyLabel.grid(row=4) #Empty label for geometry purpose
entryList=[]
for x in range(4):
entryList.append([])
for y in range(4):
entryList[x].append('')
entryList[x][y]=tk.Entry(root, bg="white",width=2,justify="center",
takefocus=True,insertofftime=True)
entryList[x][y].grid(row=x,column=y)
solvebt=tk.Button(root,text='Solve').grid(row=5,column=2)
newgamebt=tk.Button(root,text='New').grid(row=5,column=1)
#BROKEN PART STARTS HERE
def changebg(x,y):
entryList[x][y]['bg']='yellow'
for x in range(4):
for y in range(4):
entryList[x][y].after(300,changebg(x,y))
#Same result with root.after(300,changebg(x,y))
root.mainloop()
The problem is that when I start the program, I would expect it to show me as it "paints", one at time, all of the entry boxes in yellow. What happens, instead, is that the program freezes for (300*16) milliseconds and then, all of a sudded, every entry boxes is yellow!
The problem is here:
def changebg(x,y):
entryList[x][y]['bg']='yellow'
for x in range(4):
for y in range(4):
entryList[x][y].after(300,changebg(x,y))
#Same result with root.after(300,changebg(x,y))
You're calling changebg to immediately in the double for loop -- You're then passing the return value (None) to root.after. This won't lead to the delay that you describe. Perhaps your actual code looks like:
for x in range(4):
for y in range(4):
entryList[x][y].after(300,lambda x=x,y=y : changebg(x,y))
That will lead to the behavior you actually describe. Ultimately, what you need is to flatten your list of widgets and then pass then one at a time -- registering the next one if it exists:
import itertools
all_entries = itertools.chain.from_iterable(entryList)
def changebg(ientries):
ientries = iter(ientries) #allow passing a list in as well ...
entry = next(ientries,None)
if entry is not None:
entry['bg'] = 'yellow' #change the color of this widget
root.after(300,lambda : changebg(ientries)) #wait 300ms and change color of next one.
changebg(all_entries)