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