getting rid of being able to press buttons after game is won

88 Views Asked by At
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import font as tkFont

#initializes a 3x3 list named 'board' with empty strings as elements
board = [['' for _ in range(3)] for _ in range(3)]

game_active = True

#defines the function to switch to the game screen
def play_game():

    #creates a top level window (appears above all other windows when pressed)
    game_window = tk.Toplevel(win)

    #sets the name of the new window to "Tic-Tac-Toe Game"
    game_window.title("Tic-Tac-Toe Game")

    #sets the size of the game window when opened to 600x600
    game_window.geometry("600x600")

    #doesn't allow the main game window to be resized
    game_window.resizable(False, False)


    #creates a canvas to draw lines
    canvas = tk.Canvas(game_window, width=600, height=600)
    canvas.pack()

    #creates horizontal lines
    for i in range(1, 3):
        canvas.create_line(0, i * 200, 600, i * 200, fill="black")

    #creates vertical lines
    for i in range(1, 3):
        canvas.create_line(i * 200, 0, i * 200, 600, fill="black")


    #creates the buttons in the game window, uses configure to later destroy the button after it has been pressed
    for i in range(3):
        for j in range(3):
            x1 = i * 200
            y1 = j * 200
            button_tile = ttk.Button(game_window)
            button_tile.place(x=x1, y=y1, width=200, height=200)
            button_tile.configure(command=lambda row=i, col=j, btn=button_tile, parent=game_window: button_click(row, col, btn, parent))


#sets first turn of player to 'O' and next is 'X', so on and on
o_Turn = True



#defines the function what happens when one of the 9 buttons are pressed
def button_click(row, col, button, parent):

    #sets a global variable o_Turn so that it can be accessed anywhere - removing localerror
    global o_Turn

    game_active

    if not game_active:
        return

    Sign = 'X' if not o_Turn else 'O'
    o_Turn = not o_Turn

    ttk.Label(parent, text=Sign, font=('Times', 60, tkFont.BOLD)).place(x=(row*200)+75, y=(col*200)+55)

    board[row][col] = Sign
    button.destroy()

    if check_winner(Sign):
        game_active = False
        winner_window = tk.Toplevel(parent)
        winner_window.title("Winner")
        ttk.Label(winner_window, text=f"Player {Sign} wins!", font=('Times New Roman', 20)).pack(padx=20, pady=20)
        #TO BE ADDED WHILE WAITING
        for widget in parent.winfo_children():
            if isinstance(widget, ttk.Button):
                widget.destroy()

def check_winner(player):
    #check rows
    for i in range(3):
        if board[i][0] == board[i][1] == board[i][2] == player:
            return True
        

    #check columns
    for j in range(3):
        if board[0][j] == board[1][j] == board[2][j] == player:
            return True

    #check diagonals
    if board[0][0] == board[1][1] == board[2][2] == player:
        return True
    if board[0][2] == board[1][1] == board[2][0] == player:
        return True

    return False


#defines the function to open the user guide

def open_user_guide():
    #creates a top level window (appears above all other windows when pressed)
    guide_window = tk.Toplevel(win)

    #sets the name of the new window to "Tic-Tac-Toe Game"
    guide_window.title("User Guide")

    #sets the size of the game window when opened to 600x700
    guide_window.geometry("800x300")

    #doesn't allow the user guide window to be resized
    guide_window.resizable(False, False)

    #creates a frame to contain the labels
    frame = ttk.Frame(guide_window)
    frame.pack(expand=True, fill='both', padx=20, pady=20)

    #creates a new label with the text "How to play:" on the top of the window
    guide_label = ttk.Label(frame, text="User Guide", font=('Times New Roman', 20, 'bold'))
    guide_label.pack(pady=(0, 10), anchor='center')  #centres the label horizontally

    #creates a new text section 
    instructions_label = ttk.Label(frame, text="""
    Hi there! And welcome to my Tic Tac Toe game, made with tkinter.
    
    Here's how to play Tic-Tac-Toe! As you can see, the game is played on a grid that is 3 squares by 3 squares.

    1. The player that starts is X, and the other person is O. You and another player take turns placing X or O into a square.
    2. The first player to get all 3 marks in a row (horizontally, vertically or diagonally) wins the game.
    3. When all the squares are filled and no one has won, the game ends in a tie. AND THAT'S IT! Have fun playing!

    And press reset to play again!""",
    font=('Times New Roman', 12))
    instructions_label.pack(anchor='w')


