Python Forum

Full Version: canvas image problem
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hi,

The (abbreviated) code below does exactly what it is supposed to do.
-Open an image
-resize it
-show it on canvas

But it shows no picture. I have found that this could be because of the garbage collector
that takes the picture away before it can be shown. Some say it is a tkinter "bug".

I can see it should work perfectly, if I add a last line that makes the program crash, e.g.
some operation with a non existant variable ! Now it shows the image!

I read that you must "hold on" to the picture by attaching it to something, like "label.image= img."
But I have no label, and neither "root", nor "canvas" have an .image property;
What to do ?
Paul

 img = Image.open(foto)
 resized_img = img.resize((newW,newH),Image.LANCZOS)
 show_img =ImageTk.PhotoImage(resized_img)      
 x_offset = int((1700-newW)/2)
 canvas.create_image(x_offset,0, image=show_img, anchor = NW)
This works

#! /usr/bin/env python3

import tkinter as tk

root = tk.Tk()
my_img = tk.PhotoImage(file='icons/ratt.png')
my_img.img = my_img

resized_img = my_img.subsample(2,2)
canvas = tk.Canvas()
canvas.create_image(root.winfo_reqwidth(), root.winfo_reqheight()/2, image=resized_img)
canvas.pack()
root.mainloop()
This works as well

#! /usr/bin/env python3

import tkinter as tk
from PIL import Image, ImageTk

root = tk.Tk()
my_img = (Image.open('icons/ratt.png'))
resized_img = my_img.resize((120,120), Image.LANCZOS)
new_img = ImageTk.PhotoImage(resized_img)
canvas = tk.Canvas()
canvas.create_image(root.winfo_reqwidth(), root.winfo_reqheight()/2, image=new_img)
canvas.pack()
root.mainloop()
OK, I will study those.
All in all, there is virtually no difference between the second example and mine.

I have not tried the subsample one.
Will do, and report back.
Paul
Please provide runnable examples. The posted code does not work at all and the missing parts are probably the cause of the problems you are seeing.

I wrote this code that opens a .png file, resizes and draws the image. Looks a lot like your code and it works perfectly.
from PIL import Image, ImageTk
import tkinter as tk

root = tk.Tk()
img = Image.open('dice6.png')
resized_img = img.resize((100, 100), Image.LANCZOS)
show_img = ImageTk.PhotoImage(resized_img)

canvas = tk.Canvas(root, width=200, height=200)
canvas.pack()
canvas.create_image(100, 100, image=show_img)
root.mainloop()
This code looks the same to the unwary eye, but it does not display the image.
from PIL import Image, ImageTk
import tkinter as tk

def get_image(filename):
    img = Image.open(filename)
    resized_img = img.resize((100, 100), Image.LANCZOS)
    show_img = ImageTk.PhotoImage(resized_img)
    return show_img

root = tk.Tk()
canvas = tk.Canvas(root, width=200, height=200)
canvas.pack()
canvas.create_image(100, 100, image=get_image('dice6.png'))
root.mainloop()
This demonstrates what you are calling the "tkinter bug". This is not really a bug, but a result of tkinter being such a thin Python wrapper over Tk. tkinter unloads the responsibility of maintaining image references on the programmer. When you draw an image on the canvas, tkinter passes along the image ID and DOES NOT keep a reference. If you delete your only reference to the image the Python garbage collector sees the reference count drop to zero and the object memory is recycled. The reason the first example displays an image and the second doesn't is because the variables that reference the image in the second example are local to the function. These are deleted when the function exits leaving the image with nobody referencing it, and Python recycles the image.

You can stuff a reference anywhere, but it is best if the holder is related to the image. In this example I am going to fix the problem by adding an "image" attribute to the canvas that displays the image.
from PIL import Image, ImageTk
import tkinter as tk

def get_image(filename):
    img = Image.open(filename).resize((100, 100), Image.LANCZOS)
    return ImageTk.PhotoImage(img)

root = tk.Tk()
canvas = tk.Canvas(root, width=200, height=200)
canvas.pack()
canvas.image = get_image('dice6.png')  # Make sure you don't overwrite a real attribute.
canvas.create_image(100, 100, image=canvas.image)
root.mainloop()
I could keep the image in a global variable or a class variable or a list or a dictionary or a ...

If this information does not solve your problem please post a more complete example.
Hi,

Thanks for all the help. I wish the tkinter docs would explain things better.
I now understand why the image is disappearing.

So it seems I can stuff the reference anywhere.
My photoImage is called "show_img", just adding
root.img = show_img
does the trick, and I don't have to change anything.
Thx,
Paul