From 9e6f112a783753c112253b222d4bff69d2328876 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Wed, 18 Mar 2020 12:50:17 +0100 Subject: [PATCH] Destroy figures by manager instance, not by number. This avoids destroying the wrong figure when two managers share the same number. (I don't think this can be easily tested as what really matters is UI closing?) --- lib/matplotlib/_pylab_helpers.py | 20 ++++++++++++++------ lib/matplotlib/backends/_backend_tk.py | 2 +- lib/matplotlib/backends/backend_gtk3.py | 6 ++---- lib/matplotlib/backends/backend_macosx.py | 2 +- lib/matplotlib/backends/backend_nbagg.py | 2 +- lib/matplotlib/backends/backend_qt5.py | 2 +- lib/matplotlib/backends/backend_wx.py | 2 +- lib/matplotlib/pyplot.py | 2 +- 8 files changed, 22 insertions(+), 16 deletions(-) diff --git a/lib/matplotlib/_pylab_helpers.py b/lib/matplotlib/_pylab_helpers.py index 57e6d985af22..4cb8be0a14f8 100644 --- a/lib/matplotlib/_pylab_helpers.py +++ b/lib/matplotlib/_pylab_helpers.py @@ -45,14 +45,23 @@ def get_fig_manager(cls, num): @classmethod def destroy(cls, num): """ - Destroy figure number *num*. + Destroy manager *num* -- either a manager instance or a manager number. In the interactive backends, this is bound to the window "destroy" and "delete" events. + + It is recommended to pass a manager instance, to avoid confusion when + two managers share the same number. """ - if not cls.has_fignum(num): - return - manager = cls.figs.pop(num) + if all(hasattr(num, attr) for attr in ["num", "_cidgcf", "destroy"]): + manager = num + if cls.figs.get(manager.num) is manager: + cls.figs.pop(manager.num) + else: + try: + manager = cls.figs.pop(num) + except KeyError: + return manager.canvas.mpl_disconnect(manager._cidgcf) manager.destroy() gc.collect(1) @@ -62,8 +71,7 @@ def destroy_fig(cls, fig): """Destroy figure *fig*.""" canvas = getattr(fig, "canvas", None) manager = getattr(canvas, "manager", None) - num = getattr(manager, "num", None) - cls.destroy(num) + cls.destroy(manager) @classmethod def destroy_all(cls): diff --git a/lib/matplotlib/backends/_backend_tk.py b/lib/matplotlib/backends/_backend_tk.py index 6556d104f382..a982385a87a2 100644 --- a/lib/matplotlib/backends/_backend_tk.py +++ b/lib/matplotlib/backends/_backend_tk.py @@ -456,7 +456,7 @@ def show(self): if not self._shown: def destroy(*args): self.window = None - Gcf.destroy(self.num) + Gcf.destroy(self) self.canvas._tkcanvas.bind("", destroy) self.window.deiconify() else: diff --git a/lib/matplotlib/backends/backend_gtk3.py b/lib/matplotlib/backends/backend_gtk3.py index 0e8dc97a48d8..1f9408b0668a 100644 --- a/lib/matplotlib/backends/backend_gtk3.py +++ b/lib/matplotlib/backends/backend_gtk3.py @@ -367,10 +367,8 @@ def add_widget(child): self.window.set_default_size(w, h) - def destroy(*args): - Gcf.destroy(num) - self.window.connect("destroy", destroy) - self.window.connect("delete_event", destroy) + self.window.connect("destroy", lambda *args: Gcf.destroy(self)) + self.window.connect("delete_event", lambda *args: Gcf.destroy(self)) if mpl.is_interactive(): self.window.show() self.canvas.draw_idle() diff --git a/lib/matplotlib/backends/backend_macosx.py b/lib/matplotlib/backends/backend_macosx.py index 31bf8a4cb947..84d2ec11492c 100644 --- a/lib/matplotlib/backends/backend_macosx.py +++ b/lib/matplotlib/backends/backend_macosx.py @@ -106,7 +106,7 @@ def __init__(self, canvas, num): self.canvas.draw_idle() def close(self): - Gcf.destroy(self.num) + Gcf.destroy(self) class NavigationToolbar2Mac(_macosx.NavigationToolbar2, NavigationToolbar2): diff --git a/lib/matplotlib/backends/backend_nbagg.py b/lib/matplotlib/backends/backend_nbagg.py index c61d2a13d86b..49e3d514ad03 100644 --- a/lib/matplotlib/backends/backend_nbagg.py +++ b/lib/matplotlib/backends/backend_nbagg.py @@ -231,7 +231,7 @@ def new_figure_manager_given_figure(num, figure): if is_interactive(): manager.show() figure.canvas.draw_idle() - canvas.mpl_connect('close_event', lambda event: Gcf.destroy(num)) + canvas.mpl_connect('close_event', lambda event: Gcf.destroy(manager)) return manager @staticmethod diff --git a/lib/matplotlib/backends/backend_qt5.py b/lib/matplotlib/backends/backend_qt5.py index 46b684bba928..5c657bf5b2a2 100644 --- a/lib/matplotlib/backends/backend_qt5.py +++ b/lib/matplotlib/backends/backend_qt5.py @@ -590,7 +590,7 @@ def _widgetclosed(self): return self.window._destroying = True try: - Gcf.destroy(self.num) + Gcf.destroy(self) except AttributeError: pass # It seems that when the python session is killed, diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index e48e7cf45f29..e8635df49aa5 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -1007,7 +1007,7 @@ def _onClose(self, event): _log.debug("%s - onClose()", type(self)) self.canvas.close_event() self.canvas.stop_event_loop() - Gcf.destroy(self.num) + Gcf.destroy(self) # self.Destroy() def GetToolBar(self): diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 63a267ae4583..bce356516fdd 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -690,7 +690,7 @@ def close(fig=None): if figManager is None: return else: - _pylab_helpers.Gcf.destroy(figManager.num) + _pylab_helpers.Gcf.destroy(figManager) elif fig == 'all': _pylab_helpers.Gcf.destroy_all() elif isinstance(fig, int):