Python Forum
[Tkinter] image inside button - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: GUI (https://python-forum.io/forum-10.html)
+--- Thread: [Tkinter] image inside button (/thread-34264.html)



image inside button - rwahdan - Jul-12-2021

I am trying to add image inside a button. It is empty, I have the image in the exact location and no errors.

    photo1 = PhotoImage(file="imgs/math/1.png")
    photoimage1 = photo1.subsample(3, 3)
    Button(exam_started,image=photoimage1).pack()
Thanks


RE: image inside button - deanhystad - Jul-12-2021

My guess is you are doing something like this:
import pathlib
import tkinter as tk

IMAGE_DIR = pathlib.Path(__file__).parent

def make_image_button(parent, image_file):
    image1 = tk.PhotoImage(file=IMAGE_DIR/image_file)
    tk.Button(parent, image=image1).pack()
    
ROOT = tk.Tk()
make_image_button(ROOT, 'dice1.png')
ROOT.mainloop()
This displays an empty button, but with one small change it works.
"""A dice game"""
import pathlib
import tkinter as tk

image1 = None
IMAGE_DIR = pathlib.Path(__file__).parent

def make_image_button(parent, image_file):
    global image1
    image1 = tk.PhotoImage(file=IMAGE_DIR/image_file)
    tk.Button(parent, image=image1).pack()
    
ROOT = tk.Tk()
make_image_button(ROOT, 'dice1.png')
ROOT.mainloop()
The reason the image disappears in the upper code is because the only reference to the image object is a local variable in function "make_image_button". When the function exits the image has no references and is garbage collected. In the lower code we fix that problem by assigning a global variable to reference the image.

Does that fix the problem?


RE: image inside button - rwahdan - Jul-12-2021

(Jul-12-2021, 06:26 PM)deanhystad Wrote: Does that fix the problem?

Yes it sure did thanks for pointing out global variables.


RE: image inside button - Yoriz - Jul-12-2021

You can avoid using global and keep a reference to the image on the button object.
photo1 = tk.PhotoImage(file="imgs/math/1.png")
btn = tk.Button(exam_started,image=photoimage1)
btn.pack()
btn.photo1 = photo1



RE: image inside button - deanhystad - Jul-12-2021

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'