16

I'm trying to learn some tkinter. I can't get tkinter to display an icon. I don't know where it goes wrong. It does not produce any error and it respects the size of the image, but it's invisible. Everything I found online tells to add a second reference to prevent garbage collection from python, but somehow this did not the trick. Here is the part of my code that goes wrong:

from Tkinter import *
from PIL import Image, ImageTk

class GUI:

    def __init__(self, master):

        frame = Frame(master)
        frame.pack()

        #status bar
        self.bar = Frame(root, relief=RIDGE, borderwidth=5)
        self.bar.pack(side=TOP)

        self.iconPath = 'data/icons/size.png'
        self.icon = ImageTk.PhotoImage(Image.open(self.iconPath))
        self.icon_size = Label(self.bar, image = self.icon)
        self.icon_size.pack(side=LEFT)

root = Tk()


app = GUI(root)

root.mainloop()
Whitecold
  • 281
  • 1
  • 3
  • 8

3 Answers3

38

When you add a PhotoImage or other Image object to a Tkinter widget, you must keep your own reference to the image object. If you don’t, the image won’t always show up.

Decision here

user1142464
  • 490
  • 4
  • 8
  • 3
    Currently broken, can access here: https://web.archive.org/web/20201111190625/http://effbot.org/pyfaq/why-do-my-tkinter-images-not-appear.htm – Mattamue Apr 28 '21 at 22:45
  • This is especially true when you use OOP. Assign the `PhotoImage` as an instance variable and then use it. Transparency doesn't matter as someone mentioned the opposite above. – demberto Mar 10 '22 at 13:54
12

For some reason (I don't understand exactly why) you must anchor the image object into the widget in order for it to display. Therefore try the following change at your code:

from Tkinter import *
from PIL import Image, ImageTk

class GUI:

    def __init__(self, master):

        frame = Frame(master)
        frame.pack()

        #status bar
        self.bar = Frame(root, relief=RIDGE, borderwidth=5)
        self.bar.pack(side=TOP)

        self.iconPath = 'data/icons/size.png'
        self.icon = ImageTk.PhotoImage(Image.open(self.iconPath))
        self.icon_size = Label(self.bar)
        self.icon_size.image = self.icon  # <== this is were we anchor the img object
        self.icon_size.configure(image=self.icon)
        self.icon_size.pack(side=LEFT)

root = Tk()


app = GUI(root)

root.mainloop()

Good Luck!

Tim
  • 41,901
  • 18
  • 127
  • 145
Diego Vélez
  • 635
  • 6
  • 4
  • 7
    The "why" is because python garbage collection will destroy the image unless there's a permanent reference to it. – Bryan Oakley Nov 29 '14 at 03:05
  • 1
    @BryanOakley There was already a permanent reference as far as I can tell (`self.icon`). The first statement here is wrong, I just tested without an anchor in `Label` itself and it works fine. – kabanus Apr 14 '18 at 09:35
  • See why this is the case [here](https://stackoverflow.com/a/74857227/13597979), @Br – TimH Dec 20 '22 at 20:00
6

For all future readers, in my case the problem lied with transparency. Removing the alpha channel from the image fixed it.

Mirac7
  • 1,566
  • 4
  • 26
  • 44