Version 26 of Tk image Dos and Don'ts

Updated 2006-02-23 09:12:14

Richard Suchenwirth -- Tk images (bitmap, photo, and whatever the future may bring) are handy and indispensable for fancy displays. This page shows how to enjoy them with even fewer problems (as pop up every now and then in news:comp.lang.tcl ).

DO DELETE UNWANTED IMAGES: It is easy to create an image, but in more complex applications large numbers of images may hang around cramming the memory (so-called "leaks" in Tcl would mostly come from such images). So, make it a rule to delete those images when you no longer need them. In an extreme case, you can sanitize an interp of all images with

    foreach name [image names] {
        image delete $name
    }

Such extreme hygiene can be handy in restoring the state of long-running processes.

Also, images are memory objects. You can display the same image at various places of your GUI. No need to create it more than once, especially not in a loop.

Zarutian 13. july 2005: here is a way to delete all unused images from thie interpreter.

  foreach name [image names] {
    if {![image inuse $name]} { image delete $name }
  }

DON'T FILL photo IMAGES PIXEL BY PIXEL! It looks intuitive, but is extremely slow to write

 for {set x 0} {$x<$xmax} {incr x} {
    for {set y 0} {$y<$ymax} {incr y} {
        $photo put $color -to $x $y
    }
 }

Rather, build up the data as a list of rows, each being a list of colors, and feed them to $photo put in one go:

 set data {{red yellow green blue} {white black orange purple} ...}
 $photo put $data -to 0 0

SH: If you still want to draw pixel by pixel and you know how large your image becomes, first draw the left bottom pixel to allocate the memory for the whole image. If you don't do that, each new pixel to draw results in a newly allocation of memory, that's slow. Another way is to specify the size of the image when creating it.

You can also speed up drawing, by first deleting the image from the canvas, then drawing the pixels and then recreating it in the canvas.

DON'T GIVE AN IMAGE NAME -- TAKE ONE! You may give an image a name at creation time, but maybe you shouldn't -- because a command with that name is created and might silently overwrite the original command or proc, be that "open", "close", or "menu" (DKF: you get a particularly interesting crash if you name the image "." as this completely blows Tk away within the current interpreter or crashes the whole app, depending on the version you are using) If you don't give an image name, image will give you a constructed name which will very probably not interfere with a proc. Example: don't say

    image create photo menu -arg ...

but say

    set img(menu) [image create photo -arg ...]

and use $img(menu) (which will look like image47) ever after.

DKF: I have edited the above to use an array; I find that more visually pleasing and less likely to interfere with anything else that is lying around...

DON'T USE THE -data OPTION! Jan Nijtmans wrote in the comp.lang.tcl newsgroup: Even faster should be not to use the "-data" option, but the "<imageName> put" command. This is almost equivalent, but it prevents the storage of a copy of the binary data in memory. Just replace the line:

     image create photo $pic -data $data

by the two lines:

     image create photo $pic
     $pic put $data

ulis, 2003-07-05: -data and put are NOT almost equivalent.

  • -data is cooked and receives a structured image (as a gif image)
  • put is raw and receives lines of pixels

(CJL - 'put' now treats its input as raw pixels only if it matches no known format)

See the header note at Serializing a photo. ` Ro notes:

If you use the line:

     image create photo $pic -data $data

then it will throw an error if you are reading from a file of length zero. But if you use the two lines:

     image create photo $pic
     $pic put $data

then it will not throw an error if you are reading from a file of length zero. But it will prevent the storage of a copy of the binary data in memory, as noted above by Jan Nijtmans.

The moral of the story is: If you use the two latter lines, don't forget to check if the file is empty first, because otherwise, it will resize your picture to no size and not throw an error. This can be annoying if your picture is displaying in its own window and then all of a sudden you have a very small window to manipulate and you have to depend on your window manager to resize the window or close it.

This was tested on a GNU/Linux system with X11R6 4.0.1a

DKF notes: Actually, image put can take any sort of image data that the -data option can. It uses the same extensible image parsing engine underneath.


See http://www.satisoft.com/tcltk/icons/ for one source of images to use as Tk icons. Perhaps people will add other sources for this sort of thing.


BHE: A lot of these ideas are really helpful. I tried to do something like this:

 namespace eval test { image create photo theImage }
 image inuse test::theImage; # returns 0
 image inuse theImage; # returns 1

hmm. I wanted to create the image within the test namespace so it wouldn't destroy "::theImage". What about this?

 namespace eval test { image create photo test::theImage }
 image inuse test::theImage; # returns 0! why?
 namespace eval test {
    image inuse theImage; # returns 0
    image inuse test::theImage; # returns 1
 }

What's going on here? I gave up and switched to using the image name generated as recommended above, storing the name as an array value instead:

 namespace eval test {
    variable images
    set images(theImage) [image create photo]
 }

MG Images are created with the name you give them, without respect to namespaces. To do what you meant, you need to do:

  image create photo ::test::theImage

Also note that, according to the docs for Tk 8.4, image inuse returns 1 if the image is being used by a widget currently, and 0 otherwise (not whether it exists or not), which most wouldn't be anyway right after creation.


BHE: One other thing. I needed a way to "cut" an area out of an image and save it as a new image, leaving a transparent block. image doesnt have anything like that - the closest thing is $dst copy $src -from x1 y1 x2 y2, but the area remains in the source obviously. You could do this:

 set src [image create photo]
 set bgcolor black
 # load the source ... let's say it's 32x32

 set dst [image create photo]
 $dst copy $src -from 5 5 10 10
 $src put $bgcolor -to 5 5 10 10

But what do you do if you want to leave a transparent block after "cutting"? For that matter, how can you simply erase an area instead of an entire picture? ("$src blank" will delete everything.)

You can use a blank photo and the "$image put" command with -compositingrule as "set" instead of "overlay"

 set eraser [image create photo]
 $src copy $eraser -to 5 5 10 10 -compositingrule  set
 image delete $eraser

See also Images with transparency and plain images


Category Graphics | Don't do that | Arts and crafts of Tcl-Tk programming