How can I use Tkinter to visualize time series data?

I am trying to use Tkinter to visualize some time series data. My data is presented as a 2D matrix, with rows referring to blocks and columns from a specific time period. All values ​​are between 0 and 1.

I want to create a python script using Tkinter that creates a window, displays the blocks in a square matrix with the first column defining the brightness of each block, and then after a given amount of time, change the brightness of the blocks to match the successive columns in the data.

I created a shorthand version of what I have so far:

#!/usr/bin/python

import sys
import os.path
import Tkinter as tk
import math

# program constants
WINDOW_WIDTH = 900

# function to change the colours of the blocks based on current period
def redrawPeriod(t):
    for b in xrange(len(blocks)):
        luminance = int(blocks[b][t] * 255)
        colour = "#%x%x%x" % (luminance,luminance,luminance)
        canvas.itemconfig(block_ids[b],fill=colour)

# sample data (4 blocks 4 periods)
blocks = [
        [0.0, 0.2, 0.5, 0.8],
        [0.3, 0.0, 0.4, 0.0],
        [0.5, 0.7, 0.0, 1.0],
        [0.0, 0.0, 0.3, 0.6],
        [1.0, 0.5, 0.2, 0.9]
      ]

# get number of blocks and periods
nB = len(blocks)
nT = len(blocks[0])

n_cols = int(math.sqrt(nB))
n_rows = n_cols

# if not perfect square number of blocks, add extra row
if (nB % n_rows != 0):
    n_rows = n_rows + 1

# calculate block size
BLOCK_SIZE = WINDOW_WIDTH / n_cols
WINDOW_HEIGHT = BLOCK_SIZE * n_rows

# initialise Tkinter
root = tk.Tk()

# initialise canvas
canvas = tk.Canvas(root, width=WINDOW_WIDTH, height=WINDOW_HEIGHT, background="#002080")

# open canvas
canvas.pack()

# container for block objects
block_ids = []

x = 0
y = -1*BLOCK_SIZE

# initialise block objects
for b in xrange(nB):
    if (b % n_cols == 0):
        x = 0
        y = y + BLOCK_SIZE

    luminance = int(blocks[b][0] * 255)
    colour = "#%x%x%x" % (luminance,luminance,luminance)
    id = canvas.create_rectangle(x, y, x+BLOCK_SIZE, y+BLOCK_SIZE, outline="",fill = colour)
    block_ids.append(id)
    x = x + BLOCK_SIZE

for t in xrange(nT):
    root.after(1000, redrawPeriod,t)

root.mainloop()

      

I seem to be doing what I want, but every time it jumps right to the end frame - i.e. it doesn't draw one frame, doesn't pause, doesn't draw another frame, doesn't stop again, etc.

Can anyone help me figure out what I am doing wrong?

+3


source to share


1 answer


Your problem is that when you call:

for t in xrange(nT):
    root.after(1000, redrawPeriod,t)

      

root.after()

does not block execution, so the for loop is very fast and all your redraw events are called at the same time after 1000ms.



The usual way to start an animation in Tkinter is to write an animation method that calls itself after a delay (see Method for Animated Move for python and Tkinter canvas objects running over time for more information).

In your case, you can do something like this:

# Create all widgets
...

# Store the period indices to be redrawn ([0, 1, 2, ..., nT-1])
periods = range(nT)

# Define the animation method
def animation_loop(delay):
    # Check if there are still periods to be drawn
    if len(periods) > 0:
        t = periods[0]
        redrawPeriod(t)
        # Remove the period that was just drawn from the list of periods
        del periods[0]
        # Call the animation_loop again after a delay
        root.after(delay, animation_loop, delay)

# Start the animation loop
animation_loop(1000)

# Launch the app
root.mainloop()

      

+1


source







All Articles