Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Grid_Destory() Not Working
#1
Hi All,

I have the below code. Basically what the program does is, the computer chooses a number and the users role is to try and guess the number, they have 3 tries, if they don't do it within the three the game ends. I have it so that 3 heart pictures appear and everytime you get one wrong a heart disappears. Except when youlose your last life, the final heart does not disappear, why?

import tkinter as tk
from tkinter import *
import random
import time
from PIL import ImageTk,Image


window_4 = tk.Tk()

window_4.title("Game")

random_num = random.randint(1, 9)
print(random_num)
player_choice = 0
global lives
lives = 3
print ("You are starting with " + str(lives) + " lives")

def click_1():

    global lives
    player_choice=1
    if player_choice == random_num:
        print("Correct Guess - Well Done")
        time.sleep(1)
        print ("You had " + str(lives) + " lives left")
        time.sleep(1)
        print("The game will now terminate")
        time.sleep(3)
        window_4.destroy()
    else:
        lives = lives - 1
        if lives == 2:
            heart_3.grid_forget()
            print ("You have " + str(lives) + " lives left")
        elif lives == 1:
            heart_2 .grid_forget()
            print ("You have " + str(lives) + " lives left")
        elif lives == 0:
            heart_1.grid_forget()
            print ("GAME OVER, you have run out of your 3 lives! - Thanks for playing")
            time.sleep(3)
            window_4.destroy()

def click_2():

    global lives
    player_choice=2
    if player_choice == random_num:
        print("Correct Guess - Well Done")
        time.sleep(1)
        print ("You had " + str(lives) + " lives left")
        time.sleep(1)
        print("The game will now terminate")
        time.sleep(3)
        window_4.destroy()
    else:
        lives = lives - 1
        if lives == 2:
            heart_3.grid_forget()
            print ("You have " + str(lives) + " lives left")
        elif lives == 1:
            heart_2 .grid_forget()
            print ("You have " + str(lives) + " lives left")
        elif lives == 0:
            heart_1.grid_forget()
            print ("GAME OVER, you have run out of your 3 lives! - Thanks for playing")
            time.sleep(3)
            window_4.destroy()

def click_3():

    global lives
    player_choice=3
    if player_choice == random_num:
        print("Correct Guess - Well Done")
        time.sleep(1)
        print ("You had " + str(lives) + " lives left")
        time.sleep(1)
        print("The game will now terminate")
        time.sleep(3)
        window_4.destroy()
    else:
        lives = lives - 1
        if lives == 2:
            heart_3.grid_forget()
            print ("You have " + str(lives) + " lives left")
        elif lives == 1:
            heart_2 .grid_forget()
            print ("You have " + str(lives) + " lives left")
        elif lives == 0:
            heart_1.grid_forget()
            print ("GAME OVER, you have run out of your 3 lives! - Thanks for playing")
            time.sleep(3)
            window_4.destroy()

def click_4():

    global lives
    player_choice=4
    if player_choice == random_num:
        print("Correct Guess - Well Done")
        time.sleep(1)
        print ("You had " + str(lives) + " lives left")
        time.sleep(1)
        print("The game will now terminate")
        time.sleep(3)
        window_4.destroy()
    else:
        lives = lives - 1
        if lives == 2:
            heart_3.grid_forget()
            print ("You have " + str(lives) + " lives left")
        elif lives == 1:
            heart_2 .grid_forget()
            print ("You have " + str(lives) + " lives left")
        elif lives == 0:
            heart_1.grid_forget()
            print ("GAME OVER, you have run out of your 3 lives! - Thanks for playing")
            time.sleep(3)
            window_4.destroy()

def click_5():

    global lives
    player_choice=5
    if player_choice == random_num:
        print("Correct Guess - Well Done")
        time.sleep(1)
        print ("You had " + str(lives) + " lives left")
        time.sleep(1)
        print("The game will now terminate")
        time.sleep(3)
        window_4.destroy()
    else:
        lives = lives - 1
        if lives == 2:
            heart_3.grid_forget()
            print ("You have " + str(lives) + " lives left")
        elif lives == 1:
            heart_2 .grid_forget()
            print ("You have " + str(lives) + " lives left")
        elif lives == 0:
            heart_1.grid_forget()
            print ("GAME OVER, you have run out of your 3 lives! - Thanks for playing")
            time.sleep(3)
            window_4.destroy()
            
