embed matplotlib Figure into Tkinter Canvas

Dear All,

I am trying to embed a matplotlib Figure into a Tkinter Canvas. The problem is, when I change the content of the figure (e.g. I delete and create new axes) the new items are plotted but the old items are not deleted. The old items disappear immediately when I resize the window.

I have checked that the elements of Tkinter do their job: the Canvas that contains my figure properly refreshes itself etc, so this is an issue with matplotlib. A similar problem happens indeed using matplotlib with pylab from e.g. ipython -- there one solves the problem by calling the method show() of pylab.figure. In the present case I cannot do that because I have to use matplotlib.figure.Figure instead of matplotlib.figure, and the former lacks the show() method.

I googled a lot for this problem and, while many people discusses refreshing issues, I did not find the solution that I need. Please find below my short code for reference. I may be missing understanding of how this FigureCanvasTkAgg function works: as far as I see, it connects the Canvas of Tkinter (that is the widget in the application) to the real matplotlib.figure.Figure that I want to plot.

Any help will be greatly appreciated.

Best regards,
Andrea

···

################################################################################
# CODE BEGINS
################################################################################
#!/usr/bin/env python

import sys
import numpy as N
import Tkinter
import matplotlib, matplotlib.figure
# Set the backend for matplotlib.
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg

class App:
    def __init__(self,master):

        # Set the properties of the whole window.
        master.config(background="gray")
        master.wm_title("PaperPlot")

        # Create a figure and link it to the Tk canvas.
        self.f = matplotlib.figure.Figure(figsize=(3.46, 2.14), frameon=False, dpi=200)

        # This canvas links the figure to the Tk canvas.
        self.canvas = FigureCanvasTkAgg(self.f, master=master)
        self.canvas._tkcanvas.config(background="#c0c0c0", borderwidth=0, highlightthickness=0)
        self.canvas.draw()

        # The Tk canvas.
        self.tkcanvas = self.canvas.get_tk_widget()
        self.tkcanvas.pack(fill=Tkinter.X)

        # A frame that contains the buttons.
        frame0 = Tkinter.Frame(master)
        frame0.pack(fill=Tkinter.X, pady=3)

        # Buttons.
        self.buttonNew = Tkinter.Button(frame0,width=10,text="New",command=self.newPlot)
        self.buttonNew.pack(side=Tkinter.LEFT)

        self.buttonRun = Tkinter.Button(frame0,width=10,text="Run",command=self.runText)
        self.buttonRun.pack(side=Tkinter.LEFT)
        
        self.buttonQuit = Tkinter.Button(frame0,width=10,text='Quit',command=frame0.quit)
        self.buttonQuit.pack(side=Tkinter.RIGHT)

        self.buttonPrint = Tkinter.Button(frame0,width=10,text="Print",command=self.printPlot)
        self.buttonPrint.pack(side=Tkinter.RIGHT)

        # A frame that contains the source code of the plot.
        frame1 = Tkinter.Frame(master)
        frame1.pack(fill=Tkinter.X)
        
        # Text widget with scrollbar that fills the frame and shows the code.
        self.scrollbar = Tkinter.Scrollbar(frame1)
        self.scrollbar.pack(side=Tkinter.RIGHT, fill=Tkinter.Y)
        self.editor = Tkinter.Text(frame1, wrap=Tkinter.WORD, height=20, background="white", yscrollcommand=self.scrollbar.set)
        self.editor.pack(fill=Tkinter.X)
        self.editor.config(highlightthickness=0)
        self.scrollbar.config(command=self.editor.yview)

        # Load the file with the source code of the plot.
        # theFile = open("plot.py", "r")
        # self.editor.insert(Tkinter.END, theFile.read())
        # theFile.close()

        self.i = 1
        
    def newPlot(self):
        self.f.clear()
  # HERE SOMETHING IS NEEDED TO FORCE THE FIGURE TO REDRAW!
        self.a = self.f.add_axes([0.1*self.i, 0.10*self.i, 0.4, 0.74])
        self.i = self.i + 1
        self.canvas.draw()
                
    def runText(self):
        exec(self.editor.get("0.0", Tkinter.END))
        self.canvas.draw()
       
    def printPlot(self):
        self.f.savefig("plot.png", dpi=300)
        self.f.savefig("plot.pdf")
        theText = self.editor.get("0.0", Tkinter.END)
        theFile = open("plot.py", "w")
        print >> theFile, theText
        theFile.close()

# Create the main window of the program.
root = Tkinter.Tk()

# The window is not resizable.
root.resizable(0,0)

# The object that organizes all the widgets and the data in the program.
app = App(root)

# Start the program.
root.mainloop()

################################################################################
# CODE ENDS
################################################################################

Just call fig.canvas.draw() after clearing the old items. This is
what the resize is doing.

JDH

···

On Fri, Aug 13, 2010 at 1:42 AM, <gurnemanz@...2904...> wrote:

Dear All,

I am trying to embed a matplotlib Figure into a Tkinter Canvas. The problem is, when I change the content of the figure (e.g. I delete and create new axes) the new items are plotted but the old items are not deleted. The old items disappear immediately when I resize the window.

Dear John,

thank you for your suggestion. Unfortunately, I have just tried that and I am positive that it does not work.

It works only if the option "frameon" of the figure is True, which is not what I want in this case (I have to produce a pdf with transparent background).

Regards,
Andrea

···

On Aug 13, 2010, at 17:21 , John Hunter wrote:

On Fri, Aug 13, 2010 at 1:42 AM, <gurnemanz@...2904...> wrote:

Dear All,

I am trying to embed a matplotlib Figure into a Tkinter Canvas. The problem is, when I change the content of the figure (e.g. I delete and create new axes) the new items are plotted but the old items are not deleted. The old items disappear immediately when I resize the window.

Just call fig.canvas.draw() after clearing the old items. This is
what the resize is doing.

JDH

2010/8/13 Andrea Tomadin <gurnemanz@...2904...>:

Dear John,

thank you for your suggestion. Unfortunately, I have just tried that and I am positive that it does not work.

It works only if the option "frameon" of the figure is True, which is not what I want in this case (I have to produce a pdf with transparent background).

Indeed the code of draw() and resize() do something different.

While draw() only blits to the tk image, resize() creates a new Tk
image. I believe the bug is real and somewhere buried in tkagg.

For now, you have three options: Wait until it's fixed in svn, or
monkey-patch using the code of resize(). You can also pass in a dummy
event to .resize() with two attributes, .width and .height, being the
['width'] and ['height'] of the drawing Tk Canvas object, i.e. of your
.canvas._tkcanvas.

Furthermore, does anyone know what the self.resize_event() call in
.resize() is doing? I'm on 8626. Looks like a missed
rename-and-back-up error to me. I don't want to checkout until I'm
familiar with svn to not loose my changes.

Friedrich.