I'm making Minesweeper with tkinter. I want to have it so that when a button with 0 mines surrounding it is clicked, all the buttons around that one are automatically clicked. However, when I run this and click on a button with 0, the program crashes. (originally it gave me the error of "maximum recursion depth exceeded"). How do I fix this? Here is the full code:
import tkinter as tk
import sys
import random
from tkinter import messagebox
from PIL import Image, ImageTk
from types import FunctionType
from copy import copy
app = tk.Tk()
app.geometry("432x468")
app.title("Minesweeper")
sys.setrecursionlimit(999999999)
colors = ['#FFFFFF', '#0000FF', '#008200', '#FF0000', '#000084', '#840000', '#008284', '#840084', '#000000']
# create lists
def createlists():
global buttonlist
global mylist
global numberlist
global panelist
global rightlist
global flaglist
global rowlist
global columnlist
global abomb
global adic
global secondlist
secondlist = []
abomb = []
adic = {}
buttonlist = []
mylist = [0]
numberlist = []
panelist = []
rightlist = []
flaglist = []
rowlist = []
columnlist = []
for a in range(1,18):
mylist.append(18*a)
for i in range(1,325):
button = "b" + str(i)
flag = 'flag' + str(i)
row = 'row' + str(i)
column = 'column' + str(i)
buttonlist.append(button)
numberlist.append(i)
secondlist.append(i)
flaglist.append(flag)
rowlist.append(row)
columnlist.append(column)
# randomly select bombs
def selectbombs():
for i in range(0,50):
x = "panel" + str(bomblist[i])
panelist.append(x)
for i in bomblist:
n = "a" + str(i)
abomb.append(n)
secondlist.remove(i)
# create function for when a bomb is clicked
def mine():
global bomblist
for i in range(0,50):
panelist[i] = tk.Label(app, image = bomb)
for x in mylist:
for i in range(x,x+18):
if i+1 in bomblist:
thing = bomblist.index(i+1)
panelist[thing].grid(row=mylist.index(x)+1, column=i-x+1, columnspan=1)
MsgBox = tk.messagebox.askquestion ('Game Over', 'You clicked on a bomb! Game Over. Would you like to play again?')
if MsgBox == 'no':
app.destroy()
else:
createlists()
bomblist = random.sample(numberlist, 50)
selectbombs()
buttons()
numbers()
flags()
# create grid of buttons
def buttons():
for i in range(0,324):
if i+1 in bomblist:
buttonlist[i] = tk.Button(app, text="", width=2, height=1, command=mine)
else:
buttonlist[i] = tk.Button(app, text="", width=2, height=1)
for x in mylist:
for i in range(x,x+18):
buttonlist[i].grid(row=mylist.index(x)+1, column=i-x+1, columnspan=1)
rowlist[i] = mylist.index(x)+1
columnlist[i] = i-x+1
# determine the number of bombs adjacent to each button
def numbers():
for i in range(1,325):
adic.update({"a"+str(i) : 0})
alist = list(adic)
for i in bomblist:
for x in numberlist:
if rowlist[x-1] in range(rowlist[numberlist.index(i)]-1, rowlist[numberlist.index(i)]+2) and columnlist[x-1] in range(columnlist[numberlist.index(i)]-1, columnlist[numberlist.index(i)]+2):
adic[alist[x-1]] = adic[alist[x-1]] + 1
for i in bomblist:
del adic[alist[numberlist.index(i)]]
alist = list(adic)
for i in adic:
buttonlist[secondlist[alist.index(i)]-1].bind("<Button-1>", lambda event, x=secondlist[alist.index(i)]-1, y=adic[i] : num(x, y))
# number functions
def num(x, y):
a = rowlist[x]
b = columnlist[x]
if y==0:
buttonlist[x].config(bg = '#FFFFFF')
buttonlist[x]["state"] = "disabled"
if a != 1 and b != 1:
num(x-19, adic["a"+str(x-18)])
if a != 1:
num(x-18, adic["a"+str(x-17)])
if a != 1 and b != 18:
num(x-17, adic["a"+str(x-16)])
if b != 1:
num(x-1, adic["a"+str(x)])
if b != 18:
num(x+1, adic["a"+str(x+2)])
if a != 18 and b != 1:
num(x+17, adic["a"+str(x+18)])
if a != 18:
num(x+18, adic["a"+str(x+19)])
if a != 18 and b != 18:
num(x+19, adic["a"+str(x+20)])
else:
buttonlist[x].config(text = y, disabledforeground = colors[y], bg = '#FFFFFF')
buttonlist[x]["state"] = "disabled"
# create function to place a flag
im = Image.open("flag.png")
im = im.resize((20,20), Image.ANTIALIAS)
flag = ImageTk.PhotoImage(im)
def flags():
for i in range(0,324):
buttonlist[i].bind("<Button-3>", lambda event, x=i : right(event, x))
def right(event, x):
if buttonlist[x]["state"] == "normal":
flaglist[x] = tk.Button(app, text = "", width=18, height=19, image = flag)
flaglist[x].grid(row=rowlist[x], column=columnlist[x], columnspan=1)
flaglist[x].bind("<Button-1>", lambda event: flaglist[x].destroy())
# check if the game has been won
def checkwin():
disnum = 0
for i in secondlist:
if buttonlist[i-1]["state"] == "disabled":
disnum = disnum + 1
if disnum == 274:
MsgBox = tk.messagebox.askquestion ('Game Won', 'You have won the game! Would you like to play again?')
if MsgBox == 'no':
app.destroy()
else:
createlists()
bomblist = random.sample(numberlist, 50)
selectbombs()
buttons()
numbers()
flags()
# open images
img = Image.open("bomb.png")
img = img.resize((20,20), Image.ANTIALIAS)
bomb = ImageTk.PhotoImage(img)
createlists()
bomblist = random.sample(numberlist, 50)
selectbombs()
buttons()
numbers()
flags()
app.mainloop()
the specific part which is causing the program to crash:
def num(x, y):
a = rowlist[x]
b = columnlist[x]
if y==0:
buttonlist[x].config(bg = '#FFFFFF')
buttonlist[x]["state"] = "disabled"
if a != 1 and b != 1:
num(x-19, adic["a"+str(x-18)])
if a != 1:
num(x-18, adic["a"+str(x-17)])
if a != 1 and b != 18:
num(x-17, adic["a"+str(x-16)])
if b != 1:
num(x-1, adic["a"+str(x)])
if b != 18:
num(x+1, adic["a"+str(x+2)])
if a != 18 and b != 1:
num(x+17, adic["a"+str(x+18)])
if a != 18:
num(x+18, adic["a"+str(x+19)])
if a != 18 and b != 18:
num(x+19, adic["a"+str(x+20)])
else:
buttonlist[x].config(text = y, disabledforeground = colors[y], bg = '#FFFFFF')
buttonlist[x]["state"] = "disabled"
The issue is that in the recursion process, the code is backtracking on previous squares. When checking a new square, be sure it has not already be checked.
In the num function, add a line of code to skip squares that have already been disabled:
def num(x, y):
if buttonlist[x]["state"] == "disabled": return # add this line
a = rowlist[x]
b = columnlist[x]
Related
I'm a total beginner who just started learning to code with the book "Head First Learn to Code". I've just finished the book and coded a minesweeper game with python3 on my mac. I hope to get some advice on the game I made. If you feel like the code is too long to read, here are some specific questions I'd like to ask:
What does this: flag = flag.resize((12, 12), Image.ANTIALIAS) actually do, why do I need to assign it a variable instead of just doing this: flag.resize((12, 12), Image.ANTIALIAS) as I'd do with other objects
Is there a better way to create a stopwatch? Mine is the function update_time.
Did I make any mistake regarding the conventions of python? Please point out some for me.
Any help would be much appreciated!
Here's my code:
from tkinter import *
import random as rd
from tkinter import messagebox
from PIL import ImageTk, Image
import sys
class GameGrid(Frame): #the game
def __init__(self, master, height, width, mines_count, player):
Frame.__init__(self, master)
self.grid(row=0)
self.master = master
if sys.platform == 'win32': #checking os
self.platform = 'windows'
else:
self.platform = 'macos'
self.height = height #storing height, width, mines_count, and player's name
self.width = width
self.mines_count = mines_count
self.player_name = player
self.play_time = 0 #initiating play_time and other values
self.lost = False
self.won = False
self.notmine = height * width - mines_count #calculate the number of tiles that are not mines
flag = Image.open('flag.png') #creating and storing flag and bomb images
flag = flag.resize((12, 12), Image.ANTIALIAS)
bomb = Image.open('bomb.png')
bomb = bomb.resize((12, 12), Image.ANTIALIAS)
self.flag = ImageTk.PhotoImage(flag)
self.bomb = ImageTk.PhotoImage(bomb)
grid_model = [[0]*width for item in [0]*height] #creating a list to hold 1's and 0's
while mines_count > 0: #1 is mine, 0 is normal
randi = rd.randint(0, height-1) #putting mines into the list by generating random coordinates
randj = rd.randint(0, width-1) #and storing mine in the corresponding place
if grid_model[randi][randj] == 0:
grid_model[randi][randj] = 1
mines_count -= 1
self.tiles = {} #creating Tiles and storing them using dictionary
for i in range(height):
for j in range(width):
if grid_model[i][j] == 1:
self.tiles[i, j] = Tile(self, i, j, True)
else:
mine_neighbors = 0 #counting nearby mines if Tile in creation is not a mine
if i - 1 >= 0:
if grid_model[i-1][j] == 1:
mine_neighbors += 1
if i - 1 >= 0 and j - 1 >= 0:
if grid_model[i-1][j-1] == 1:
mine_neighbors += 1
if i - 1 >= 0 and j + 1 < width:
if grid_model[i-1][j+1] == 1:
mine_neighbors += 1
if j - 1 >= 0:
if grid_model[i][j-1] == 1:
mine_neighbors += 1
if j + 1 < width:
if grid_model[i][j+1] == 1:
mine_neighbors += 1
if i + 1 < height:
if grid_model[i+1][j] == 1:
mine_neighbors += 1
if i + 1 < height and j - 1 >= 0:
if grid_model[i+1][j-1] == 1:
mine_neighbors += 1
if i + 1 < height and j + 1 < width:
if grid_model[i+1][j+1] == 1:
mine_neighbors += 1
self.tiles[i, j] = Tile(self, i, j, False, mine_neighbors)
def reveal_surroundings(self, i, j): #reveal nearby tiles
revealing = []
width = self.width
height = self.height
if i - 1 >= 0:
revealing.append(self.tiles[i-1, j])
if i - 1 >= 0 and j - 1 >= 0:
revealing.append(self.tiles[i-1, j-1])
if i - 1 >= 0 and j + 1 < width:
revealing.append(self.tiles[i-1, j+1])
if j - 1 >= 0:
revealing.append(self.tiles[i, j-1])
if j + 1 < width:
revealing.append(self.tiles[i, j+1])
if i + 1 < height:
revealing.append(self.tiles[i+1, j])
if i + 1 < height and j - 1 >= 0:
revealing.append(self.tiles[i+1, j-1])
if i + 1 < height and j + 1 < width:
revealing.append(self.tiles[i+1, j+1])
for tile in revealing:
tile.reveal()
def lose(self): #show if lost, stop the clock
global stp
stp = True
self.lost = True
if self.platform == 'windows':
for tile in self.tiles:
if self.tiles[tile].mine:
self.tiles[tile].config(bg='red')
else:
for tile in self.tiles:
if self.tiles[tile].mine:
self.tiles[tile].config(image=self.bomb, padx=9, pady=4, bg='red')
self.tiles[tile].unbind('<Button-1>')
self.tiles[tile].unbind('<Button-2>')
messagebox.showerror(message='Boom, Game Over!!')
self.score = ScoreBoard(self.master)
def win(self): #show if won, stop the clock, creating a window recording scores
global mn, sc, stp
stp = True
self.won = True
for tile in self.tiles:
if self.tiles[tile].mine:
self.tiles[tile].config(image=self.bomb, padx=9, pady=4, bg='red')
self.tiles[tile].unbind('<Button-1>')
self.tiles[tile].unbind('<Button-2>')
messagebox.showinfo(message='Congrats, You Survived ;)')
play_time = str(m) + ' mins, ' + str(s) + ' secs'
self.score = ScoreBoard(self.master, self.player_name, play_time)
class ScoreBoard(Toplevel): #for score recording
def __init__(self, master, name=None, time=None):
Toplevel.__init__(self, master)
self.title('Hall of Fame')
fin_text = ''
if name != None: #writing in the record if there is one
self.board = open('ScoreBoard.txt', 'r') #assigning the text inside ScoreBoard.txt to board_text
board_text = '' #and writing it into ScoreBoard.txt
for line in self.board:
board_text = board_text + line
self.board = open('ScoreBoard.txt', 'w')
self.record = name + ' ' + time
self.board.write(board_text + '\n' + self.record)
self.board = open('ScoreBoard.txt', 'r') #reading text in ScoreBoard and put it on the window
for line in self.board:
fin_text = fin_text + line
self.lbl = Label(self, text=fin_text)
self.lbl.pack()
self.geometry('300x300')
self.board.close()
class Tile(Label): #the Tile
def __init__(self, master, i, j, mine, mine_neighbors=None):
Label.__init__(self, master, width=2, relief=RAISED)
self.grid(row=i, column=j)
self.game = master #storing row, column, is mine or not, count of nearby mines
self.mine = mine
self.row = i
self.col = j
self.mine_neighbors = mine_neighbors
self.revealed = False
self.marked = False
self.bind('<Button-1>', self.reveal) #bind Tile: reveal(left click), mark(right click)
self.bind('<Button-2>', self.mark)
def reveal(self, event=None): #revealing tile
if self.mine:
self.game.lose()
return
else:
if not self.revealed:
self.revealed = True
self.mark()
self.unbind('<Button-1>')
self.unbind('<Button-2>')
if self.mine_neighbors == 0: #if no nearby mines, reveal nearby tiles
self.config(text='', relief=SUNKEN, bg='lightgrey', image='', padx=1, pady=1)
self.game.reveal_surroundings(self.row, self.col)
else:
self.config(text=self.mine_neighbors, relief=SUNKEN, bg='lightgrey', image='', padx=1, pady=1)
self.game.notmine -= 1
if self.game.notmine == 0:
self.game.win()
def mark(self,event=None): #marking tile
if self.game.platform == 'windows':
if not self.marked:
self.config(text='*')
self.marked = True
else:
self.config(text='')
self.marked = False
else:
if not self.marked:
self.config(image=self.game.flag, padx=9, pady=4)
self.marked = True
else:
self.config(image='', padx=1, pady=1)
self.marked = False
stp = False #used to stop the clock when lost or won
def update_time(): #a stopwatch
global m, s, timer, stp
if stp != True:
s = s + 1
if s == 60:
m = m + 1
s = 0
mn = str(m) #making the clock look better by adding a 0 when the number
sc = str(s) #of second or minute is just one digit, e.g. 01, 06, 09..
if len(sc) == 1 and len(mn) == 1:
sc = '0' + sc
mn = '0' + mn
timer.config(text=mn+':'+sc)
elif len(mn) == 1 and len(sc) != 1:
mn = '0' + str(m)
timer.config(text=mn+':'+str(s))
elif len(sc) == 1 and len(mn) != 1:
sc = '0' + sc
timer.config(text=mn+':'+sc)
timer.after(1000, update_time)
def play(height, width, mines_count, player): #initiating the game
global s, m, timer
m = 0
s = -1
time = str(m) + ':' + str(s)
root = Tk()
root.title('MineSweeper')
root.resizable(False, False)
timer = Label(root, text='%i:%i'%(m,s)) #creating stopwatch and update it every second
timer.grid(row=1)
update_time()
game = GameGrid(root, height, width, mines_count, player)
root.mainloop()
if __name__ == '__main__':
play(10, 10, 10, 'Harley')
I am trying to make a memory game. This is what I have so far. How do I solve this problem? If there is any tips that would help in making a memory game using Tkinter, that would be much appreciated!
My code:
from tkinter import *
from tkinter import messagebox
import time
import random
difficulty = 16
rowsize= 4
columnsize = 4
numcount = 0
lastnum = 0
gotitcorrect = False
root = Tk()
root.title("MEMORY GAME!!")
root.configure(bg='gray')
def GameStart():
menuFrame.pack_forget()
gameFrame.pack()
def Timer(tim):
time.sleep(tim)
def GetRandomNumber():
lst1 = [i for i in range(1,9)]
lst2 = [i for i in range(1,9)]
random.shuffle(lst1),random.shuffle(lst2)
numlst = lst1+lst2
return numlst
def WrongOrRight(card, number):
if numcount == 0:
lastnum = number
numcount+=1
card.configure(text=str(number))
elif numcount == 1:
if number == lastnum:
gotitcorrect = True
card.configure(text=str(number))
else:
gotitcorrect = False
card.configure(text='')
numcount -= 1
menuFrame = Frame(root, bg='gray')
menu = [Label(menuFrame,text='MEMORY GAME', bg = 'gray'), Button(menuFrame,command = GameStart,text = 'Start', bg='gray')]
for i in menu:
i.pack()
menuFrame.pack()
numlst = GetRandomNumber()
print(numlst)
gameFrame = Frame(root, bg='gray')
cards = [[Button(gameFrame) for j in range(4)] for i in range(4)]
index = 1
card_dict = {}
for x in range(rowsize):
for y in range(columnsize):
print(index)
cards[x][y].grid(row = y, column = x, padx=20,pady=20)
cards[x][y].configure(text = str(numlst[index-1]))
cards[x][y].configure(command = lambda: WrongOrRight(cards[x][y],numlst[cards[x][y]]))
card_dict[cards[x][y]] = numlst[index-1]
index+=1
Timer(5)
for x in range(rowsize):
for y in range(columnsize):
cards[x][y].configure(text = '')
root.grid_rowconfigure(0,weight=1)
root.grid_columnconfigure(0,weight=1)
root.grid_rowconfigure(rowsize,weight=1)
root.grid_columnconfigure(columnsize,weight=1)
root.mainloop()
This is my error:
Exception in Tkinter callback
Traceback (most recent call last): File
"/usr/lib/python3.6/tkinter/__init__.py", line 1705, in __call__
return self.func(*args) File "memorygame.py", line 62, in <lambda>
cards[x][y].configure(command = lambda: WrongOrRight(cards[x][y],numlst[cards[x][y]])) TypeError: list indices
must be integers or slices, not Button
Here's a version of your code with a number of changes. It fixes the TypeError and gets rid of the card_dict. Its not really needed anyway because that number assigned to each card can easily be stored by adding an attribute to the Button widget representing the card. Doing this also means you only have to pass the card to the WrongOrRight() function.
All important changes have been indicated with # ALL CAPS COMMENTS. I also made cosmetic changes to the code so it followed the PEP 8 - Style Guide for Python Code in an effort to make it more readable and I strongly suggest that you read and follow the guidelines in the future yourself.
from tkinter import *
from tkinter import messagebox
import time
import random
difficulty = 16
rowsize= 4
columnsize = 4
numcount = 0
lastnum = 0
gotitcorrect = False
root = Tk()
root.title("MEMORY GAME!!")
root.configure(bg='gray')
def GameStart():
menuFrame.pack_forget()
gameFrame.pack()
def Timer(tim):
time.sleep(tim)
def GetRandomNumber():
lst1 = [i for i in range(1,9)]
lst2 = [i for i in range(1,9)]
random.shuffle(lst1), random.shuffle(lst2)
numlst = lst1+lst2
return numlst
def WrongOrRight(card): # REMOVED NO LONGER NEEDED SECOND ARGUMENT.
global lastnum, numcount, gotitcorrect # ADDED
number = card.number # ADDED
if numcount == 0:
lastnum = number
numcount += 1
card.configure(text=str(number))
elif numcount == 1:
if number == lastnum:
gotitcorrect = True
card.configure(text=str(number))
else:
gotitcorrect = False
card.configure(text='')
numcount -= 1
menuFrame = Frame(root, bg='gray')
menu = [Label(menuFrame, text='MEMORY GAME', bg='gray'),
Button(menuFrame, command=GameStart, text='Start', bg='gray')]
for i in menu:
i.pack()
menuFrame.pack()
numlst = GetRandomNumber()
print(numlst)
gameFrame = Frame(root, bg='gray')
cards = [[Button(gameFrame) for j in range(4)] for i in range(4)]
index = 1
#card_dict = {} # NOT NEEDED
for x in range(rowsize):
for y in range(columnsize):
print(index)
cards[x][y].grid(row=y, column=x, padx=20, pady=20)
cards[x][y].configure(text=str(numlst[index-1]))
# ADDED DEFAULT ARGUMENTS TO LAMBDA FUNCTION TO MAKE IT WORK PROPERLY
cards[x][y].configure(command=lambda x=x, y=y: WrongOrRight(cards[x][y]))
cards[x][y].number = numlst[index-1] # ADD ATTRIBUTE TO BUTTON WIDGET
# card_dict[cards[x][y]] = numlst[index-1] # NOT NEEDED
index += 1
#Timer(5) # DISABLED FOR TESTING
for x in range(rowsize):
for y in range(columnsize):
cards[x][y].configure(text='')
root.grid_rowconfigure(0,weight=1)
root.grid_columnconfigure(0,weight=1)
root.grid_rowconfigure(rowsize,weight=1)
root.grid_columnconfigure(columnsize,weight=1)
root.mainloop()
Cards is a list of buttons:
cards = [[Button(gameFrame) for j in range(4)] for i in range(4)]
Later in the code we have:
cards[x][y].configure(command = lambda: WrongOrRight(cards[x][y],numlst[cards[x][y]]))
the code above tries to access numlst while it uses a Button as index.
def fctCountdown():
t = 3
countdown = Label(Pong, text = "3", font = ("Fixedsys", 30), bg = "#CB997E", fg = 'black')
countdown.place(x = 387, y = 300)
while t >= 0:
if t == 2:
countdown.config(text = "2")
if t == 1:
countdown.config(text = "1")
if t == 0:
countdown.config(text = "Start !")
sleep(1)
t -= 1
countdown.place_forget()
def fctGoDown():
global x1, y1
def fctGameMenu():
background_image = PhotoImage(master = Pong, file = "fondpong0.png")
background_label = Label(Pong, image = background_image)
background_label.place(x = 0, y = 0)
background_label.image = background_image
def fctStartGame(evt):
global state
if state == 0:
state = 1
fctGameMenu()
sleep(1)
fctCountdown()
Hi guys ! I'm asking you for help because the sleep(1) line in fctStartGame is executed before the fctGameMenu() and i don't understand why. Because of it my countdown doesn't work.
I am new to python, and self taught. I am trying to make a simplifed minesweeper game called "Mines". This game has a 5 by 5 grid of buttons, with a random number of mines. Everything is working fine, except for one issue. After you click a safe space, the button should be disabled so you cannot gain any more points. With my code, I have no idea how to do this.
I did already try to call a method to disable the button, as well as using the DISABLED command. Neither worked. I want the "white" buttons to be disabled after click, as those are the safe spaces. In a perfect world, I will call the disable() method when the button is clicked and use that method to disable that button.
##Create the canvas of the board.
window = Tk ()
window.title("Mines Game")
window.geometry('819x655')
##Scoring function. The first box clicked awards 1 point, and after that each box is worth double your total points.
def score():
global scores
if (scores == 0):
scores = scores + 1
print ("Safe space hit! You have " + str(scores) + " point.")
else:
scores = scores * 2
print ("Safe space hit! You have " + str(scores) + " points.")
def squares():
##Main method of creating a 5x5 square of buttons.
global scores
scores = 0
less_bombs = True
r = -1
c = 0
x = 0
if less_bombs:
for i in range(5):
r = r + 1
for l in range(5):
y = randint(0,25)
if x == 5:
y = 10
if y < 6:
btn = Button(window, bg = "red", command=end)
btn.grid(column = c,row = r)
#btn.grid(sticky="nesw")
btn.config(height = 8, width = 22)
x = x + 1
else:
btn = Button(window, bg = "white", command=lambda:[score(),DISABLED])
btn.grid(column = c,row = r)
#btn.grid(sticky="nesw")
btn.config(height = 8, width = 22)
c = c + 1
c = 0
def end():
##This method creates the popup after you click a mine.
end = Tk ()
end.title ('Game Over!')
end.geometry ('300x161')
btn = Button(end, text ="Close game", command=close)
btn.grid(column = 0,row = 0)
#btn.grid(sticky="nesw")
btn.config(height = 10, width = 20)
btn = Button(end, text ="Reset game", command=lambda:[reset(),end.destroy()])
btn.grid(column = 1,row = 0)
#btn.grid(sticky="nesw"
btn.config(height = 10, width = 20)
if (scores == 1):
print ("Game over! You hit a mine :(")
print ("Your score for that game was " + str(scores) + " point.")
if (scores != 1):
print ("Game over! You hit a mine :(")
print ("Your score for that game was " + str(scores) + " points.")
def disable():
pass
def close():
sys.exit()
def reset():
squares()
squares()
window.mainloop()
I want the button to be disabled after being clicked, however currently the player can click the button as many times as he or she wants.
You can fix it by changing creation of white Buttonss as indicated below in ALL CAPS. This gives the lambda function a default value for btn which changes each iteration of loop (otherwise the function would always refer to the last one created in the for loop).
def squares():
""" Main method of creating a 5x5 square of buttons. """
global scores
scores = 0
less_bombs = True
r = -1
c = 0
x = 0
if less_bombs:
for i in range(5):
r = r + 1
for l in range(5):
y = randint(0,25)
if x == 5:
y = 10
if y < 6:
btn = Button(window, bg="red", command=end)
btn.grid(column=c, row=r)
#btn.grid(sticky="nesw")
btn.config(height=8, width=22)
x = x + 1
else:
btn = Button(window, bg="white") ## REMOVE command= OPTION. ##
btn.grid(column=c, row=r)
#btn.grid(sticky="nesw")
btn.config(height=8, width=22, # USE command SHOWN BELOW. ##
command=lambda btn=btn: [score(), btn.config(state=DISABLED)])
c = c + 1
c = 0
I have 2 classes, i create the instance of each class like this
if __name__ == '__main__':
root = Tk()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.focus_set() # <-- move focus to this widget
root.geometry("%dx%d+0+0" % (w, h))
root.title("Circus ticketing system")
UI_Class = GUI(root)
Program_Class = Program()
root.mainloop()
i set variables in GUI like this:
self.tenAmShowBtn = Button(self.fMain,text='10am Show',command=Program_Class.tenAmShow)
self.tenAmShowBtn.grid(column=0,row=2)
and to access variables in (ex)GUI from Program i do like this:
UI_Class.tenAmShowBtn.config(bg='#00f0ff')
but i keep getting errors like NameError: name 'UI_Class' is not defined
and NameError: name 'Program_Class' is not defined
and AttributeError: 'GUI' object has no attribute 'tenAmShowBtn'i don't understand how the hell do i do this, the Program class continuosly has to access the GUI and edit variables and widgets, but i can't access variables, keep getting error after error, it woks great when i put everything in 1 class, i just CAN'T get this to work with 2 separete classes.
I put the whole code because i cannot explain in just bits of code, i aplogise.
try:
# for Python2
from Tkinter import *
except ImportError:
# for Python3
from tkinter import *
from random import randint
#from user import Program
class GUI:
def __init__(self, parent):
self.seats_being_bought1 = IntVar()#Light-Blue buttons number for textvariable
self.seats_being_bought1.set(0)
self.seats_being_bought2 = IntVar()#Light-Blue buttons number for textvariable
self.seats_being_bought2.set(0)
self.seats_being_bought3 = IntVar()#Light-Blue buttons number for textvariable
self.seats_being_bought3.set(0)
self.available_seats1 = IntVar() #Non-red buttons
self.available_seats1.set("0")
self.available_seats2 = IntVar() #Non-red buttons
self.available_seats2.set(0)
self.available_seats3 = IntVar() #Non-red buttons
self.available_seats3.set(0)
self.pricePerTicket1 = IntVar()
self.pricePerTicket1.set(5)
self.pricePerTicket2 = IntVar()#<> Ticket/Seat Price Variables
self.pricePerTicket2.set(5)
self.pricePerTicket3 = IntVar()
self.pricePerTicket3.set(12)
self.totalPrice1 = IntVar()
self.totalPrice1.set(0)
self.totalPrice2 = IntVar()#total price variables
self.totalPrice2.set(0)
self.totalPrice3 = IntVar()
self.totalPrice3.set(0)
self.totalTicketsSold = IntVar()#Total seats sold
self.totalTicketsSold.set(0)
self.totalIncome = IntVar()#Total income
self.totalIncome.set(0)
self.white = '#ffffff' #save time
self.currentShow = StringVar()#currrent show
self.instructions_text = 'Select the show you desire, then click one of the seats to the right and click the buy button'
self.button_list1={} #all seats(Buttons)as matrix/object
self.button_list2={}
self.button_list3={}
self.total_seats1 = StringVar()
self.total_seats2 = StringVar()
self.total_seats3 = StringVar()
self.total_seats1.set('/250')
self.total_seats2.set('/150')
self.total_seats3.set('/150')
self.selected_button_list1=[] #Light-Blue buttons
self.selected_button_list2=[] #Light-Blue buttons
self.selected_button_list3=[] #Light-Blue buttons
self.previousTransactionButtonList1 = []#List of Seats in the last transaction
self.previousTransactionButtonList2 = []
self.previousTransactionButtonList3 = []
self.automatic_seat_selection_no = IntVar()#automatic seat selection number
self.automatic_seat_selection_no.set(0)
self.f1 = Frame(parent) #Frame for Seats/Buttons
self.seatTitle = Label(self.f1,bg=self.white,textvariable=self.currentShow).grid(row=0,column=0,columnspan=11,sticky=E+W)
self.f1.grid(column=2,row=0)
self.f2 = Frame(parent) #Frame for Seats/Buttons
self.seatTitle = Label(self.f2,bg=self.white,textvariable=self.currentShow).grid(row=0,column=0,columnspan=11,sticky=E+W)
self.f2.grid(column=2,row=0)
self.f3 = Frame(parent) #Frame for Seats/Buttons
self.seatTitle = Label(self.f3,bg=self.white,textvariable=self.currentShow).grid(row=0,column=0,columnspan=11,sticky=E+W)
self.f3.grid(column=2,row=0)
self.f2.grid_remove() #Hide other 2 frames
self.f3.grid_remove() #Hide other 2 frames
#self.create_UI(parent)
#START WITH 10AM SHOW
#self.currentShow.set('10Am Show')
#self.tenAmShowBtn.config(bg='#00f0ff')
Program.tenAmShow(Program)
Program.displaySeats(10)#10 refers to 10am show
#CREATING OTHER SEATS BUT THEIR FRAMES ARE HIDDEN
Program.displaySeats(3)
Program.displaySeats(8)
def create_UI(self,parent):
self.fMain = Frame(parent)#Frame for rest of program
fLegend = Frame(self.fMain)#Frame for Legend
legendLabel = Label(fLegend,text='Legend').grid(column=0,row=0,columnspan=3,sticky=E+W)
legendRed = Label(fLegend,text='seat123',fg='#ff0000',bg='#ff0000').grid(column=0,row=1,sticky=E+W)
legendRed1 = Label(fLegend,text=' =').grid(column=1,row=1)
legendRed2 = Label(fLegend,text='Taken Seat').grid(column=2,row=1)
legendLightRed = Label(fLegend,text='seat123',fg='#f99999',bg='#f99999').grid(column=0,row=2,sticky=E+W)
legendLightRed1 = Label(fLegend,text=' =').grid(column=1,row=2)
legendLightRed2 = Label(fLegend,text='Bought Seat').grid(column=2,row=2)
legendLightBlue = Label(fLegend,text='seat123',fg='#00f0ff',bg='#00f0ff').grid(column=0,row=3,sticky=E+W)
legendLightBlue1 = Label(fLegend,text=' =').grid(column=1,row=3)
legendLightBlue2 = Label(fLegend,text='Selected Seat').grid(column=2,row=3)
fLegend.grid(column=0,row=0,columnspan=3)
#end Legend frame
self.instructions = Label(self.fMain, text=self.instructions_text).grid(column=0,row=1,columnspan=3)
#Show Selection gui
self.tenAmShowBtn = Button(self.fMain,text='10am Show',command=Program_Class.tenAmShow)
self.tenAmShowBtn.grid(column=0,row=2)
self.threePmShowBtn = Button(self.fMain,text='3pm Show',command=Program_Class.threePmShow)
self.threePmShowBtn.grid(column=1,row=2)
self.eightPmShowBtn = Button(self.fMain,text='8Pm Show',command=Program_Class.eightPmShow)
self.eightPmShowBtn.grid(column=2,row=2)
#Purchase details and commands gui
self.seat_amountLabel = Label(self.fMain, text='Amount of seats Available').grid(column=0, row=3)
self.seat_amountEntry = Label(self.fMain, textvariable=self.available_seats1,bg="#444444",fg='#ffffff',anchor=E)
self.seat_amountEntry.grid(column=1,row=3,sticky=E+W)
self.seat_amountTotal = Label(self.fMain,textvariable=self.total_seats1 ,bg='#444444',fg='#ffffff',anchor=W)
self.seat_amountTotal.grid(column=2,row=3,sticky=E+W)
self.seatsBeingBoughtLabel = Label(self.fMain,text='Amount of seats being purchased').grid(column=0,row=4)
self.seatsBeingBoughtVal = Label(self.fMain, textvariable=self.seats_being_bought1,bg="#444444",fg='#ffffff')
self.seatsBeingBoughtVal.grid(column=1,row=4,columnspan=2,sticky=E+W)
#price per ticket
self.pricePerTicketLabel = Label(self.fMain,text='Cost per Ticket/Seat in $').grid(column=0,row=5)
self.pricePerTicketVal = Label(self.fMain,textvariable=self.pricePerTicket1,bg='#ffffff',fg='#444444')
self.pricePerTicketVal.grid(column=1,row=5,columnspan=2,sticky=E+W)
#total price
self.totalPriceLabel = Label(self.fMain,text='Total Price in $').grid(column=0,row=6)
self.totalPriceVal = Label(self.fMain,textvariable=self.totalPrice1,bg='#ffffff',fg='#444444')
self.totalPriceVal.grid(column=1,row=6,columnspan=2,sticky=E+W)
#Automatically select seats
self.auto_seat_selection_label = Label(self.fMain,text='Amount of seats to buy:').grid(column=0,row=7)
self.auto_seat_selection_entry = Entry(self.fMain, textvariable=self.automatic_seat_selection_no).grid(column=1,row=7,columnspan=2,sticky=E+W)
#cancel and purchase button
self.resetBtn = Button(self.fMain,text="Cancel/Reset",bg='#ff0000',command=Program_Class.CancelTransaction).grid(column=0,row=8,columnspan=1,sticky=E+W)
self.buyTicketsBtn = Button(self.fMain,text="Buy ticket",bg='#00f0ff',command=Program_Class.click_function).grid(column=1,row=8,columnspan=2,sticky=E+W)
#totals
self.totalTicketsSoldLabel = Label(self.fMain,text='Total tickets sold:').grid(column=0,row=9)
self.totalTicketsSoldVal = Label(self.fMain,textvariable=self.totalTicketsSold).grid(column=1,row=9,columnspan=2)
self.totalIncomeLabel = Label(self.fMain,text='Total Income for the Day in $:').grid(column=0,row=10)
self.totalIncomeVal = Label(self.fMain,textvariable=self.totalIncome).grid(column=1,row=10,columnspan=2)
self.fMain.grid(column=1,row=0,sticky=N)
class Program:
def __init__(self):
print('test')
def click_function(self): #Buy Button click
show = self.currentShow.get()
action = 0
inputed_seat = self.automatic_seat_selection_no.get()
#for automatic seat selection
if(inputed_seat != 0):
if(show == '10Am Show'):
button_list = self.button_list1
available_seats = self.available_seats1.get()
elif(show == '3Pm Show'):
button_list = self.button_list2
available_seats = self.available_seats2.get()
else:
button_list = self.button_list3
available_seats = self.available_seats3.get()
counter_var = 1
seat = 1
while counter_var <= inputed_seat:#i use while loop instead of for loop so i can continue/extend the loop if i find a taken/red seat
if(inputed_seat > available_seats):
print('Not enough seats available')
break
else:
if seat in button_list:
if(button_list[seat]['bg'] != '#f99999' and button_list[seat]['bg'] != '#00f0ff'):
self.SeatClick(seat)
else:
counter_var -= 1
else:#seat is taken/red
if(available_seats == 0):
print('Not enough seats available')
break
else:
counter_var -= 1
counter_var += 1
seat += 1
self.automatic_seat_selection_no.set(0)
self.click_function()
else:#for manual seat selection
if(show == '10Am Show'):
action = len(self.selected_button_list1)
elif(show == '3Pm Show'):
action = len(self.selected_button_list2)
else:
action = len(self.selected_button_list3)
if(action != 0):
if(show == '10Am Show'):
del self.previousTransactionButtonList1[:]#Clear last transaction
for i in range(len(self.selected_button_list1)):
self.button_list1[self.selected_button_list1[i]].config(bg='#f99999')
self.available_seats1.set(self.available_seats1.get() - 1)
#save to previous transactions
self.previousTransactionButtonList1.append(self.button_list1[self.selected_button_list1[i]])
self.selected_button_list1 = []
self.seats_being_bought1.set(0)
self.totalPrice1.set(0)
elif(show == '3Pm Show'):
del self.previousTransactionButtonList2[:]#Clear last transaction
for i in range(len(self.selected_button_list2)):
self.button_list2[self.selected_button_list2[i]].config(bg='#f99999')
self.available_seats2.set(self.available_seats2.get() - 1)
#save to previous transactions
self.previousTransactionButtonList2.append(self.button_list2[self.selected_button_list2[i]])
self.selected_button_list2 = []
self.seats_being_bought2.set(0)
self.totalPrice2.set(0)
else:
del self.previousTransactionButtonList3[:]#Clear last transaction
for i in range(len(self.selected_button_list3)):
self.button_list3[self.selected_button_list3[i]].config(bg='#f99999')
self.available_seats3.set(self.available_seats3.get() - 1)
#save to previous transactions
self.previousTransactionButtonList3.append(self.button_list3[self.selected_button_list3[i]])
self.selected_button_list3 = []
self.seats_being_bought3.set(0)
self.totalPrice3.set(0)
#get total seats sold INITIAL ONLY, SPECIFIC FUNCTION AT END OF PROGRAM
self.resetVal()
else:
print('No Seats Selected!')
def CancelTransaction(self):
show = self.currentShow.get()
if(show == '10Am Show' and len(self.previousTransactionButtonList1) >= 1):
for x in range(len(self.previousTransactionButtonList1)):
self.previousTransactionButtonList1[x].config(bg='SystemButtonFace')#make button return to available color
self.available_seats1.set(self.available_seats1.get() + 1)#Adjust available seat counter
self.totalTicketsSold.set(self.totalTicketsSold.get() - 1)
self.resetVal()
del self.previousTransactionButtonList1[:]#delete/clear previous transaction
elif(show == '3Pm Show' and len(self.previousTransactionButtonList2) >= 1):
for x in range(len(self.previousTransactionButtonList2)):
self.previousTransactionButtonList2[x].config(bg='SystemButtonFace')
self.available_seats2.set(self.available_seats2.get() + 1)#Adjust available seat counter
self.totalTicketsSold.set(self.totalTicketsSold.get() - 1)
self.resetVal()
del self.previousTransactionButtonList2[:]#delete/clear previous transaction
elif(show == '8Pm Show' and len(self.previousTransactionButtonList3) >= 1):
for x in range(len(self.previousTransactionButtonList3)):
self.previousTransactionButtonList3[x].config(bg='SystemButtonFace')
self.available_seats3.set(self.available_seats3.get() + 1)#Adjust available seat counter
self.totalTicketsSold.set(self.totalTicketsSold.get() - 1)
self.resetVal()
del self.previousTransactionButtonList3[:]#delete/clear previous transaction
else:
print('no previous transaction found')
def seatCounter(self,taken,show): #to count available seats
if(show == 1):
self.available_seats1.set(self.available_seats1.get() - taken)
elif(show == 2):
self.available_seats2.set(self.available_seats2.get() - taken)
else:
self.available_seats3.set(self.available_seats3.get() - taken)
#just to initially update the variables
def resetVal(self):
ticketSold1 = 250 - self.available_seats1.get()
ticketSold2 = 150 - self.available_seats2.get()
ticketSold3 = 150 - self.available_seats3.get()
self.totalTicketsSold.set(ticketSold1 + ticketSold2 + ticketSold3)
self.totalIncome.set((ticketSold1 * 5) + (ticketSold2 * 5) + (ticketSold3 * 12))
#CLICK ON SEAT/BUTTON
def SeatClick(self,seat_no):
show = self.currentShow.get()
action = 0
if(show == '10Am Show'):
action = self.button_list1[seat_no]['bg']
elif(show == '3Pm Show'):
action = self.button_list2[seat_no]['bg']
elif(show == '8Pm Show'):
action = self.button_list3[seat_no]['bg']
else:
return False
if(action == '#f99999'):
print('already bought')
else:
if(show == '10Am Show'):
if(seat_no in self.selected_button_list1):#IF Seat/Button already selected, then remove .after(1000, self.update_clock)
self.button_list1[seat_no].config(bg='SystemButtonFace')
self.selected_button_list1.remove(seat_no)
self.seats_being_bought1.set(str(len(self.selected_button_list1)))
self.totalPrice1.set(self.pricePerTicket1.get() * len(self.selected_button_list1))
else:
self.button_list1[seat_no].config(bg='#00f0ff')#IF Seat/Button not selected then toggle it
self.selected_button_list1.append(seat_no)
self.seats_being_bought1.set(str(len(self.selected_button_list1)))
self.totalPrice1.set(self.pricePerTicket1.get() * len(self.selected_button_list1))
elif(show == '3Pm Show'):
if(seat_no in self.selected_button_list2):
self.button_list2[seat_no].config(bg='SystemButtonFace')#IF Seat/Button already selected, then remove
self.selected_button_list2.remove(seat_no)
self.seats_being_bought2.set(str(len(self.selected_button_list2)))
self.totalPrice2.set(self.pricePerTicket2.get() * len(self.selected_button_list2))
else:
self.button_list2[seat_no].config(bg='#00f0ff')#IF Seat/Button not selected then toggle it
self.selected_button_list2.append(seat_no)
self.seats_being_bought2.set(len(self.selected_button_list2))
self.totalPrice2.set(self.pricePerTicket2.get() * len(self.selected_button_list2))
else:
if(seat_no in self.selected_button_list3):
self.button_list3[seat_no].config(bg='SystemButtonFace')#IF Seat/Button already selected, then remove
self.selected_button_list3.remove(seat_no)
self.seats_being_bought3.set(str(len(self.selected_button_list3)))
self.totalPrice3.set(self.pricePerTicket3.get() * len(self.selected_button_list3))
else:
self.button_list3[seat_no].config(bg='#00f0ff')#IF Seat/Button not selected then toggle it 613553
self.selected_button_list3.append(seat_no)
self.seats_being_bought3.set(len(self.selected_button_list3))
self.totalPrice3.set(self.pricePerTicket3.get() * len(self.selected_button_list3))
# SHOW SELECTION
def tenAmShow(self):
UI_Class.tenAmShowBtn.config(bg='#00f0ff')
self.threePmShowBtn.config(bg=self.white)
self.eightPmShowBtn.config(bg=self.white)
self.currentShow.set('10Am Show')
self.seat_amountEntry.config(textvariable = self.available_seats1)
self.seatsBeingBoughtVal.config(textvariable=self.seats_being_bought1)
self.pricePerTicketVal.config(textvariable=self.pricePerTicket1)
self.totalPriceVal.config(textvariable=self.totalPrice1)
self.seat_amountTotal.config(textvariable=self.total_seats1)
self.f1.grid()
self.f2.grid_remove()
self.f3.grid_remove()
def threePmShow(self):
self.threePmShowBtn.config(bg='#00f0ff')
self.tenAmShowBtn.config(bg=self.white)
self.eightPmShowBtn.config(bg=self.white)
self.currentShow.set('3Pm Show')
self.seat_amountEntry.config(textvariable = self.available_seats2)
self.seatsBeingBoughtVal.config(textvariable=self.seats_being_bought2)
self.pricePerTicketVal.config(textvariable=self.pricePerTicket2)
self.totalPriceVal.config(textvariable=self.totalPrice2)
self.seat_amountTotal.config(textvariable=self.total_seats2)
self.f1.grid_remove()
self.f2.grid()
self.f3.grid_remove()
def eightPmShow(self):
self.eightPmShowBtn.config(bg='#00f0ff')
self.tenAmShowBtn.config(bg=self.white)
self.threePmShowBtn.config(bg=self.white)
self.currentShow.set('8Pm Show')
self.seat_amountEntry.config(textvariable = self.available_seats3)
self.seatsBeingBoughtVal.config(textvariable= self.seats_being_bought3)
self.pricePerTicketVal.config(textvariable=self.pricePerTicket3)
self.totalPriceVal.config(textvariable=self.totalPrice3)
self.seat_amountTotal.config(textvariable=self.total_seats3)
self.f1.grid_remove()
self.f2.grid_remove()
self.f3.grid()
#BUTTON/SEAT CREATION AND DISPLAY
def createSeats(self,num_of_seats,frame_pointer):
col = num_of_seats / 10
if(frame_pointer == 1):
seat_counter1 = 1
for x in range(int(col)):
X = x + 10
for y in range(1, 11):
taken_seats = randint(1,3)
if(taken_seats == 3):
b1 = Button(
self.f1, text='Seat%d' % seat_counter1,
name='seat%d' % seat_counter1, bg='#ff0000'
)
b1.grid(row=X, column=y)
seat_counter1 += 1
self.seatCounter(1, 1)
else:
b1 = Button(
self.f1, text='Seat%d' % seat_counter1,
name='seat%d' % seat_counter1,command= lambda j = seat_counter1: self.SeatClick(j)
)
b1.grid(row=X, column=y)
self.button_list1[seat_counter1] = b1
seat_counter1 += 1
elif(frame_pointer == 2):
seat_counter2 = 1
for x in range(int(col)):
X = x + 10
for y in range(1, 11):
taken_seats = randint(1,3)
if(taken_seats == 3):
b2 = Button(
self.f2, text='Seat%d' % seat_counter2,
name='seat%d' % seat_counter2, bg='#ff0000'
)
b2.grid(row=X, column=y)
seat_counter2 += 1
self.seatCounter(1, 2)
else:
b2 = Button(
self.f2, text='Seat%d' % seat_counter2,
name='seat%d' % seat_counter2,command= lambda j = seat_counter2: self.SeatClick(j)
)
b2.grid(row=X, column=y)
self.button_list2[seat_counter2] = b2
seat_counter2 += 1
else:
seat_counter3 = 1
for x in range(int(col)):
X = x + 10
for y in range(1, 11):
taken_seats = randint(1,3)
if(taken_seats == 3):
b3 = Button(
self.f3, text='Seat%d' % seat_counter3,
name='seat%d' % seat_counter3, bg='#ff0000'
)
b3.grid(row=X, column=y)
seat_counter3 += 1
self.seatCounter(1, 3)
else:
b3 = Button(
self.f3, text='Seat%d' % seat_counter3,
name='seat%d' % seat_counter3,command= lambda j = seat_counter3: self.SeatClick(j)
)
b3.grid(row=X, column=y)
self.button_list3[seat_counter3] = b3
seat_counter3 += 1
def displaySeats(self,show):
if(show == 10):
self.available_seats1.set(250)
self.createSeats(250, 1)
elif(show == 3):
self.available_seats2.set(150)
self.createSeats(150, 2)
else:
self.available_seats3.set(150)
self.createSeats(150, 3)
self.resetVal()
if __name__ == '__main__':
root = Tk()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
#root.overrideredirect(1) #removes menubar
root.focus_set() # <-- move focus to this widget
root.geometry("%dx%d+0+0" % (w, h))
root.title("Circus ticketing system")
UI_Class = GUI(root)
Program_Class = Program()
root.mainloop()
the Program_Class object is instantiated after the UI_Class object and the latter doesn't reference the prior. Try passing Program_Class to the GUI constructor:
Program_Class = Program()
UI_Class = GUI(root, Program_Class)
where:
class GUI:
def __init__(self, parent, program):
self.program = program
then you should be able to reference self.program.