How do you disable a button when it is clicked? - python

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

Related

Why does my program require me to press the enter key before continuing (python tkinter)

I am making a program, using python tkinter, which simply prints some circles to the screen (I call it a board in this program). The program moves on to a different "board" once the mouse cursor moves over the button. The problem I have is that I simply call the "create_board" function 3 times using a for loop however in-between each iteration of the loop the "enter" key must be pressed. This isn't a massive deal but I'm trying to understand why and if there is a way to remove this requirement and have the next board load automatically.
I'm certain it has something to do with the tkinter windows and triggering the command "destroy" once the buttons (circles) are pressed however I'm still learning how to effectively use tkinter and any help would be very much appreciated.
def create_board(user_name, board):
# define the name of tkinter window
win = Tk()
# get the size of the displace for position and size calculations
app = wx.App(False)
w, h = wx.GetDisplaySize()
name = user_name
# define variables based on board number
if board == 0:
gx_pos = int(w/8) # locations of circles
gy_pos = int(h/8)
bx_pos = (w/8)*5
by_pos = (h/8)*5
board_num = str(1)
elif board == 1:
gx_pos = int(w/12)
gy_pos = int(h/12)
bx_pos = (w/6)*5
by_pos = (h/6)*5
board_num = str(2)
elif board == 2:
gx_pos = int(w/3)
gy_pos = int(h/3)
bx_pos = (w/3)*2
by_pos = (h/3)*2
board_num = str(3)
# records the mouse cursor position into a file along with time taken
def record_pos(x, y, board_num, s):
filename = name + "_" + board_num + ".txt"
try:
os.path.isfile('./'+filename)
except:
open(filename, 'r')
with open(filename, 'a') as f:
f.write(str(x) + "," + str(y) + "," + str(s) + "\n")
# determining when left click should be made
def mouse_pos():
flags, hcursor, (x, y) = win32gui.GetCursorInfo()
time_taken = time.time()
record_pos(x, y, board_num, time_taken)
mouse.click('left')
win.after(500, mouse_pos)
# wait 3 seconds before loading first board
time.sleep(3)
geometry = "%dx%d" % (w,h)
win.geometry(geometry)
win.attributes('-fullscreen', True)
win.config(cursor="circle")
# get the grid image
bg = Image.open("grid_image.png")
img = bg.resize((w, h))
grid_img=ImageTk.PhotoImage(img)
image_label = Label(win, image=grid_img)
image_label.place(x=0, y=0, relwidth=1, relheight=1)
# print an image of a green circle
gw = int(w/26)
gh = int(h/15)
g_circle = Image.open('green_circle.png')
g_img = g_circle.resize((gw,gh))
g_circle_image=ImageTk.PhotoImage(g_img)
g_label = Label(win, image=g_circle_image)
g_label.place(x = gx_pos,y = gy_pos)
g_btn = Button(win, image=g_circle_image, command = win.destroy)
g_btn.place(x= gx_pos , y= gy_pos)
# print an image of a blue circle
bw = int(w/26)
bh = int(h/15)
b_circle = Image.open('circle.png')
b_img = b_circle.resize((bw,bh))
b_circle_image=ImageTk.PhotoImage(b_img)
b_label = Label(win, image=b_circle_image)
b_label.place(x=bx_pos, y=by_pos)
b_btn = Button(win, image=b_circle_image, command = win.destroy)
b_btn.place(x=bx_pos, y=by_pos)
# record mouse position
mouse_pos()
win.mainloop()
EDIT: I added the simple for loop that I'm using to iterate through the boards.
for i in range(3):
create_board(user_name, i)
The issue was caused by using time.sleep() in tkinter. After removing this the code runs with out requiring an enter key press each time.

Is there a way to update Tkinter canvas live with text?

