Update Tkinter Canvas in Mainloop()

Update Tkinter canvas in mainloop()

You are going to want to use after() to manager an updating loop that wont affect the mainloop. By using after() we can create a visual update over time. We will need a tracking variable to manage the index on the loop and a global statement for the tracker.

Example:

from tkinter import *
import random
height, width = 350, 600
randomLines = []
master = Tk()
tracker = 0
w = Canvas(master, width=width, height=height)
w.pack()

def drawRandomLines():
global tracker
if tracker <= width:
w.create_line(tracker, 0, tracker, randomLines[tracker])
tracker += 1
master.after(10, drawRandomLines)

def bubbleSort():
for passnum in range(len(randomLines)-1, 0, -1):
for i in range(passnum):
if randomLines[i] > randomLines[i+1]:
temp = randomLines[i]
randomLines[i] = randomLines[i+1]
randomLines[i+1] = temp

for i in range(width):
rnd = random.randint(0, width)
randomLines.append(rnd)
drawRandomLines()
bubbleSort()
mainloop()

Update an element of a canvas interrupting the main loop of Tkinter

widget.after(ms, callback, *args) calls the callback(*args) ms milliseconds after the first time it's been read by the program. So you can define a callback method, self.flashRow for example, and just call it with your time frame in __init__ like:

self.canvasRoot.after(250, self.flashRow)

or:

self.flashRow

both should be fine as long as they're before the mainloop call. Then in your callback, self.flashRow, you need to make sure it recursively calls itself in a time frame such as 250 ms:

self.canvasRoot.after(250, self.flashRow)

I configured self.flashRow a bit to have some random flashing:

def flashRow(self):
'''Flashes the row specified as attribute'''

import random
global spellerFrame
_row = random.randint(0, 2)
_color = random.choice((color_text_hl, 'grey'))
print(_color)
for col_index in range(self.n_col):
spellerFrame.itemconfig(handle_comm[_row][col_index], fill=_color)
self.canvasRoot.after(250, self.flashRow)

Also see, the entire configuration of the non-mcve code you've provided:

from tkinter import *

class RCP:

global handle_comm
global handle_rect
global handle_res
global spellerFrame

global color_rect_bg, color_rect_hl, color_text_bg, color_text_hl, color_text_wa

def __init__(self, targets, w_width, w_height):
self.n_row = len(targets)
self.n_col = len(targets[0])
self.w_width = w_width
self.w_height = w_height
self.targets = targets
self.canvasRoot = Tk()
self.canvasRoot.configure(background='grey')
self.setDefaults()

# Initializate the main loop
self.createGrid()
self.canvasRoot.after(250, self.flashRow)
self.canvasRoot.mainloop()

def setDefaults(self):
global color_rect_bg, color_rect_hl, color_text_bg
global color_text_hl, color_text_wa
color_rect_bg = '#000000'
color_rect_hl = '#ffffff'
color_text_bg = '#757575'
color_text_hl = '#ffffff'
color_text_wa = '#ffff00'
global font_ratio_bg, font_ratio_hl
font_ratio_bg = 0.5
font_ratio_hl = 0.7

def createGrid(self):
# Calculate the maximum cell and font size that do not
# deform the commands' display
cell_size = min(self.w_height / self.n_row, self.w_width / self.n_col)
font_size = int(round(font_ratio_bg * cell_size))
result_size = int(round(cell_size/5))

# Create the canvas for the result text
global handle_res
resultLabel = Canvas(self.canvasRoot, width=self.w_width,
height=result_size, bd=0,
highlightthickness=0, relief='ridge', background='grey')
resultLabel.grid(row=0, column=0, columnspan=self.n_col)
handle_res = resultLabel.create_text(2, round(result_size/2),
text=' PRUEBA', fill=color_text_wa, anchor='w',
font=("Purisa", round(result_size/2), "bold"))

# Create the frame for the speller
global spellerFrame
spellerFrame = Canvas(self.canvasRoot, width=self.w_width,
height=self.w_height, bd=0,
highlightthickness=0, relief='ridge')
spellerFrame.grid(row=1, column=0)

# Create the grid of commands
global handle_comm, handle_rect
handle_comm = [[None for i in range(self.n_col)] for j in range(self.n_row)]
handle_rect = handle_comm
for row_index in range(self.n_row):
for col_index in range(self.n_col):
x1 = col_index * cell_size
y1 = row_index * cell_size
x2 = (col_index + 1) * cell_size
y2 = (row_index + 1) * cell_size
handle_rect[row_index][col_index] = spellerFrame.create_rectangle(x1,
y1, x2, y2, fill=color_rect_bg)
handle_comm[row_index][col_index] = \
spellerFrame.create_text(((x1+x2)/2,(y1+y2)/2),
text=self.targets[row_index][col_index],
fill=color_text_bg,
font=("Purisa", font_size, "bold"))

def flashRow(self):
'''Flashes the row specified as attribute'''

import random
global spellerFrame
_row = random.randint(0, 2)
_color = random.choice((color_text_hl, 'grey'))
print(_color)
for col_index in range(self.n_col):
spellerFrame.itemconfig(handle_comm[_row][col_index], fill=_color)
self.canvasRoot.after(250, self.flashRow)

targets = [['A','B','C','D'],['E','F','G','H'],['I','J','K','L']]
myRCP = RCP(targets, 800, 600)

how to make tkinter canvas update?

You will need to delete the old graph and create a new graph whenever you change the numbers.

You should do that by moving the code to draw the bar chart into a function.

def draw_barchart(data):
c.delete("all")
for x, y in enumerate(data):
x0 = x * x_stretch + x * x_width + x_gap
y0 = c_height - (y * y_stretch + y_gap)
x1 = x * x_stretch + x * x_width + x_width + x_gap
y1 = c_height - y_gap
c.create_rectangle(x0, y0, x1, y1, fill="red")
c.create_text(x0 + 2, y0, anchor=tk.SW, text=str(y))

You can then call this whenever the data changes, passing in the new data.

There are other problems with your code, however. You need to call mainloop() at the very end of your program since it won't return until the window is destroyed.

You also either need to use after to periodically update the data, or call tkinter's update function to allow the window to redraw itself.

Here's how to write the bottom part of the code to add data and redraw the graph every five seconds:

def update():
count = 0
for i in range(5):
data.append(random.randint(1, 11))
c.delete("all")
draw_barchart(data)
root.after(5000, update)

update()

root.mainloop()


Related Topics



Leave a reply



Submit