It doesn't have to be a global variable, it just has to be a variable that isn't thrown away. In fact it really shouldn't be a global variable. Global variables are BAD, BAD, BAD. Only use them when you must.
If you want to make buttons with images It may work better for you if you make a new button class that keeps track of it's images.
import pathlib
import tkinter as tk
IMAGE_DIR = pathlib.Path(__file__).parent
class ImageButton(tk.Button):
"""I am a button with an image"""
def __init__(self, parent, image_file, *args, **kvargs):
self.image = tk.PhotoImage(file=IMAGE_DIR/image_file)
super().__init__(parent, *args, image=self.image, **kvargs)
ROOT = tk.Tk()
ImageButton(ROOT, 'dice1.png').pack()
ROOT.mainloop()
All this class does is add an instance variable to Button that references the image so it isn't garbage collected.
If you don't like making a class, you can always stuff the image variable into the Button object.
import pathlib
import tkinter as tk
IMAGE_DIR = pathlib.Path(__file__).parent
ROOT = tk.Tk()
image = tk.PhotoImage(file=IMAGE_DIR/'dice1.png')
button = tk.Button(ROOT, image=image)
button.image = image # Add image instance variable to button
button.pack()
ROOT.mainloop()
I've seen a lot of code online that does this. I don't know how many of the authors knew what they were doing and why it worked. Essentially it does the same thing as the subclass solution, but in an informal way. Python classes act a lot like dictionaries and you can add attributes to them like you can add entries to a dictionary. The code above adds an attributed named "image" to the button object. This prevents the image reference count from going to zero and the image being garbage collected. Normally a tkinter Button doesn't have an image attribute. The code below raises an exception when trying to access "button.image".
import tkinter as tk
IMAGE_DIR = pathlib.Path(__file__).parent
ROOT = tk.Tk()
image = tk.PhotoImage(file=IMAGE_DIR/'dice1.png')
button = tk.Button(ROOT, image=image)
print(button.image)
button.pack()
ROOT.mainloop()
Output:
AttributeError: 'Button' object has no attribute 'image'