I am trying to visualise the backtracking algorithm to solve sudoku puzzles using Tkinter (Example video: https://www.geeksforgeeks.org/building-and-visualizing-sudoku-game-using-pygame/)
def play_puzzle(self):
self.play_frame.pack_forget()
self.home_frame.pack_forget()
self.play_frame.pack(fill=BOTH, expand=1)
self.canvas = Canvas(self.play_frame, width=WIDTH, height=HEIGHT)
self.canvas.grid(row=0, column=0, columnspan=9, rowspan=9)
self.canvas.bind("<Button-1>", self.cell_clicked)
self.canvas.bind("<Key>", self.key_pressed)
solution_btn = ttk.Button(self.play_frame, text='Solution', command=self.solve_puzzle)
home_btn = ttk.Button(self.play_frame, text='Home', command=lambda: self.return_home('play'))
clear = ttk.Button(self.play_frame, text='clear', command = lambda: self.canvas.delete('numbers'))
view_solution_btn = ttk.Button(self.play_frame, text='View Solution', command=self.view_solution)
solution_btn.grid(row= 1, column = 11)
home_btn.grid(row = 3, column = 11)
clear.grid(row=5, column = 11)
view_solution_btn.grid(row=7, column = 11)
self.draw_grid()
self.draw_puzzle()
def view_solution(self):
find = self.game.find_empty()
if not find:
print('Solution found')
return True
else:
e_row, e_col = find
for i in range(1,10):
if self.game.is_valid(i, e_row, e_col):
self.game.puzzle[e_row][e_col] = i
self.play_puzzle()
time.sleep(1)
if self.view_solution():
return True
self.game.puzzle[e_row][e_col] = 0
return False
def draw_puzzle(self):
self.canvas.delete("numbers")
for i in range(9):
for j in range(9):
answer = self.game.puzzle[i][j]
if answer != 0:
x = MARGIN + j * SIDE + SIDE / 2
y = MARGIN + i * SIDE + SIDE / 2
original = self.game.start_puzzle[i][j]
color = "black" if answer == original else "sea green"
self.canvas.create_text(x, y, text=answer, tags="numbers", fill=color)
def draw_grid(self):
for i in range(10):
color = 'blue' if i%3==0 else 'gray'
x0 = MARGIN + i*SIDE
y0 = MARGIN
x1 = MARGIN + i*SIDE
y1 = HEIGHT - MARGIN
self.canvas.create_line(x0,y0,x1,y1, fill=color)
x0 = MARGIN
y0 = MARGIN + i*SIDE
x1 = WIDTH-MARGIN
y1 = MARGIN + i*SIDE
self.canvas.create_line(x0,y0,x1,y1, fill=color)
When I call the view_solution function in the above snippet(by clicking the view solution button), it doesn't update the canvas every time it runs but outputs the answer/fills the puzzle with solution after it completes the entire loop. Is there a way that I could make this work like the one in the video shown?
I have tried using .after() function in Tkinter but I am not sure how to implement it perfectly.
Entire code here - https://github.com/ssram4298/sudoku_gui_tkinter
There are a few ways to update the canvas while processing it.
root.update() #self.parent.update() in your code
root.update_idletasks() #self.parent.update_idletasks() in your code
You can also update individual widgets by calling update() on them (myButton.update()).
If you need to process a change on a widget it needs to be updated before it will be rendered.

Maximum recursion error depth causing crash?

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]

How to allow Tkinter GUI to detect key presses when not selected

I am trying to make a full GUI, for A game borderlands 2, however it only detects my key presses when the tkinter box is selected (in focus). In a game this is not happening, so I need a way for tkinter to detect my key presses while not selected.
This is the code so far. It is not finished, ignore the "Auto Exec" and "Auto Exit" the problem is "Auto Reload". Clicking once will turn it ON and again will turn it OFF. Just choose any number (1-4) for the weapon slot, does not affect the error.
from easygui import *
from pyautogui import *
from time import *
import os
from tkinter import *
count = 1
slot = "0"
allowReload, allowExit = False, False
def poop():
sleep(3)
print("poop")
def countdown(count):
autoexecB.config(text = count)
if count == 2:
autoexecB.config(bg = "orange")
if count == 1:
autoexecB.config(bg = "yellow")
if count == 0:
autoexecB.config(text = "Running...", bg = "#44ff00")
if count > -1:
root.after(1000, countdown, count-1)
else:
for i in range(1, 3):
sleep(1)
press("'")
sleep(0.5)
type("exec Patch.txt")
press("enter")
press("esc")
press("enter")
sleep(4)
press("esc")
pressMult("down", 4)
press("enter")
press("up")
press("enter")
sleep(1)
press("'")
sleep(0.5)
type("exec STV.txt")
press("enter")
press("esc")
autoexecB.config(text = "Auto Exec", bg = "red")
def type(text):
typewrite(text)
def pressMult(key, amount):
for i in range(1, amount+1):
press(key)
def autoexec():
countdown(3)
def info():
msgbox("Auto Exec: Runs Mods automaticly\nAuto Exit: Exits the game automaticly to the main menu using INSERT\nAuto Reload: Automaticly reloads your gun using LEFT SHIFT")
def exit():
global count
count = 1
press("esc")
pressMult("down", 4)
press("enter")
press("up")
press("enter")
sleep(2.1)
press("enter")
if choose == "FARM at Hero's Pass":
sleep(3)
keyDown("w")
sleep(0.7)
keyUp("w")
keyDown("d")
sleep(1)
keyUp("d")
keyDown("ctrl")
sleep(0.5)
press("e")
keyUp("ctrl")
count += 1
def reloadslot():
global allowReload, slot
while True:
if allowReload == True:
break
slot = str(integerbox("Enter in the weapon's slot to be reloaded"))
if slot not in ("1","2","3","4"):
msgbox("A weapon can only be in slot 1, 2, 3 or 4")
else:
break
if allowReload == True:
allowReload = False
else:
allowReload = True
def on_press(event):
print(event.keysym)
if event.keysym == "Insert" and allowExit == True:
print("exit")
exit()
if event.keysym == "Shift_L" and allowReload == True:
print("running reload")
press(";")
press("e")
press(slot)
print("done")
root = Tk()
root.bind('<KeyPress>', on_press)
root.geometry("378x134")
root.config(bg = "blue")
autoexecB = Button(text = "Auto Exec", bg = "red", font = ("calibri","13"), height = 3, width = 13, command = lambda: autoexec())
autoexitB = Button(text = "Auto Exit", bg = "red", font = ("calibri","13"), height = 3, width = 13)
autoreloadB = Button(text = "Auto Reload", bg = "red", font = ("calibri","13"), height = 3, width = 13, command = lambda: reloadslot())
infoB = Button(text = "INFO", bg = "blue", width = 26, height = 3, command = lambda: info())
exitB = Button(text = "EXIT", bg = "blue", width = 25, height = 3, command = lambda: root.destroy())
autoexecB.place(x = 0, y = 0)
autoexitB.place(x = 126, y = 0)
autoreloadB.place(x = 252, y = 0)
infoB.place(x = 0, y = 78)
exitB.place(x = 193, y = 78)
root.mainloop()
root.mainloop()
I need a way for tkinter to detect my key presses while not selected
You can't use tkinter for that. It can only detect keyboard events when it has the keyboard focus. You will have to use some platform-specific library to do what you want.

