Categories
image python tkinter tkinter-canvas

Why does Tkinter image not show up if created in a function?

74

This code works:

import tkinter

root = tkinter.Tk()
canvas = tkinter.Canvas(root)
canvas.grid(row = 0, column = 0)
photo = tkinter.PhotoImage(file="./test.gif")
canvas.create_image(0, 0, image=photo)
root.mainloop()

It shows me the image.

Now, this code compiles but it doesn’t show me the image, and I don’t know why, because it’s the same code, in a class:

import tkinter

class Test:
    def __init__(self, master):
        canvas = tkinter.Canvas(master)
        canvas.grid(row = 0, column = 0)
        photo = tkinter.PhotoImage(file="./test.gif")
        canvas.create_image(0, 0, image=photo)

root = tkinter.Tk()
test = Test(root)
root.mainloop()

3

  • 2

    effbot.org is down. The gist of it is that the image is passed by reference. If the reference is to a local variable, the memory referenced gets reused and the reference becomes stale. The variable storing the image should be in the same scope (has to have the same lifetime) as the Tk gui object it appears on.

    – maszoka

    Jan 31, 2021 at 1:26

  • @maszoka: effbot.org may be down, but you can still read the link Why do my Tkinter images not appear? thanks to the Internet Archive wayback machine.

    – martineau

    Oct 5, 2021 at 13:35

  • 1

    Also note that the same problem can appear anywhere temporary PhotoImages are used, for example in a calling sequence such as label = Label(image=ImageTk.PhotoImage(Image.fromarray(data))).

    – martineau

    Feb 26 at 15:51


104

The variable photo is a local variable which gets garbage collected after the class is instantiated. Save a reference to the photo, for example:

self.photo = tkinter.PhotoImage(...)

If you do a Google search on “tkinter image doesn’t display”, the first result is this:

Why do my Tkinter images not appear? (The FAQ answer is currently not outdated)

7

  • 7

    Wow. Do they consider this a bug in tkinter? They should.

    Dec 26, 2020 at 23:12

  • I found a very old ticket, already closed without a fix: bugs.python.org/issue632323

    Dec 26, 2020 at 23:19

  • The link is not working atm, is there another way than using “global”?

    – Aru

    Jan 19, 2021 at 19:15


  • @aru: This example shows how to do it without using a global.

    Jan 19, 2021 at 19:18


  • 7

    @TamasHegedus: I agree it’s bug, but apparently not one that anyone has ever bothered to fix after (currently) nearly two decades. Have lost count how many times I see a question regarding to it still pops up.

    – martineau

    Mar 15, 2021 at 17:03

6

from tkinter import *
from PIL import ImageTk, Image

root = Tk()

def open_img():
    global img
    path = r"C:\.....\\"
    img = ImageTk.PhotoImage(Image.open(path))
    panel = Label(root, image=img)
    panel.pack(side="bottom", fill="both")
but1 = Button(root, text="click to get the image", command=open_img)
but1.pack()
root.mainloop() 

Just add global to the img definition and it will work

1

  • 1

    This answer is fine for a program that just uses functions, but if, as in the OP’s case, you use a class, than global is not the way to go.

    Jan 7 at 16:44

4

The problem is Python automatically deletes the references to the variable by a process known as Garbage Collection. The solution is to save the reference or to create a new reference.

The following are the ways:

  1. Using self to increase the reference count and to save the reference.
import tkinter

class Test:
    def __init__(self, master):
        canvas = tkinter.Canvas(master)
        canvas.grid(row = 0, column = 0)
        self.photo = tkinter.PhotoImage(file="./test.gif") # Changes here
        canvas.create_image(0, 0, image=self.photo) # Changes here

root = tkinter.Tk()
test = Test(root)
root.mainloop()
  1. Saving it to a list to increase the reference count and to save the reference.
import tkinter
l=[]
class Test:

    def __init__(self, master):
        canvas = tkinter.Canvas(master)
        canvas.grid(row = 0, column = 0)
        photo = tkinter.PhotoImage(file="./test.gif")
        l.append(photo)
        canvas.create_image(0, 0, image=photo)

root = tkinter.Tk()
test = Test(root)
root.mainloop()

While using method 2, you can either make a global list as i did or use list inside the class. Both would work.

Some useful links: