I created a simple application to display a scatter chart using the Tkinter Canvas widget (see a simple example below). After building 10,000 data points, the application becomes very slow, which can be seen trying to resize the window.
I understand that every element added to the Canvas is an object, so at some point there may be some performance issues, however I expected the level to be much higher than 10,000 simple oval objects. In addition, I could accept some delays when drawing points or interacting with them, but after drawing them, why not just resize the window so slowly?
After reading the problems with the effbot effect with Canvas widgets, it seems that when resizing, unused continuous inactivity tasks may be required:
The Canvas widget implements a direct image of the damage / repair model. Changes to the canvas and external events, such as Expose, are all seen as “damage” to the screen. The widget supports a dirty rectangle to track the damaged area.
When the first corruption event occurs, the canvas registers an unoccupied task (using after_idle), which is used to “repair” the canvas when the program returns to the main Tkinter loop. You can force updates by calling the update_idletasks method.
So the question is, is there a way to use update_idletasks to make the application more responsive after the data has been built? If so, how?
The following is the simplest working example. Try resizing the window after loading to see how slow the application is.
Update
I initially observed this problem on Mac OS X (Mavericks), where I get a significant surge in CPU usage with a simple window resizing. Encouraged by Ramchandra's comments, I tested this on Ubuntu and this does not seem to be happening. Perhaps this is a Mac Python / Tk issue? It would not be the first I came across, see my other question:
PIL PNG display broken on OS X Mavericks?
Can someone try on Windows (I do not have access to the Windows window)?
I can try running on a Mac with my own compiled version of Python and see if the problem persists.
Minimum working example:
import Tkinter import random LABEL_FONT = ('Arial', 16) class Application(Tkinter.Frame): def __init__(self, master, width, height): Tkinter.Frame.__init__(self, master) self.master.minsize(width=width, height=height) self.master.config() self.pack( anchor=Tkinter.NW, fill=Tkinter.NONE, expand=Tkinter.FALSE) self.main_frame = Tkinter.Frame(self.master) self.main_frame.pack( anchor=Tkinter.NW, fill=Tkinter.NONE, expand=Tkinter.FALSE) self.plot = Tkinter.Canvas( self.main_frame, relief=Tkinter.RAISED, width=512, height=512, borderwidth=1) self.plot.pack( anchor=Tkinter.NW, fill=Tkinter.NONE, expand=Tkinter.FALSE) self.radius = 2 self._draw_plot() def _draw_plot(self):