Skip to content

Commit bef9802

Browse files
committed
API: overhaul how active figures are tracked
- switch from list to dict keyed on Figures - go with pyplot style numbering - improve the clean up logic to try and not leak refs
1 parent 7f18073 commit bef9802

File tree

1 file changed

+23
-16
lines changed

1 file changed

+23
-16
lines changed

Diff for: mpl_gui/__init__.py

+23-16
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import functools
1717
import logging
1818
import warnings
19-
from weakref import WeakKeyDictionary
19+
import weakref
2020

2121
from matplotlib.backend_bases import FigureCanvasBase as _FigureCanvasBase
2222

@@ -70,7 +70,7 @@ def show(figs, *, block=None, timeout=0):
7070
if fig.canvas.manager is not None:
7171
managers.append(fig.canvas.manager)
7272
else:
73-
managers.append(promote_figure(fig))
73+
managers.append(promote_figure(fig, num=None))
7474

7575
if block is None:
7676
block = not is_interactive()
@@ -117,33 +117,41 @@ def __init__(self, *, block=None, timeout=0, prefix="Figure "):
117117
# settings stashed to set defaults on show
118118
self._timeout = timeout
119119
self._block = block
120-
# Settings / state to control the default figure label
121-
self._fig_to_number = WeakKeyDictionary()
122-
self._prefix = prefix
123120
# the canonical location for storing the Figures this registry owns.
124-
# any additional views must never include a figure not in the list but
121+
# any additional views must never include a figure that is not a key but
125122
# may omit figures
126-
self.figures = []
123+
self._fig_to_number = dict()
124+
# Settings / state to control the default figure label
125+
self._prefix = prefix
126+
127+
@property
128+
def figures(self):
129+
return tuple(self._fig_to_number)
127130

128131
def _register_fig(self, fig):
129132
# if the user closes the figure by any other mechanism, drop our
130133
# reference to it. This is important for getting a "pyplot" like user
131134
# experience
132-
fig.canvas.mpl_connect(
133-
"close_event",
134-
lambda e: self.figures.remove(fig) if fig in self.figures else None,
135-
)
136-
# hold a hard reference to the figure.
137-
self.figures.append(fig)
135+
def registry_cleanup(fig_wr):
136+
fig = fig_wr()
137+
if fig is not None:
138+
if fig.canvas is not None:
139+
fig.canvas.mpl_disconnect(cid)
140+
self.close(fig)
141+
142+
fig_wr = weakref.ref(fig)
143+
cid = fig.canvas.mpl_connect("close_event", lambda e: registry_cleanup(fig_wr))
138144
# Make sure we give the figure a quasi-unique label. We will never set
139145
# the same label twice, but will not over-ride any user label (but
140146
# empty string) on a Figure so if they provide duplicate labels, change
141147
# the labels under us, or provide a label that will be shadowed in the
142148
# future it will be what it is.
143-
fignum = max(self._fig_to_number.values(), default=0) + 1
149+
fignum = max(self._fig_to_number.values(), default=-1) + 1
144150
if fig.get_label() == "":
145151
fig.set_label(f"{self._prefix}{fignum:d}")
146152
self._fig_to_number[fig] = fignum
153+
if is_interactive():
154+
promote_figure(fig, num=fignum)
147155
return fig
148156

149157
@property
@@ -302,8 +310,7 @@ def close(self, val):
302310
# disconnect canvas from figure
303311
_FigureCanvasBase(figure=fig)
304312
assert fig.canvas.manager is None
305-
if fig in self.figures:
306-
self.figures.remove(fig)
313+
self._fig_to_number.pop(fig, None)
307314

308315

309316
class FigureContext(FigureRegistry):

0 commit comments

Comments
 (0)