def click_6():

    global lives
    player_choice=6
    if player_choice == random_num:
        print("Correct Guess - Well Done")
        time.sleep(1)
        print ("You had " + str(lives) + " lives left")
        time.sleep(1)
        print("The game will now terminate")
        time.sleep(3)
        window_4.destroy()
    else:
        lives = lives - 1
        if lives == 2:
            heart_3.grid_forget()
            print ("You have " + str(lives) + " lives left")
        elif lives == 1:
            heart_2 .grid_forget()
            print ("You have " + str(lives) + " lives left")
        elif lives == 0:
            heart_1.grid_forget()
            print ("GAME OVER, you have run out of your 3 lives! - Thanks for playing")
            time.sleep(3)
            window_4.destroy()

def click_7():

    global lives
    player_choice=7
    if player_choice == random_num:
        print("Correct Guess - Well Done")
        time.sleep(1)
        print ("You had " + str(lives) + " lives left")
        time.sleep(1)
        print("The game will now terminate")
        time.sleep(3)
        window_4.destroy()
    else:
        lives = lives - 1
        if lives == 2:
            heart_3.grid_forget()
            print ("You have " + str(lives) + " lives left")
        elif lives == 1:
            heart_2 .grid_forget()
            print ("You have " + str(lives) + " lives left")
        elif lives == 0:
            heart_1.grid_forget()
            print ("GAME OVER, you have run out of your 3 lives! - Thanks for playing")
            time.sleep(3)
            window_4.destroy()

def click_8():

    global lives
    player_choice=8
    if player_choice == random_num:
        print("Correct Guess - Well Done")
        time.sleep(1)
        print ("You had " + str(lives) + " lives left")
        time.sleep(1)
        print("The game will now terminate")
        time.sleep(3)
        window_4.destroy()
    else:
        lives = lives - 1
        if lives == 2:
            heart_3.grid_forget()
            print ("You have " + str(lives) + " lives left")
        elif lives == 1:
            heart_2 .grid_forget()
            print ("You have " + str(lives) + " lives left")
        elif lives == 0:
            heart_1.grid_forget()
            print ("GAME OVER, you have run out of your 3 lives! - Thanks for playing")
            time.sleep(3)
            window_4.destroy()

def click_9():

    global lives
    player_choice=9
    if player_choice == random_num:
        print("Correct Guess - Well Done")
        time.sleep(1)
        print ("You had " + str(lives) + " lives left")
        time.sleep(1)
        print("The game will now terminate")
        time.sleep(3)
        window_4.destroy()
    else:
        lives = lives - 1
        if lives == 2:
            heart_3.grid_forget()
            print ("You have " + str(lives) + " lives left")
        elif lives == 1:
            heart_2 .grid_forget()
            print ("You have " + str(lives) + " lives left")
        elif lives == 0:
            heart_1.grid_forget()
            print ("GAME OVER, you have run out of your 3 lives! - Thanks for playing")
            time.sleep(3)
            window_4.destroy()
    
label_instructions = tk.Label(text="The computer has chosen a number, your job is to correctly guess the number. You only have 3 tries, Good Luck!")
label_instructions.grid(row=0, column = 2)

game_button_1 = tk.Button(text="1", height=1, width=2, command=click_1)
game_button_1.grid(row=1, column = 1)

game_button_2 = tk.Button(text="2", height=1, width=2, command=click_2)
game_button_2.grid(row=1, column = 3)

game_button_3 = tk.Button(text="3", height=1, width=2, command=click_3)
game_button_3.grid(row=1, column = 5)

clear_label_1=tk.Label(text=" ")
clear_label_1.grid(row=2, column=1)

game_button_4 = tk.Button(text="4", height=1, width=2, command=click_4)
game_button_4.grid(row=3, column = 1)

