Tkinter canvas refresh rate decreases during the program

The following python program creates a Tkinter object Canvasand draws a random matrix on it. It also measures the time required to complete 10 subsequent updates. As you can see from below, this time it grows continuously and substantially throughout the program. What is the reason for this behavior and how to fix it?

from Tkinter import Tk, Canvas
import time
import numpy as np

window = Tk()
nRows = 30
nCols = 30
CELL_SIZE = 10
canvas = Canvas(window, width=CELL_SIZE*nRows,
                height=CELL_SIZE*nCols)
canvas.pack()
def drawbox(m):
    for y in range(nRows):
        for x in range(nCols):
            if m[y][x]:
                color = '#00FF00'
            else:
                color = '#000000'
            canvas.create_rectangle(CELL_SIZE*x,
                                    CELL_SIZE*y,
                                    CELL_SIZE*x+CELL_SIZE,
                                    CELL_SIZE*y+CELL_SIZE,
                                    fill=color,
                                    outline="#000000", width=1)
count = 0
timeStart = time.time()
while(True):
    board = np.random.rand(nRows, nCols) > 0.5
    if count % 10 == 0:
        print '%.1f seconds'%(time.time() - timeStart)
        timeStart = time.time()
        count = 0
    count += 1
    drawbox(board)
    canvas.after(5)
    canvas.update()

Here is the conclusion

0.0 seconds
1.7 seconds
4.1 seconds
6.3 seconds
8.7 seconds
+3
source share
3 answers

You create new items with every update. The canvas displays all the rectangles that you previously added and therefore slower and slower (each update creates 900 rectangles, after 30 you have 27,000 objects in your scene ...)

, , .

:

rectangles = [ [ canvas.create_rectangle (CELL_SIZE*x, CELL_SIZE*y,
                    CELL_SIZE*x+CELL_SIZE, CELL_SIZE*y+CELL_SIZE,
                    fill="#000000",outline="#000000", width=1) 
                 for x in range(nCols)] for y in range(nRows)]

drawbox:

canvas.itemconfig(rectangles[y][x], fill=color)
+2

, drawbox, , . ( , ). , , , .

- , canvas.itemconfig(rectangle_id,fill=color). () drawbox , .

def drawbox(m,_rectangles={}):
    if(_rectangles):
        myrectangles=_rectangles
    else:
        myrectangles={}

    for y in range(nRows):
        for x in range(nCols):
            if m[y][x]:
                color = '#00FF00'
            else:
                color = '#000000'
            if(not _rectangles):
                cid=canvas.create_rectangle(CELL_SIZE*x,
                                            CELL_SIZE*y,
                                            CELL_SIZE*x+CELL_SIZE,
                                            CELL_SIZE*y+CELL_SIZE,
                                            fill=color,
                                            outline="#000000", width=1)
                myrectangles[(y,x)]=cid
            else:
                canvas.itemconfig(_rectangles[(y,x)],fill=color)

    if(not _rectangles):
      _rectangles.update(myrectangles)
+2

, , ( 1000 10 1000 ). . , , , . , .

The second problem is your endless loop and your sleep ( canvas.after(5)). There's a much better way to achieve the effect without the annoying side effect of freezing the GUI for 5ms at a time.

All you have to do is create a function that draws or updates objects, and then queues the event for a second call. Then it will automatically update without the need to explicitly create a loop.

For instance:

def redraw():
    board = np.random.rand(nRows, nCols) > 0.5
    drawbox(board)
    canvas.after(100, redraw)
+2
source

All Articles