Search code examples
pythontkinterlabel

Question: _tkinter.TclError: can't invoke "label" command: application has been destroyed


I made a game about guess numbers. After repeated attempts, I still couldn't change a problem. The code is as follows:

#

#
import tkinter
import random
import pygame

#
pygame.init()

#
root = tkinter.Tk()

#
bg = tkinter.Canvas(root, width=1000, height=750)
bg.pack()
bg_p = tkinter.PhotoImage(file="bg.png")
bg.create_image(500, 375, image=bg_p)

#
bgm = pygame.mixer.Sound("bgm.mp3")
bgm.play(-1)

#
low = 0
high = 101

#
random.randrange(low, high)

#
def onclick():

    #
    global low, high

    #
    guess = int(input("Guess a number: "))

    #
    if guess == answer:

        #
        bg = tkinter.Canvas(root, width=1000, height=750)
        bg.pack()
        bg_p = tkinter.PhotoImage(file="bingo.png")
        bg.create_image(500, 375, image=bg_p)

        #
        low = guess
        high = guess

        #
        bingo = pygame.mixer.Sound("bingo.mp3")
        bingo.play(0)

    #
    elif guess > answer:

        #
        bg = tkinter.Canvas(root, width=1000, height=750)
        bg.pack()
        bg_p = tkinter.PhotoImage(file="too_high.png")
        bg.create_image(500, 375, image=bg_p)

        #
        high = guess

        #
        lol = pygame.mixer.Sound("lol.mp3")
        lol.play(0)

    #
    else:

        #
        bg = tkinter.Canvas(root, width=1000, height=750)
        bg.pack()
        bg_p = tkinter.PhotoImage(file="too_low.png")
        bg.create_image(500, 375, image=bg_p)

        #
        low = guess

        #
        lol = pygame.mixer.Sound("lol.mp3")
        lol.play(0)

#
while True:

    #
    for event in pygame.event.get():

        #
        if event.type == pygame.QUIT:

            #
            pygame.quit()
            exit()

        #
        if event.type == pygame.MOUSEBUTTONDOWN:

            #
            onclick()

    #
    try:

        #
        l1.destroy()
        l2.destroy()
    
    #
    except:

        #
        pass

    #
    finally:

        #
        l1 = tkinter.Label(root, text=str(low), font=("Arial", 60), bg="white")
        l1.place(x=280, y=570)
        l2 = tkinter.Label(root, text=str(high), font=("Arial", 60), bg="white")
        l2.place(x=630, y=570)

    #
    root.mainloop()  

After executing the program, _tkinter.TclError: can't invoke "label" command: application has been destroyed is returned. No matter where I put mainloop() it doesn't work. Why?

I put mainloop() on everywhere, but it still doesn't work.


Solution

  • You mix pygame and tkinter but it seems one big mess.

    You try to use pygame.event but you don't create pygame window - so system will NOT send any events to pygame. System sends pygame events only to pygame window - and this window has to be visible (active as top window on screen).

    It will NOT generate pygame.MOUSEBUTTONDOWN when you click in tkinter window because it generates pygame event only when you click inside pygame window.

    So it can't get pygame.QUIT and pygame.MOUSEBUTTONDOWN. And frankly, you don't need pygame loop if you use pygame only to play sound.

    If you create tkinter window then you should use tkinter events. And it means to use tkinter.Button(..., command=on_click) to execute function when you click button. Or you have to use root.bind('<Button-1>', on_click) to execute function when you click in any place in tkinter window.


    Here is my version - but I didn't test how works pygame.mixer because at this moment I problem with audio on my Linux.

    I create all widgets only once - and later I only replace text in Label, and replace image on Canvas

    I also uses tkinter.Entry instead of input() because it looks stupid when GUI uses terminal instead of widgets.

    I don't use place() but pack() (or eventually grid()) and I don't have to calculate manually positions. I also use tkinter.Frame to put two labels in one row.

    I add tkinter.Button to check number but you can also press ENTER in tkinter.Entry to check number.

    import tkinter
    import random
    import pygame
    
    # --- functions ---
    
    def on_click(event=None): # default value for event because `command=` doesn't send event
        global low, high
    
        guess = int(entry.get())  # entry needs `.get()` to get value
    
        if guess == answer:
            bg.itemconfig(image_id, image=bg_p_bingo)  # replace image in PhotoImage
    
            low = guess
            high = guess        
            label_low['text'] = str(low)    # replace text in Label
            label_high['text'] = str(high)  # replace text in Label
            
            #bingo = pygame.mixer.Sound("bingo.mp3")
            #bingo.play(0)
    
        elif guess > answer:
            bg.itemconfig(image_id, image=bg_p_high)  # replace image in PhotoImage
    
            high = guess
            label_high['text'] = str(high)  # replace text in Label
    
            #lol = pygame.mixer.Sound("lol.mp3")
            #lol.play(0)
        else:
            bg.itemconfig(image_id, image=bg_p_low)    # replace image in PhotoImage
    
            low = guess
            label_low['text'] = str(low)    # replace text in Label
    
            #lol = pygame.mixer.Sound("lol.mp3")
            #lol.play(0)
        
    # --- main ---
    
    low = 0
    high = 101
    
    # --- 
    
    answer = random.randrange(low, high)
    
    #pygame.mixer.init()
    
    root = tkinter.Tk()
    
    label = tkinter.Label(root, text="Guess a number: ")
    label.pack(padx=5, pady=5)
    
    entry = tkinter.Entry(root)
    entry.pack(padx=5, pady=5)
    
    button = tkinter.Button(root, text='CHECK', command=on_click)
    button.pack(padx=5, pady=5)
    
    entry.bind('<Return>', on_click)  # run function on pressing ENTER - it sends `event` to function
    
    bg = tkinter.Canvas(root, width=300, height=300)
    bg.pack()
    
    bg_p = tkinter.PhotoImage(file="bg.png")
    bg_p_bingo = tkinter.PhotoImage(file="bingo.png")
    bg_p_low   = tkinter.PhotoImage(file="too_low.png")
    bg_p_high  = tkinter.PhotoImage(file="too_high.png")
    
    image_id = bg.create_image(150, 150, image=bg_p, anchor='center')
    
    frame = tkinter.Frame(root)  # create Frame to put widgets in one row
    frame.pack(fill='both', expand=True)
    
    label_low = tkinter.Label(frame, text=f'{low}',  font=("Arial", 30), bg="white", width=3)
    label_low.pack(side='left', padx=5, pady=5, fill='x', expand=True)
    
    label_high = tkinter.Label(frame, text=f'{high}', font=("Arial", 30), bg="white", width=3)
    label_high.pack(side='right', padx=5, pady=5, fill='x', expand=True)
    
    #bgm = pygame.mixer.Sound("bgm.mp3")
    #bgm.play(-1)
    
    root.mainloop()  
    
    pygame.mixer.quit()
    

    enter image description here


    I put code with images on GitHub furas/python-examples/tkinter/game-guess-number