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

Tkinter is a popular Python library for creating graphical user interfaces (GUIs). It provides various features and widgets to help developers build interactive applications. One common issue that developers face when using Tkinter is the image not showing up when created in a function.

In this article, we will explore why this issue occurs and how to solve it. We will discuss the difference between the two code snippets provided and understand why the image is displayed in one case but not the other.

Understanding the Problem

To understand the problem, let's analyze the code snippets provided:

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()

The above code snippet creates a Tkinter application with a canvas widget, loads an image file using the PhotoImage class, and displays the image on the canvas using the create_image method. When we run this code, the image is successfully displayed.

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()

In the second code snippet, we have a class called Test that has an __init__ method. This method creates a canvas widget, loads an image file, and displays the image on the canvas. We create an instance of the Test class and run the Tkinter main loop. However, when we run this code, the image does not appear on the canvas.

Understanding the Issue

The issue lies in how tkinter handles garbage collection. When a PhotoImage is created inside a function or method, it is stored as a local variable. Once the function or method finishes executing, the local variables are destroyed, including the PhotoImage object. This results in the image not being displayed.

In the first code snippet, the PhotoImage object is created in the global scope and remains alive throughout the execution of the program. The canvas.create_image method retrieves the image from the global variable and displays it on the canvas.

In the second code snippet, the PhotoImage object is created inside the __init__ method of the Test class. As soon as the __init__ method finishes executing, the local variables are destroyed, including the PhotoImage object. Therefore, when the canvas.create_image method is called, it doesn't find the image object, resulting in the image not being displayed.

Solving the Issue

There are a few ways to solve this issue:

Use a Class Attribute

One way to solve this issue is to use a class attribute to store the PhotoImage object. By doing so, the image object will remain alive as long as the instance of the class exists.

import tkinter

class Test:
    photo = None

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

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

In the updated code snippet, we have added a class attribute called photo and assigned it the value None. Inside the __init__ method, we assign the PhotoImage object to the class attribute Test.photo. By doing so, the image object is stored as a class attribute and will remain alive throughout the execution of the program.

Store the Image Object in a Global Variable

Another way to solve this issue is to store the PhotoImage object in a global variable. This ensures that the image object remains alive throughout the execution of the program.

import tkinter

photo = None

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

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

In the updated code snippet, we have added a global variable called photo. Inside the __init__ method, we assign the PhotoImage object to the global variable photo using the global keyword. By doing so, the image object is stored in a global variable and will remain alive throughout the execution of the program.

Conclusion

In conclusion, when creating a PhotoImage object in Tkinter, it is important to ensure that the object remains alive throughout the execution of the program. This can be achieved by either using a class attribute or a global variable to store the image object. By doing so, the image will be successfully displayed on the canvas.

Next time you encounter the issue of the Tkinter image not showing up when created in a function, remember to apply the solutions discussed in this article to solve the problem.