Matplotlib: How to Show a Figure That Has Been Closed

Matplotlib: how to show a figure that has been closed

When plt.close is called on a figure instance, what is actually destroyed is the graphical interface (the FigureManager) that is used to show the figure on-screen (see comment by JoeKington at Matplotlib: re-open a closed figure?). So the figure instance still exists and has not been destroyed. To show the figure on-screen again, we would have to reconstruct, in some way, an interface to replace the one that has been destroyed when calling plt.close(fig).

This can be done by simply creating a new figure with plt.figure(), "stealing" its manager, and use it to display the figure that we want to show on-screen. Alternatively, it is possible to reconstruct manually an interface to display the figure with a GUI Toolkit. I provide an example with PySide using the Qt4Agg backend. Moreover, there is a nice example that shows how this can be done with Tkinter (TkAgg) here : http://matplotlib.org/examples/user_interfaces/embedding_in_tk.html (I've tested this approach also and it works).

Dummy figure approach:

This solution is based on how to close a show() window but keep the figure alive? and Obtaining the figure manager via the OO interface in Matplotlib. The GUI toolkit that is used to construct the graphical interface for showing the figure on-screen depends on the backend that is used by matplotlib. If the backend used is TkAgg, TkInter will give some warning in Python 2.7 that can be ignored (see this post on python bug tracker).

import matplotlib.pyplot as plt

def new_figure():

fig = plt.figure()
plt.plot([0, 1], [2, 3])
plt.close(fig)
return fig

def show_figure(fig):

# create a dummy figure and use its
# manager to display "fig"

dummy = plt.figure()
new_manager = dummy.canvas.manager
new_manager.canvas.figure = fig
fig.set_canvas(new_manager.canvas)

if __name__ == '__main__':

fig = new_figure()
show_figure(fig)

plt.show()

Pyside approach:

This consists in reconstructing a GUI with a new canvas and toolbar to display the fig instance on-screen.

Important Note: The code below must be executed in a new dedicated Python console (press F6) if run from Spyder, since Spyder is also a Qt application that starts it's own QApplication (see PySide Qt script doesn't launch from Spyder but works from shell).

import matplotlib
matplotlib.use('Qt4Agg')
matplotlib.rcParams['backend.qt4']='PySide'
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT
import matplotlib.pyplot as plt

from PySide import QtGui
import sys

def new_figure():

fig = plt.figure()
plt.plot([0, 1], [2, 3])
plt.close(fig)
return fig

class myFigCanvas(QtGui.QWidget):

def __init__(self, fig, parent=None):
super(myFigCanvas, self).__init__(parent)

#---- create new canvas and toolbar --

canvas = FigureCanvasQTAgg(fig)
toolbar = NavigationToolbar2QT(canvas, self)

#---- setup layout of GUI ----

grid = QtGui.QGridLayout()
grid.addWidget(canvas, 0, 0)
grid.addWidget(toolbar, 1, 0)

self.setLayout(grid)

if __name__ == '__main__':

app = QtGui.QApplication(sys.argv)

fig = new_figure()
new_canvas = myFigCanvas(fig)
new_canvas.show()

sys.exit(app.exec_())

which results in:

Sample Image

Detect which figure was closed with Matplotlib

The evt that is passed to your handler has a member canvas, which in turn has a member figure, which of course is the figure the event refers to.

So if you want to remove that figure from your list, you would do

figs.remove(evt.canvas.figure)

If you want to get the figure number, you access

evt.canvas.figure.number

and you could get the name from

evt.canvas.figure._label

However, the leading _ probably means you shouldn't rely on that.

view and then close the figure automatically in matplotlib?

Documentation on pyplot.show() reads:

matplotlib.pyplot.show(*args, **kw)

Display a figure. When running in ipython with its pylab mode, display
all figures and return to the ipython prompt.

In non-interactive mode, display all figures and block until the figures have been closed; in interactive mode it has no effect
unless figures were created prior to a change from non-interactive to
interactive mode (not recommended). In that case it displays the
figures but does not block.

A single experimental keyword argument, block, may be set to True or False to override the blocking behavior described above.

So the solution is this:

plt.show(block=False)
plt.pause(3)
plt.close()

How to detect that an axis belong to a window that has been closed in matplotlib

Why not just connect a callback to the "close_event"? You could either add a ax.has_been_closed flag to the axes object or your class itself. (There are probably even cleaner solutions than a "has_been_closed" flag, depending on exactly what you're doing... The callback function can be anything.)

import matplotlib.pyplot as plt

def on_close(event):
event.canvas.figure.axes[0].has_been_closed = True
print 'Closed Figure'

fig = plt.figure()
ax = fig.add_subplot(111)
ax.has_been_closed = False
ax.plot(range(10))

fig.canvas.mpl_connect('close_event', on_close)

plt.show()

print ax.has_been_closed

Edit: (Expanding on my comment below) If the OSX backend end doesn't properly implement a close event, you could also do something like this:

import matplotlib.pyplot as plt

def has_been_closed(ax):
fig = ax.figure.canvas.manager
active_fig_managers = plt._pylab_helpers.Gcf.figs.values()
return fig not in active_fig_managers

fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(10))

print has_been_closed(ax)

plt.show()

print has_been_closed(ax)

How do I tell matplotlib that I am done with a plot?

You can use figure to create a new plot, for example, or use close after the first plot.

Matplotlib: re-open a closed figure?

Yes, closing the figure deletes it completely.

Matplotlib pyplot show() doesn't work once closed

It might be from a bug in previous versions of matplotlib. I was having a similar problem when I issued sequential show() commands -- only the first would show (and stay); but, when I updated matplotlib to 1.0.1 the problem went away.



Related Topics



Leave a reply



Submit