r mosher
r mosher

Reputation: 91

Using Matplotlib with tkinter (TkAgg)

I have been having consistent problems running Matplotlib with tkinter. This happens with my code, and with others, including sample code that I have downloaded from the web, that presumably works for others.

The initial user warning from matplotlib.use('TkAgg') occurs when I use the IPython console, but not the standard Python console. I think this just means IPython is more verbose, because in either case the program crashes on canvas.show(). The complete code that I have been trying to run is from the Matplotlib web site:

#!/usr/bin/env python

import matplotlib
matplotlib.use('TkAgg')

from numpy import arange, sin, pi
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
# Implement the default mpl key bindings
from matplotlib.backend_bases import key_press_handler


from matplotlib.figure import Figure

import sys
if sys.version_info[0] < 3:
    import Tkinter as Tk
else:
    import tkinter as Tk

root = Tk.Tk()
root.wm_title("Embedding in TK")


f = Figure(figsize=(5, 4), dpi=100)
a = f.add_subplot(111)
t = arange(0.0, 3.0, 0.01)
s = sin(2*pi*t)

a.plot(t, s)


# A tk.DrawingArea
canvas = FigureCanvasTkAgg(f, master=root)
canvas.show()
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)

toolbar = NavigationToolbar2TkAgg(canvas, root)
toolbar.update()
canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)


def on_key_event(event):
    print('you pressed %s' % event.key)
    key_press_handler(event, canvas, toolbar)

canvas.mpl_connect('key_press_event', on_key_event)


def _quit():
    root.quit()     # Stops mainloop
    root.destroy()  # This is necessary on Windows to prevent
                    # Fatal Python Error: PyEval_RestoreThread: NULL tstate

button = Tk.Button(master=root, text='Quit', command=_quit)
button.pack(side=Tk.BOTTOM)

Tk.mainloop()
# If you put root.destroy() here, it will cause an error if
# the window is closed with the window manager.

Using the debugger I follow canvas.show into tkinter (backend_tkagg.py):

def draw(self):
    FigureCanvasAgg.draw(self)
    tkagg.blit(self._tkphoto, self.renderer._renderer, colormode=2)
    self._master.update_idletasks()

I step over FigureCanvasAgg.draw ok and step into tkagg.blit... notice none of the data passed to tkagg.blit is application data. This call takes me to tkagg.py, namely:

def blit(photoimage, aggimage, bbox=None, colormode=1):
    tk = photoimage.tk

    if bbox is not None:
        bbox_array = bbox.__array__()
    else:
        bbox_array = None
    data = np.asarray(aggimage)
    try:
        tk.call("PyAggImagePhoto", photoimage,
            id(data), colormode, id(bbox_array))
    except Tk.TclError:
        try:
            try:
                _tkagg.tkinit(tk.interpaddr(), 1)
            except AttributeError:
                _tkagg.tkinit(id(tk), 0)
            tk.call("PyAggImagePhoto", photoimage,
                    id(data), colormode, id(bbox_array))
        except (ImportError, AttributeError, Tk.TclError):
            raise

where it fails repeatedly on the tk.call, which I think is a call into Tcl.

I modified the code here to catch the TclError as a variable so I could inspect it in the debugger. It said: tclErr: invalid command name "PyAggImagePhoto"

What do I make of this?

Upvotes: 9

Views: 66527

Answers (2)

BTMcG
BTMcG

Reputation: 21

This code worked for me once I replaced

NavigationToolbar2TkAgg

With

NavigationToolbar2Tk

And also replaced

canvas.show()

With

canvas.draw()

Note, I use Anaconda 4.8.3, Matplotlib 3.2.1, Tkinter 8.6, and Windows 10.

Upvotes: 2

tacaswell
tacaswell

Reputation: 87376

To summarize:

Upvotes: 0

Related Questions