game_button_5 = tk.Button(text="5", height=1, width=2, command=click_5)
game_button_5.grid(row=3, column = 3)

game_button_6 = tk.Button(text="6", height=1, width=2, command=click_6)
game_button_6.grid(row=3, column = 5)

clear_label_2=tk.Label(text=" ")
clear_label_2.grid(row=4, column=1)

game_button_7 = tk.Button(text="7", height=1, width=2, command=click_7)
game_button_7.grid(row=5, column = 1)

game_button_8 = tk.Button(text="8", height=1, width=2, command=click_8)
game_button_8.grid(row=5, column = 3)

game_button_9 = tk.Button(text="9", height=1, width=2, command=click_9)
game_button_9.grid(row=5, column = 5)


heart_1 = Canvas(window_4, width = 300, height = 300)  
heart_1.grid(row=6, column=2)
img_1 = ImageTk.PhotoImage(Image.open("D:\Work\School\ComputerScience\Code\Other\pic_1.png"))  
heart_1.create_image(20, 20, anchor=NW, image=img_1) 

heart_2 = Canvas(window_4, width = 300, height = 300)  
heart_2.grid(row=6, column=3)
img_2 = ImageTk.PhotoImage(Image.open("D:\Work\School\ComputerScience\Code\Other\pic_2.png"))  
heart_2.create_image(20, 20, anchor=NW, image=img_2)

heart_3 = Canvas(window_4, width = 300, height = 300)  
heart_3.grid(row=6, column=4)
img_3 = ImageTk.PhotoImage(Image.open("D:\Work\School\ComputerScience\Code\Other\pic_3.png"))  
heart_3.create_image(20, 20, anchor=NW, image=img_3) 

window_4.mainloop()
Reply
#2
Each time in your callback you mark the window to be removed from the grid. But nothing happens until your function exits and TK redraws the screen via tasks in mainloop.

On your last time, your function never exits, so TK never redraws the screen. Try adding a call to window4.update() to redraw the screen.
Reply
#3
(Mar-03-2021, 09:18 PM)bowlofred Wrote: Each time in your callback you mark the window to be removed from the grid. But nothing happens until your function exits and TK redraws the screen via tasks in mainloop.

On your last time, your function never exits, so TK never redraws the screen. Try adding a call to window4.update() to redraw the screen.

Before every line that says heart_1.destroy I have put window_4.update() and the same issue still occurs.
Reply
#4
You should not have sleep commands in a GUI application. They prevent mainloop from running and stop window updates.

Instead of this:
print("The game will now terminate")
time.sleep(3)
window_4.destroy()
try this
print("The game will now terminate")
root.after(3000, quit)
You have 9 nearly identical functions. Can you think of a way to make 1 function work for all 9 buttons?
Reply
#5
(Mar-04-2021, 02:05 PM)deanhystad Wrote: You should not have sleep commands in a GUI application. They prevent mainloop from running and stop window updates.

Instead of this:
print("The game will now terminate")
time.sleep(3)
window_4.destroy()
try this
print("The game will now terminate")
root.after(3000, quit)
You have 9 nearly identical functions. Can you think of a way to make 1 function work for all 9 buttons?

Thanks for the help, that has sort of resolved the issue. Now instead when I press the final button it now just closes the program with no pause. SO when there is one life left and I get it incorrect, the window just closes instantly.

When coding it I did think of a way to try and combine them but couldn't think of any of them because they are all for separate buttons and all set a variable to a different number depending on what button is clicked. If there is a way for it to know hcih button has been pressed then I might be able to.
Reply
#6
I tried "quit" and it complained that "Quitter" object had no attribute '__name__'. You program wasn't exiting after a few seconds, it was crashing. I did find that 'root.destroy' worked fine.

It is easy to specify arguments in a callback function. This example uses lambda expressions. There is also the "partial" function in the functools library. Both are very useful and you should know how to use them if you want to write GUI applications.

This is your code without duplicate code. Only one "click" function required to handle all 9 buttons. Your buttons are nearly identical too, so I make them in a loop.
import sys
import pathlib
import random
import tkinter as tk

