Saving a canvas animation

The only built-in image save function for Tkinter canvas is a postscript save. This didn’t work for me and instead of finding out why, I thought of another way which works great.
Create a frameless canvas window using ‘overrideredirect(1)’, place the canvas in the top corner and use PIL’s ImageGrab and save the images. Finally, ‘destroy’ the window when finished, since there is no kill button in the top corner. Here is an example that animates a polygon moving on the canvas.

from Tkinter import *
from PIL import ImageGrab
from numpy import array
import os


class App(Tk):
    def __init__(self, parent):
        Tk.__init__(self, parent)
        self.overrideredirect(1)
        
        self.width = 900
        self.height = 640
        self.initialize()
        
    def initialize(self):
        self.c = Canvas(self, width=self.width, height=self.height, background='white')
        self.c.pack(side=RIGHT, expand=YES, fill=BOTH)
        self.update()
        
        self.run_anim()
        self.destroy()
        
        
    def run_anim(self):
        print os.getcwd()
        c = self.c
        polyo = array([34,60,226,15,419,60,359,151,91,151])
        polyd = array([205,253,296,187,388,253,353,360,239,360])
        trantime = 20
        for i in xrange(trantime):
            c.delete('pol')
            ptrans = (float(i)/trantime)*polyd+(trantime-float(i))/trantime*polyo
            c.create_polygon(list(ptrans), width=4, outline='black', fill='red', tags='pol')


            self.update()
            savename = 'im_{0:0>6}'.format(i)
            ImageGrab.grab((0,0,900,640)).save(savename + '.jpg')
            

app = App(None)
app.mainloop()

Tkinter notes

I keep getting tripped up by the same problems so this note on Tkinter is long overdue.

Most importantly, remember to keep a global reference to all images that you want to have persist on the canvas or tk window. Also, make sure that the ‘global’ reference is called at the beginning of any method using those images. I keep forgetting one or the other and getting stuck over why my images are not appearing. Any images created within a method will not appear unless referenced by a more permanent variable. I need to remember that you cannot just create images on the fly and throw them onto the canvas (although this seems to work for lines and ovals). So:
1) Make sure there is a permanent reference, and
2) Make sure to use ‘global’ or pass the image as an argument for editing.

Another problem I keep encountering is editing PIL images and getting the ImageTk.PhotoImage method to accept an image.
Getting subwindow of image:

subwin = imjpg.copy().crop( (box coordinates) )

You can chain the copy with crop.
im.load():
The load method seems to only give you pixel access to an image for editing, so I think it is not much use since you can’t do slicing. ImageDraw is better for drawing figures.

Masking:

# MAKE COPY IF YOU WANT TO KEEP ORIGINAL.
imcopy = im.copy()
# COLOR: 0 = TRANSPARENT, 255 = OPAQUE.
mask = Image.new('L', imjpg.size, color=100)
# GET A VIEW OF IMAGE FOR DRAWING. EDITING 'DRAW' ALSO CHANGES 'MASK'.
draw = ImageDraw.Draw(mask)
...
# OVERWRITE OR ADD ALPHA LAYER TO IMAGE ('RGB' -> 'RGBA')
imcopy.putalpha(mask)
# SAVE TO GLOBALLY REFERENCED VARIABLE AS A Tk COMPATIBLE IMAGE.
immasked = ImageTk.PhotoImage(imcopy)