Newbie question on Tkinter

Eric Brunel eric.brunel at pragmadev.com
Thu Jul 4 04:04:23 EDT 2002


Michele Simionato wrote:
> I have found a confusing (to me) behavior of Tkinter in displaying GIF
> images.
> Consider the following four programs (assuming you have a GIF image
> called 'picture.gif' in the current directory):
> 
> #Program 1: works
> import Tkinter
> root=Tkinter.Tk()
> img=Tkinter.PhotoImage(file='picture.gif')
> lbl=Tkinter.Label(root,image=img)
> lbl.pack()
> Tkinter.mainloop()
> 
> #Program 2: doesn't work
> import Tkinter
> root=Tkinter.Tk()
> lbl=Tkinter.Label(root,image=Tkinter.PhotoImage(file='picture.gif'))
> lbl.pack()
> Tkinter.mainloop()
> 
> #Program 3: doesn't work
> import Tkinter
> def DisplayPicture(root,picture):
>     img=Tkinter.PhotoImage(file='picture.gif')
>     lbl=Tkinter.Label(root,image=img)
>     lbl.pack()
> root=Tkinter.Tk()
> DisplayPicture(root,'picture.gif')
> Tkinter.mainloop()
> 
> #Program 4: works
> import Tkinter
> def DisplayPicture(root,picture):
>     global img
>     img=Tkinter.PhotoImage(file='picture.gif')
>     lbl=Tkinter.Label(root,image=img)
>     lbl.pack()
> root=Tkinter.Tk()
> DisplayPicture(root,'picture.gif')
> Tkinter.mainloop()
> 
> The second program should give the same output of the first one,
> however
> the first one works, the second one gives a transparent picture! Why
> ??
> The only difference is that there is no explicit name for the
> PhotoImage object.
> Moreover: program 3 has an explicit name for the PhotoImage, but the
> picture is still
> transparent ! Why ?? By trials, I discovered that it is possible to
> fix the problem
> at the price of making the PhotoImage name a global variable (see
> program 4).

You just encountered the weirdest "feature" of Tkinter: it doesn't keep 
references on images, even if they are used. If you're unfamiliar with the 
concept of references in Python, you may at first read the corresponding 
chapter in the reference manual ( 
http://www.python.org/doc/current/ref/objects.html ). Anyway, here is the 
explanation of the behaviour you've seen:

- your program #1 works because when you start Tkinter mainloop, there is 
still an explicit reference to your image (the variable "img")

- your program #2 does not work because:
lbl=Tkinter.Label(root,image=Tkinter.PhotoImage(file='picture.gif'))
does *not* keep a reference on the created image. Therefore, just after 
this statement, the created instance is deleted and can't be used

- your program #3 does not work because the only reference you created in 
your DisplayPicture function (img) was local to the function, so it was 
discarded at the function return. Since:
lbl=Tkinter.Label(root,image=img)
does *not* add a reference to the image, again, the picture was deleted at 
the end of your DisplayPicture function

- your program #4 works because since the variable "img" is global, it 
still exists when Tkinter's mainloop is called, so the image is still 
referenced and can be used.

You may think it's a bug (and I'm personally not convinced that it isn't 
one...), but it is not considered as a bug by Tkinter designers (see Python 
FAQ, entry 4.69 at 
http://www.python.org/cgi-bin/faqw.py?req=show&file=faq04.069.htp )

> How can I avoid that ?

To keep things simple, I'd try to create a sub-class of Tkinter.PhotoImage 
that remembers its instances:

import Tkinter
class MyPhotoImage(Tkinter.PhotoImage):
  instances = []
  def __init__(self, *args, **options):
    apply(Tkinter.PhotoImage.__init__, (self,) + args, options)
    MyPhotoImage.instances.append(self)

Then try:

root=Tkinter.Tk()
lbl=Tkinter.Label(root,image=MyPhotoImage(file='picture.gif'))
lbl.pack()
Tkinter.mainloop()

and it should work the way you expect. But note that all images you'll 
create with this class will be remembered, even unused ones. It may eat up 
memory quite fast if you have lots of images (and/or big ones...).

The other solution is to explicitely keep a reference on every image you 
use, taking care of deleting the reference when the image is not used 
anymore.

> P.S. BTW, how to display jpeg images ?

I think PIL can do that, but I'm not sure. See 
http://www.pythonware.com/products/pil/

HTH
-- 
- Eric Brunel <eric.brunel at pragmadev.com> -
PragmaDev : Real Time Software Development Tools - http://www.pragmadev.com



More information about the Python-list mailing list