Home > Back-end >  This is the weirdest thing i have ever encountered in python (Tkinter module) , any help understandi
This is the weirdest thing i have ever encountered in python (Tkinter module) , any help understandi

Time:07-20

So basically i have a function called click_roll where if you click the roll button it will roll dice and show the result as an image of a dice side, for some reason the program will only work if the function had and error at the end of it! how does this make any sense? (the error_variable at the end of the function doesnt exist so adding 1 to it will cause an error)

from tkinter import *
import random
from PIL import ImageTk,Image
root = Tk()
root.geometry('300x400')
def rolldice():
    number = random.randint(1,6)
    return number
def click_roll():
    number = rolldice()
    if number == 2:
        image1 = ImageTk.PhotoImage(Image.open("dice2.jpg"))
        image1_label = Label(image=image1)
        image1_label.place(relx=0.5, rely=0.3, anchor=CENTER)
    elif number == 3:
        image1 = ImageTk.PhotoImage(Image.open("dice3.jpg"))
        image1_label = Label(image=image1)
        image1_label.place(relx=0.5, rely=0.3, anchor=CENTER)
    elif number == 4:
        image1 = ImageTk.PhotoImage(Image.open("dice4.jpg"))
        image1_label = Label(image=image1)
        image1_label.place(relx=0.5, rely=0.3, anchor=CENTER)
    elif number == 5:
        image1 = ImageTk.PhotoImage(Image.open("dice5.jpg"))
        image1_label = Label(image=image1)
        image1_label.place(relx=0.5, rely=0.3, anchor=CENTER)
    elif number == 6:
        image1 = ImageTk.PhotoImage(Image.open("dice6.jpg"))
        image1_label = Label(image=image1)
        image1_label.place(relx=0.5, rely=0.3, anchor=CENTER)
    elif number == 1:
        image1 = ImageTk.PhotoImage(Image.open("dice1.jpg"))
        image1_label = Label(image=image1)
        image1_label.place(relx=0.5, rely=0.3, anchor=CENTER)
    
    error_variable  =1


image1 = ImageTk.PhotoImage(Image.open("dice1.jpg"))
image1_label = Label(image=image1)

roll_button = Button(root,text='Roll',height=5,width=12,command=click_roll)

roll_button.grid(row=0,column=1)
image1_label.place(relx=0.5, rely=0.3, anchor=CENTER)
roll_button.place(relx=0.5, rely=0.5, anchor=CENTER)

root.mainloop()

CodePudding user response:

The class ImageTk.PhotoImage has an implementation for __del__ that deletes the image from tkinter (not sure what exactly happens here, as i don't use tkinter).

Your click_roll creates an image and drops all references to it (the label for some reason does not keep a reference to the image). Eventually the __del__ method on the image is called and it is removed from the main window.

When you raise an exception inside the click_roll function, the traceback will keep the frame alive and thus the image is not removed from the main window.

To prevent that from happening, you should restructure your code a bit so that the images are stored in a "safe" location.

# store the images in a variable that "survives" the click_roll handler
die_images = {
    1: ImageTk.PhotoImage('dice1.jpg'),
    ...
    6: ImageTk.PhotoImage('dice6.jpg')
}


def click_roll():
    number = rolldice()
    image = die_images[number]  # pick the correct image
    label = Label(image=image)
    ...

If you are familiar with dict-comprehensions you could also simplify the die_images creation:

die_images = {number: ImageTk.PhotoImage(f'dice{number}.jpg') for number in range(1, 7)}

Final thoughts: this seems extremely weird, maybe a tkinter expert can comment if this is expected behaviour. In my opinion the the label should keep a reference to the image. But maybe the interaction between tkinter and Pillow is just suboptimal.

CodePudding user response:

The picture disappears, because after the function terminates, the new label with the image is eaten by the garbage collector. Instead of creating a new label, you just need to replace the image on it and create an additional link to the image, and taking into account Sembei Norimaki's comment, the click_roll() function code:

def click_roll():

    number = rolldice()
    image1 = ImageTk.PhotoImage(Image.open(f'heart{number}.png'))
    image1_label.configure(image=image1)
    image1_label.image = image1
  • Related