win = tk.Tk()

#sets window size to 1000 x 600
win.geometry("1000x600")


#sets background image to a tictac
bg = tk.PhotoImage(file = "tictac.png")

#shows image using a label
backgroundimage = tk.Label(win, image=bg) 
backgroundimage.place(x = 0, y = 0)


#sets minimum window resizable size to 500 x 300
win.minsize(500, 300)

#doesn't allow the launcher window to be resized
win.resizable(False, False)

#sets title name of window to Tic-Tac-Toe (name of game)
win.title("Tic-Tac-Toe")

#integrates a new font into tkinter application
tmrn = tkFont.Font(family='Times New Roman', size=15, weight='bold')

#creates a custom style for buttons
style = ttk.Style()
style.configure('tmrn.TButton', font=('Times New Roman', 15, 'bold'), padding=(200, 10))  # Increase left and right padding to make the buttons longer


#creates a label with big black letters at the top of the window
title_label = ttk.Label(win, text="Tic-Tac-Toe Game!", font=('Times New Roman', 40, 'bold'), foreground='black')
title_label.pack(side=tk.TOP, pady=20)  # Add some padding at the top


#creates a button called 'Play' - takes the user to the actual playable game
play_button = ttk.Button(win, text="Play", command=play_game, style='tmrn.TButton')
play_button.place(relx=0.5, rely=0.6, anchor=tk.CENTER)  #positions the button just below the center

#creates a button called 'User Guide' - supposed to take the user to another window with how to play
guide_button = ttk.Button(win, text="User Guide", command=open_user_guide, style='tmrn.TButton')
guide_button.place(relx=0.5, rely=0.7, anchor=tk.CENTER)  #positions the button just below the "Play" button with some padding


win.mainloop()

This is my code so far, when I run it and play the tic-tac-toe game it works well. However, after I finish a game with a winner and the new window pops up with 'player X' or 'player o' wins, I am still able to press the buttons - I'd like it so that the buttons can't be pressed anymore, but I'm not sure how - i know that the button.destroy() function might work but I dont know how to do it for the remaining few. If I could get some help on this that could be great! Thanks.

2

There are 2 best solutions below

5
Scott On

You can just introduce a global variable, let's say game_active, to track whether the game is ongoing. This will be True when the game starts and should be set to False when someone wins or there is a tie.

I modified the button_click function to check the state of game_active before proceeding with its logic. If game_active is False, the function should return immediately.

Set game_active to False in the check_winner function when there is a winner.

Obviously reset game_active to True every time a new game starts.

Example:

game_active = True

def button_click(row, col, button, parent): global o_Turn, game_active

# Return immediately if the game is no longer active
if not game_active:
    return

Sign = 'X' if not o_Turn else 'O'
o_Turn = not o_Turn

ttk.Label(parent, text=Sign, font=('Times', 60, tkFont.BOLD)).place(x=(row*200)+75, y=(col*200)+55)

board[row][col] = Sign
button.destroy()

if check_winner(Sign):
    game_active = False
    winner_window = tk.Toplevel(parent)
    winner_window.title("Winner")
    ttk.Label(winner_window, text=f"Player {Sign} wins!", font=('Times New Roman', 20)).pack(padx=20, pady=20)
2
acw1668 On

As you said, you can destroy the remaining buttons:

def button_click(row, col, button, parent):
    ...
    if check_winner(Sign):
        # destroy remaining buttons
        for widget in parent.winfo_children():
            if isinstance(widget, ttk.Button):
                widget.destroy()
        ...