Trying to call a range of instances from a class using for loops

Firstly, sorry this is such a big code dump. I just wanted to make sure all the information was present.
So basically the whole goal is to make a working version of battleship. I will achieve this by making a large grid of buttons for both the players play area (where their ships are placed). Currently I am simply trying to set up some way of disabling one grid during one player's turn (so they can't attack their own ships) and the other for the other player. However, I am struggling to call the buttons by their name using the loop function I have below (named enableAndDisableFunc)
This is all simply just set up for the UI
from tkinter import *
from tkinter import messagebox
base = Tk()
# **********************************
# UI SETUP *************************
# **********************************
# Base ****************************
baseTitle = Frame(base)
baseTitle.config(background="orange", height=20)
baseTitle.pack(side=TOP, fill=X)
baseTop = Frame(base)
baseTop.config(background="blue")
baseTop.pack(side=TOP, fill="both")
baseBottom = Frame(base)
baseBottom.config(background="black", height=40)
baseBottom.pack(side=BOTTOM, fill="both")
# root ********************************
rootPL1 = Frame(baseTop)
rootPL1.config(background="red")
rootPL1.pack(side=LEFT)
emptySpace = Frame(baseTop)
emptySpace.config(background="grey", width=30)
emptySpace.pack(side=LEFT)
rootPL2 = Frame(baseTop)
rootPL2.config(background="red")
rootPL2.pack(side=LEFT)
# Sect(ion) *******************************************
SectPL1 = Frame(rootPL1)
SectPL1.config(background="green", height=30)
SectPL1.pack(side=TOP, fill=X)
SectGridPL1 = Frame(rootPL1)
SectGridPL1.config(background="blue", height=400, width=150)
SectGridPL1.pack(side=BOTTOM)
SectPL2 = Frame(rootPL2)
SectPL2.config(background="green", height=30)
SectPL2.pack(side=TOP, fill=X)
SectGridPL2 = Frame(rootPL2)
SectGridPL2.config(background="blue", height=400, width=150)
SectGridPL2.pack(side=BOTTOM)
# Pl(ayer) ***********************************
PL1ButGrid = Frame(SectGridPL1)
PL1ButGrid.grid(row=8, column=8)
PL1ShipHitCountTagLabel = Label(SectPL1, text="Ship Hit Count", anchor=E, padx=3)
PL1ShipHitCountTagLabel.pack(side=LEFT)
PL1ShipHitCount = 0
PL1ShipHitCountLabel = Label(SectPL1, text=str(PL1ShipHitCount), anchor=E, padx=3)
PL1ShipHitCountLabel.pack(side=LEFT)
# *********************************************
PL2ButGrid = Frame(SectGridPL2)
PL2ButGrid.grid(row=8, column=8)
PL2ShipHitCountTagLabel = Label(SectPL2, text="Ship Hit Count", anchor=E, padx=3)
PL2ShipHitCountTagLabel.pack(side=LEFT)
PL2ShipHitCount = 0
PL2ShipHitCountLabel = Label(SectPL2, text=str(PL2ShipHitCount), anchor=E, padx=3)
PL2ShipHitCountLabel.pack(side=LEFT)
# Switch Turn Button ********************************
SwitchButton = Button(baseBottom, width=20, height=2, text="Switch Turn to Other Player",
command=lambda: enableAndDisableFunc())
SwitchButton.pack(side=TOP)
# Title *********************************************
titleLabel = Label(baseTitle, text="Battleship!", font="Arial", fg="black", bg="orange", padx=10, pady=10)
titleLabel.pack(side=TOP)
This part loads the class that creates all the buttons that the player's grids are made from
# classes and methods
def colourChanger(self):
self.button.config(bg="red")
global PL2ShipHitCount
global PL1ShipHitCount
PL1ShipHitCount += 1
PL2ShipHitCount += 1
PL1ShipHitCountLabel.config(text=PL1ShipHitCount)
PL2ShipHitCountLabel.config(text=PL2ShipHitCount)
print("Working for cell " + str(self) + "!")
And finally this section is where each button's values are generated (using multiple loops to achieve all the details about each row, column, id and label)
grid1namelist = []
grid2namelist = []
class GameBoardButtons:
def __init__(self, name, buttonText, buttonRow, buttonColumn, buttonState, frame):
self.name = name
self.button = Button(frame, text=buttonText, fg="grey", state=buttonState, command=lambda: colourChanger(self))
self.button.grid(row=buttonRow, column=buttonColumn)
def enable(self):
self.button.config(state=NORMAL)
def disable(self):
self.button.config(state=DISABLED)
def buttonGen(givenFrame, iState):
for i in range(1, 65):
if 1 <= int(i) <= 8:
iText = "A" + str(i)
rowNum = 1
colNum = i
if 9 <= int(i) <= 16:
iText = "B" + str((i % 8) + 1)
rowNum = 2
colNum = (i % 8) + 1
if 17 <= int(i) <= 24:
iText = "C" + str((i % 8) + 1)
rowNum = 3
colNum = (i % 8) + 1
if 25 <= int(i) <= 32:
iText = "D" + str((i % 8) + 1)
rowNum = 4
colNum = ((i % 8) + 1)
if 33 <= int(i) <= 40:
iText = "E" + str((i % 8) + 1)
rowNum = 5
colNum = ((i % 8) + 1)
if 41 <= int(i) <= 48:
iText = "F" + str((i % 8) + 1)
rowNum = 6
colNum = ((i % 8) + 1)
if 49 <= int(i) <= 56:
iText = "G" + str((i % 8) + 1)
rowNum = 7
colNum = ((i % 8) + 1)
if 57 <= int(i) <= 64:
iText = "H" + str((i % 8) + 1)
rowNum = 8
colNum = ((i % 8) + 1)
if str(givenFrame) == ".!frame2.!frame.!frame2":
iFrameValue = str(1)
iName = "grid" + iFrameValue + "but" + iText
grid1namelist.append(iName)
else:
iFrameValue = str(2)
iName = "grid" + iFrameValue + "but" + iText
grid2namelist.append(iName)
iName = GameBoardButtons(iName, iText, rowNum, colNum, iState, givenFrame)
print(iName)
# Main setup for grids
buttonGen(SectGridPL1, iState=NORMAL)
buttonGen(SectGridPL2, iState=DISABLED)
def enableAndDisableFunc():
switchTurnPopUp(1)
switchTurnPopUp(2)
messagebox.showinfo("Turn Switcher", "It is now player " + str(2) + "'s turn")
def switchTurnPopUp(gridNum):
if gridNum == 1:
for i in grid1namelist:
i.button.enable()
if gridNum == 2:
for i in grid2namelist:
i.button.disable()
base.mainloop()
How do I disable (set the state=DISABLED) one whole set of buttons on one grid (all of grid 1 or, in other words, all the possibilities of "grid1" + "A" to "H" + "1" to "8"). This will allow the player only to choose a grid coord on their opponents grid using the tkinter disable function.
Many thanks, sorry for the large code dump and the messy ordering of code, all and any help greatly appreciated!
Question: Trying to call a range of instances
If you use gridxnamelist only for toogle purpose, its obsolet.
Change the following:
Pass the Grid Frame to switchTurnPopUp
def enableAndDisableFunc():
switchTurnPopUp(SectGridPL1)
switchTurnPopUp(SectGridPL2)
Replace your switchTurnPopUp with this:
def switchTurnPopUp(grid):
# Unkown 'state' for this 'grid'
state = None
# Get all children 'id' from this 'grid'
children = grid.children
# Loop the children 'id'
for c in children:
# Get the 'widget' of this 'id'
widget = children[c]
# Process only 'Button' widgets
if isinstance(widget, (Button)):
# Check the 'state' only once
if state is None:
# Get the current 'state' of the first 'Button'
state = widget.cget('state')
# Toogle the state
if state == 'normal':
state = 'disabled'
elif state == 'disabled':
state = 'normal'
# Set the toogled 'state'
widget.configure(state=state)
Tested with Python: 3.5 - TkVersion: 8.6

Categories