IMAGE_DIR = pathlib.Path(sys.path[0])
font = ('Helvetica', 32, 'bold')

def click(player_choice):
    if player_choice == random_num:
        message['text'] = "Correct Guess - Well Done"
        root.after(5000, root.destroy)
    else:
        hearts.pop(-1).grid_forget()
        
        lives = len(hearts)
        if lives > 0:
            message['text'] = f"You have {lives} lives left"
        else:
            message['text'] = "GAME OVER! - Thanks for playing"
            root.after(5000, root.destroy)

root = tk.Tk()
root.title("Game")
random_num = random.randint(1, 9)

message = tk.Label(text=f"Try to guess the number {random_num}")
message.grid(row=0, column=0, columnspan=4, padx=10, pady=10)

for i in range(9):
    button = tk.Button(root, text=str(i+1), font=font, command=lambda number=i+1: click(number))
    button.grid(row=i // 3 + 1, column=i % 3, padx=10, pady=10)
 
heart_images = [
    tk.PhotoImage(file=IMAGE_DIR/'ttt_x.png'),
    tk.PhotoImage(file=IMAGE_DIR/'ttt_o.png'),
    tk.PhotoImage(file=IMAGE_DIR/'ttt_x.png')]

hearts = []
for i, image in enumerate(heart_images):
    heart = tk.Label(root, image=image)
    heart.grid(row=i+1, column=4)
    hearts.append(heart)
 
root.mainloop()
I used some image files I had laying around. When referencing files you should treat the filename and the file path independently. Otherwise you have no hope of ever running your programs on another computer or just surviving a minor directory reorganization. In this example I'm using a trick to set IMAGE_DIR to the folder that contains the current python module (program we are running).
Reply
#7
(Mar-04-2021, 03:21 PM)deanhystad Wrote: I tried "quit" and it complained that "Quitter" object had no attribute '__name__'. You program wasn't exiting after a few seconds, it was crashing. I did find that 'root.destroy' worked fine.

It is easy to specify arguments in a callback function. This example uses lambda expressions. There is also the "partial" function in the functools library. Both are very useful and you should know how to use them if you want to write GUI applications.

This is your code without duplicate code. Only one "click" function required to handle all 9 buttons. Your buttons are nearly identical too, so I make them in a loop.
import sys
import pathlib
import random
import tkinter as tk

IMAGE_DIR = pathlib.Path(sys.path[0])
font = ('Helvetica', 32, 'bold')

def click(player_choice):
    if player_choice == random_num:
        message['text'] = "Correct Guess - Well Done"
        root.after(5000, root.destroy)
    else:
        hearts.pop(-1).grid_forget()
        
        lives = len(hearts)
        if lives > 0:
            message['text'] = f"You have {lives} lives left"
        else:
            message['text'] = "GAME OVER! - Thanks for playing"
            root.after(5000, root.destroy)

root = tk.Tk()
root.title("Game")
random_num = random.randint(1, 9)

message = tk.Label(text=f"Try to guess the number {random_num}")
message.grid(row=0, column=0, columnspan=4, padx=10, pady=10)

for i in range(9):
    button = tk.Button(root, text=str(i+1), font=font, command=lambda number=i+1: click(number))
    button.grid(row=i // 3 + 1, column=i % 3, padx=10, pady=10)
 
heart_images = [
    tk.PhotoImage(file=IMAGE_DIR/'ttt_x.png'),
    tk.PhotoImage(file=IMAGE_DIR/'ttt_o.png'),
    tk.PhotoImage(file=IMAGE_DIR/'ttt_x.png')]

hearts = []
for i, image in enumerate(heart_images):
    heart = tk.Label(root, image=image)
    heart.grid(row=i+1, column=4)
    hearts.append(heart)
 
root.mainloop()
I used some image files I had laying around. When referencing files you should treat the filename and the file path independently. Otherwise you have no hope of ever running your programs on another computer or just surviving a minor directory reorganization. In this example I'm using a trick to set IMAGE_DIR to the folder that contains the current python module (program we are running).


thanks!
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020