Yotam
Yotam

Reputation: 10685

generating animation in tkinter

I wrote a small program using tkinter to generate a figure based on parameters I'm giving the program (through gui interface). I am drawing the figure directly with tkinter using Canvas. Now I want to generate a series of figures and put them in animated loop. I can generate a gif animation and load it into the figure but the question is if I can do this directly in tkinter. I can put everything in loop, but then I'll lose the event handler. Any other option?


Edit

Here is a simple script:

#!/usr/bin/python
from Tkinter import *
import ttk
import numpy as np


colors = {1:'#F00',
          2:'#0F0',
          3:'#00F',
          4:'#FF0',
          5:'#0FF'}

root = Tk()
canvas = Canvas(root,width=400,height=400)
label = Label(root,text='seed')
values = StringVar()
combo =  ttk.Combobox(root)


def painter(event):
    seed = int(combo.get())
    np.random.seed(seed)
    nrows =  10
    ncols =  10
    Y = 0
    dx = 400/nrows
    dy = 400/ncols
    for i in range(nrows):
        X = 0
        for j in range(ncols):
            n =  np.random.randint(1,5)
            col =  colors[n]
            canvas.create_rectangle(X,Y,X+dx,Y+dy,width=1,fill=col)
            X += dx
        Y += dy


canvas.grid(column=0,row=0,rowspan=2)
combo.grid(row=1,column=1)
label.grid(row=0,column=1)
combo['values'] =  [1,2,3,4]
combo.bind('<<ComboboxSelected>>',painter)
combo.current(0)



painter(None)


root.mainloop()

I could change the painter function to include an infinite loop but this way I never reach the mainloop(), so I need to call this only after the main loop is created, but how?

Upvotes: 0

Views: 2882

Answers (1)

James Kent
James Kent

Reputation: 5933

a simple example of some animation using tkinter canvas:

import tkinter as tk

class App(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.title("Canvas animation example")

        self.c = tk.Canvas(self, width=400, height=400)
        self.c.pack()

        self.f_index = 0 # index so we know which frame to draw next
        # array to hold our frame data,
        # you'll probably need this to hold more than
        # just a set of coordinates to draw a line...
        self.f_data = [] 

        for num in range(0, 400, 5): # make up a set of fake data
            self.f_data.append([num, num, num+10, num+10])

    def next_frame(self):
        data = self.f_data[self.f_index] # fetch frame data
        self.c.delete('all') # clear canvas
        self.c.create_line(*data) # draw new frame data
        self.f_index += 1 # increment frame index
        if (self.f_index >= len(self.f_data)): # check and wrap if at end of sequence
            self.f_index = 0
        self.c.after(50, self.next_frame) # call again after 50ms

if __name__ == "__main__":
    app = App()
    app.next_frame() # called manually once to start animation
    # could be started with 'after' instead if desired
    app.mainloop()

note that the more items you want to delete and redraw the slower this code will perform. you need to have realistic expectations, drawing a few 10's of items will be fine, a few 100's might be ok on a decent pc, but 1000's of items would be very poorly performing.

Upvotes: 2

Related Questions