Imad
Imad

Reputation: 2741

Displaying an image in python via Button and Canvas

I am a begginer in python, tkinter. I have written a code that should normally display an image in a canvas.

What happens is that the main frame (gui) is displayed with the menu bar, then when I click on load image, the gui window shrinks (to 100x100 I guess) but nothing is displayed within.

Could you please explain to me why this is happening so I can understand where the error occurs, and how to correct it?

# -*- coding:utf-8 -*-
# Imports
from tkinter import Tk, Menu, Canvas
from PIL import Image, ImageTk

# Function definitions
def deleteImage(canvas):
    canvas.delete("all")
    return

def loadImage(canvas, img):
    filename = ImageTk.PhotoImage(img)
    canvas.image = filename
    canvas.create_image(0,0,anchor='nw',image=filename)
    return

def quitProgram():
    gui.destroy()
# Main window
gui = Tk()

# Inside the main gui window
#Creating an object containing an image
# A canvas with borders that adapt to the image within it
img = Image.open("fleur.jpg")
canvas = Canvas(gui,height=img.size[0],width=img.size[0])
canvas.pack()

# Menu bar
menubar = Menu(gui)
# Adding a cascade to the menu bar:
filemenu = Menu(menubar, tearoff=0)
menubar.add_cascade(label="Files", menu=filemenu)
# Adding a load image button to the cascade menu "File"
filemenu.add_command(label="Load an image", command=loadImage)
# Adding a delete image button to the cascade menu "File"
filemenu.add_command(label="Delete image", command=deleteImage)
filemenu.add_separator()
filemenu.add_command(label="Quit", command=quitProgram)
menubar.add_separator()
menubar.add_cascade(label="?")

# Display the menu bar
gui.config(menu=menubar)
gui.mainloop()

EDIT: The second problem is that I want to create a canvas and the image in the main gui window, and pass them as arguments to the menu buttons (See code above, where img and canvas are created separately from the function loadImage). Seeing as putting parenthesis in the command=loadImage() created a problem on its own.

Another point that rises a question in my head : Regarding the first problem which was solved by keeping a reference to the filename=ImageTk.PhotoImage(img). Wouldn't it normally be pointless to keep a reference inside the function since it's a local variable anyway?

Upvotes: 1

Views: 9644

Answers (1)

Lafexlos
Lafexlos

Reputation: 7735

As stated in effbot's PhotoImage page, you have to keep a reference of your image to ensure it's not garbage collected.

You must keep a reference to the image object in your Python program, either by storing it in a global variable, or by attaching it to another object.

Note: When a PhotoImage object is garbage-collected by Python (e.g. when you return from a function which stored an image in a local variable), the image is cleared even if it’s being displayed by a Tkinter widget.

To avoid this, the program must keep an extra reference to the image object. A simple way to do this is to assign the image to a widget attribute, like this:

Your loadImage() method should look like below.

def loadImage():
    img = Image.open("fleur.jpg")
    filename = ImageTk.PhotoImage(img)
    canvas = Canvas(gui,height=100,width=100)
    canvas.image = filename  # <--- keep reference of your image
    canvas.create_image(0,0,anchor='nw',image=filename)
    canvas.pack()

Upvotes: 4

Related Questions