[Matplotlib-users] Figures piling up in Tkinter GUI (1.2.0rc2)
Hello everybody, I'm building a small Tkinter GUI using matplotlib, in which I have to change/update plots quite often depending on user input (with different contents & sizes, in different places in the GUI, etc.; but always only one figure at a time). As a first resort, I regenerated the figures with plt.figure(...) whenever necessary; unfortunately, the program happily accumulated memory with every new figure until the computer would no longer cooperate in a timely fashion. The following minimal script should demonstrate the tendency: --- start of script --- import math from Tkinter import Tk, Button import Tkconstants from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.figure import Figure from matplotlib import pyplot as plt def replot(): global globalCanvas, globalFigure, plotShift # any variety of clean-up calls if globalFigure is not None: plt.close() globalCanvas.get_tk_widget().destroy() globalFigure.clf() globalFigure = Figure(dpi=120, figsize=(4, 4)) globalCanvas = FigureCanvasTkAgg(globalFigure, master=root) globalCanvas.get_tk_widget().grid(row=0, column=1) xVals = xrange(100) ax = globalFigure.add_subplot(111) ax.plot(xVals, [math.sin(x + plotShift) for x in xVals]) plotShift += 10 # MAIN globalCanvas = None globalFigure = None plotShift = 0 # just to see the plot change root = Tk() draw_button = Button(root, text="Replot", command=replot) draw_button.grid(row=0, column=0, sticky=Tkconstants.N) root.mainloop() --- end of script --- I have tried various clean-up calls, but the effect (of memory piling up) is always the same. Using objgraph (http://mg.pov.lt/objgraph/), I took a look at object counts by adding the following snippet at the end of the "replot" call: --- start of insertion --- import gc import objgraph gc.collect() print "---" for c in ('FigureCanvasTkAgg', 'Figure'): print "{}\t{}".format(len(objgraph.by_type(c)), c) --- end of insertion --- The output shows that the total number of both Figures and FigureCanvasTkAggs increases constantly (i.e., one each after the first call, then two each, etc.), whereas I had expected that old ones get released, and that the count remains at one each. Now I am wondering if I am missing some detail, e.g., some other clean-up procedure? Or should this work & could be a memory leak in matplotlib or Tkinter? And/or is this approach (of generating a new figure every time) not recommended in the first place? I tried reusing the figure, but some aspects like changing the layout in the GUI and applying new size and dpi then proved tricky in their own ways. Many thanks in advance, Hans -- Everyone hates slow websites. So do we. Make your web apps faster with AppDynamics Download AppDynamics Lite for free today: http://p.sf.net/sfu/appdyn_sfd2d_oct ___ Matplotlib-users mailing list Matplotlib-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/matplotlib-users
Re: [Matplotlib-users] Figures piling up in Tkinter GUI
On Fri, 02 Nov 2012 16:45:22 +0100, wrote: > Message: 5 > Date: Fri, 2 Nov 2012 12:01:35 +0100 > From: Vlastimil Brom > Subject: Re: [Matplotlib-users] Figures piling up in Tkinter GUI > (1.2.0rc2) > To: matplotlib-users@lists.sourceforge.net > Message-ID: > > Content-Type: text/plain; charset=ISO-8859-1 > 2012/11/1 Hans Bering : >> Hello everybody, >> >> I'm building a small Tkinter GUI using matplotlib, in which I have to >> change/update plots quite often depending on user input (with different >> contents & sizes, in different places in the GUI, etc.; but always only >> one figure at a time). >> >> As a first resort, I regenerated the figures with plt.figure(...) >> whenever necessary; unfortunately, the program happily accumulated >> memory with >> every new figure until the computer would no longer cooperate in a >> timely fashion. The following minimal script should demonstrate the >> tendency: >> > ... > > Hi, > I'd recommend to use an embedded plot and only clear and replace its > content [...] > I only roughly adapted that source to use your function and the memory > usage appears to be more effective (although there is some increase > too - as displayed in Process Explorer). Would some variation of the > following work for you? [...] Hi Vlastimil, thanks for your effort; I had tried the approach of clearing & replacing the plot myself, too. Unfortunately, that approach has a different problem: Because of the figure's size, I have to present it with scrollbars, and clearing & reusing the plot does not seem to work when resizing & scrolling the plot. I had posted that problem as a question at Stackoverflow (as http://stackoverflow.com/questions/13197469 ), since I hoped it might be "easier"; i.e., just a misunderstanding on my part of how to wire together the scrollbars/canvas/figure. Please note that, while I also use plt.figure(...) in the post at Stackoverflow, the effect remains the same when using Figure(...) and ax.plot(...). So basically I'm stuck between a rock and a hard place - I can either have the memory issue reported previously; or the plot won't behave properly with scrolling+resizing. I am wondering: Should repeatedly creating Figures in a Tkinter GUI work, and could this be a Matplotlib bug worth mentioning on some bug tracker? Thanks & Regards Hans -- LogMeIn Central: Instant, anywhere, Remote PC access and management. Stay in control, update software, and manage PCs from one command center Diagnose problems and improve visibility into emerging IT issues Automate, monitor and manage. Do more in less time with Central http://p.sf.net/sfu/logmein12331_d2d ___ Matplotlib-users mailing list Matplotlib-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/matplotlib-users
[Matplotlib-users] "bad screen distance" tkinter error with German locale
Hi, sorry if this has already been addressed. I did a search on the archives, but even though that turned up lots of hits, none of them seemed to be related to the issue. The following very simple example will reliably crash in Python 2.7.[0-2] with matplotlib 1.0.1 under a 64 bit German Windows (and probably also on other machines where you can set an equivalent locale): --- import matplotlib from pylab import arange,sin,pi t = arange(0.0, 2.0, 0.01) s = sin(2*pi*t) import locale # the locale setting in the next line makes pyplot.plot crash # have to use "deu_deu" instead of "de_DE" on German Windows locale.setlocale(locale.LC_ALL, 'deu_deu') print "locale =", locale.getlocale(locale.LC_NUMERIC) print "will plot ..." matplotlib.pyplot.plot(t, s, linewidth=1.0) # doesn't get this far with German locale print "will show ..." matplotlib.pyplot.show() --- The program crashes in pyplot.plot(). The stacktrace is: --- Traceback (most recent call last): File "C:\[...]\badScreenSizeMPL.py", line 14, in matplotlib.pyplot.plot(t, s, linewidth=1.0) File "C:\Python27\lib\site-packages\matplotlib\pyplot.py", line 2279, in plot ax = gca() File "C:\Python27\lib\site-packages\matplotlib\pyplot.py", line 593, in gca ax = gcf().gca(**kwargs) File "C:\Python27\lib\site-packages\matplotlib\pyplot.py", line 292, in gcf return figure() File "C:\Python27\lib\site-packages\matplotlib\pyplot.py", line 270, in figure **kwargs) File "C:\Python27\lib\site-packages\matplotlib\backends\backend_tkagg.py", line 82, in new_figure_manager figManager = FigureManagerTkAgg(canvas, num, window) File "C:\Python27\lib\site-packages\matplotlib\backends\backend_tkagg.py", line 400, in __init__ self.toolbar = NavigationToolbar2TkAgg( canvas, self.window ) File "C:\Python27\lib\site-packages\matplotlib\backends\backend_tkagg.py", line 667, in __init__ NavigationToolbar2.__init__(self, canvas) File "C:\Python27\lib\site-packages\matplotlib\backend_bases.py", line 2310, in __init__ self._init_toolbar() File "C:\Python27\lib\site-packages\matplotlib\backends\backend_tkagg.py", line 711, in _init_toolbar borderwidth=2) File "C:\Python27\lib\lib-tk\Tkinter.py", line 2466, in __init__ Widget.__init__(self, master, 'frame', cnf, {}, extra) File "C:\Python27\lib\lib-tk\Tkinter.py", line 1977, in __init__ (widgetName, self._w) + extra + self._options(cnf)) _tkinter.TclError: bad screen distance "640.0" Fatal Python error: PyEval_RestoreThread: NULL tstate --- The reason appears to be that at some point Tkinter tries to parse the string "640.0" as a number, which does not work in a locale where the decimal marker is, e.g., the comma (as in German). If you comment out the locale setting (or set it to "C"), the example works. The float value of 640.0 seems to emerge from the following piece of code in "backend_tkagg.py". --- class NavigationToolbar2TkAgg(NavigationToolbar2, Tk.Frame): [...] def _init_toolbar(self): xmin, xmax = self.canvas.figure.bbox.intervalx height, width = 50, xmax-xmin Tk.Frame.__init__(self, master=self.window, width=width, height=height, borderwidth=2) --- Through the initialization by difference, "width" is a 'numpy.float64'; changing the assignment of "height, width" to height, width = 50, int(xmax-xmin) makes the example program run through without problems. One the one hand, I guess this should be fixed in the depths of Tkinter (where apparently a number type gets stringified just to be parsed again as a number). One the other hand, it would be very simple fix in the TkAgg backend, and it seems sensible to make the width an int. (Perhaps even the intervals in intervalx should already be ints?) I would like to point out that, even though this might sound like a contrived problem, it can easily occur where machines are set up with different languages; we had a tool run on an English Windows, but we got the stack trace from above when we moved that tool to a German Windows which we believed to be set up in just the same way as the original Windows. It took us a day to figure out what the reason behind the cryptic Tkinter error was. Kind regards, H. -- All of the data generated in your IT infrastructure is seriously valuable. Why? It contains a definitive record of application performance, security threats, fraudulent activity, and more. Splunk takes this data and makes sense of it. IT sense. And common sense. http://p.sf.net/sfu/splunk-d2d-c2 ___ Matplotlib-users mailing list Matplotlib-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/matplotlib-users
Re: [Matplotlib-users] "bad screen distance" tkinter error with German locale
Hi, thanks for the very quick response & fix. We were surprised, too, that we hadn't found more about this problem. We were put on the right track by this related report: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=395867 - Thanks also for the link to the other report. I'll see if I can come up with a nice example and submit it there. Cheers HB > On 06/30/2011 01:10 PM, Michael Droettboom wrote: > > I'm surprised this bug (which really lies in Tkinter) isn't more widely > > known -- searching the Python bug tracker revealed nothing. It would be > > great to follow-up there (with a standalone Tkinter-crashing example) if > > you're so inclined. > I did find this bug, which seems to be related. > > http://bugs.python.org/issue10647 > > Mike -- All of the data generated in your IT infrastructure is seriously valuable. Why? It contains a definitive record of application performance, security threats, fraudulent activity, and more. Splunk takes this data and makes sense of it. IT sense. And common sense. http://p.sf.net/sfu/splunk-d2d-c2 ___ Matplotlib-users mailing list Matplotlib-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/matplotlib-users
Re: [Matplotlib-users] adding scrollbars to plot embedded in tk
Hi Matt, a possible workaround seems to be to embed the figure's canvas in a second Tk canvas using canvas.create_window(...). The second (embedding) canvas handles the appropriate resizing & scrolling. I have attached a script below to demonstrate. Unfortunately, scrolling is rather sluggish, but it seems to work - the plot is not resized, and you can scroll around to different areas. Does that help? Cheers Hans On Wed, 31 Aug 2011 22:19:26 +0200, Benjamin Root wrote: > On Wed, Aug 31, 2011 at 2:55 PM, Matthew Hemke wrote: > >> I have a plot canvas added to a tk interface (python 2.7.2, matplotlib >> 1.0.1) according to the recipe here: >> >> >> http://matplotlib.sourceforge.net/examples/user_interfaces/embedding_in_tk.html >> >> When the window containing the plot is resized the plot shrinks, often >> leading to REALLY ugly, unreadable plots. >> >> I tried adding scrollbars to the canvas returned by get_tk_widget() and >> they connect as expected (using the yview method). Then, I set a >> scrollarea >> config option for the canvas. >> >> Everything seems to be working just like a tkinter canvas, but then when >> the window is resized, the plot still resizes and the scrollbars never >> activate. I was hoping the plot wouldn't resize and the scrollbars would >> activate to allow the user to scroll to see the appropriate part of the >> plot, while still keeping the plot looking pretty. >> >> Is there a way (besides editing backend_tkagg.py self.resize method) >> that >> would allow the scrollbars to work properly? >> >> If my question isn't clear, I can mock up some code, but it may be a bit >> lengthy, so if anyone can steer me in a better direction that would be >> great. >> >> Thanks, >> >> -Matt >> --- start of script --- from Tkinter import Tk, Frame, Canvas, Scrollbar from Tkconstants import NSEW, HORIZONTAL, EW, NS, ALL from matplotlib import pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg def doLargePlot(): from numpy.random import randn matrix = randn(100, 100) plt.pcolor(matrix) def getScrollingCanvas(frame): """ Adds a new canvas with scroll bars to the argument frame NB: uses grid layout @return: the newly created canvas """ frame.grid(sticky=NSEW) frame.rowconfigure(0, weight=1) frame.columnconfigure(0, weight=1) canvas = Canvas(frame) canvas.grid(row=0, column=0, sticky=NSEW) xScrollbar = Scrollbar(frame, orient=HORIZONTAL) yScrollbar = Scrollbar(frame) xScrollbar.grid(row=1, column=0, sticky=EW) yScrollbar.grid(row=0, column=1, sticky=NS) canvas.config(xscrollcommand=xScrollbar.set) xScrollbar.config(command=canvas.xview) canvas.config(yscrollcommand=yScrollbar.set) yScrollbar.config(command=canvas.yview) return canvas if __name__ == "__main__": root = Tk() root.rowconfigure(0, weight=1) root.columnconfigure(0, weight=1) frame = Frame(root) scrollC = getScrollingCanvas(frame) # use more dpi for bigger plot #figure = plt.figure(dpi=200) figure = plt.figure() mplCanvas = FigureCanvasTkAgg(figure, scrollC) canvas = mplCanvas.get_tk_widget() canvas.grid(sticky=NSEW) scrollC.create_window(0, 0, window=canvas) scrollC.config(scrollregion=scrollC.bbox(ALL)) doLargePlot() root.mainloop() --- end of script --- -- Special Offer -- Download ArcSight Logger for FREE! Finally, a world-class log management solution at an even better price-free! And you'll get a free "Love Thy Logs" t-shirt when you download Logger. Secure your free ArcSight Logger TODAY! http://p.sf.net/sfu/arcsisghtdev2dev ___ Matplotlib-users mailing list Matplotlib-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/matplotlib-users