Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 772a315

Browse filesBrowse files
authored
Merge pull request #16220 from anntzer/3dp
Fix interaction with unpickled 3d plots.
2 parents 7db82ea + 08236e7 commit 772a315
Copy full SHA for 772a315

File tree

Expand file treeCollapse file tree

5 files changed

+57
-15
lines changed
Filter options
Expand file treeCollapse file tree

5 files changed

+57
-15
lines changed
+8Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Canvas's callback registry now stored on Figure
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
The canonical location of the `~.cbook.CallbackRegistry` used to
5+
handle Figure/Canvas events has been moved from the Canvas to the
6+
Figure. This change should be transparent to almost all users,
7+
however if you are swapping switching the Figure out from on top of a
8+
Canvas or visa versa you may see a change in behavior.

‎lib/matplotlib/backend_bases.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backend_bases.py
+4-2Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1713,8 +1713,6 @@ def __init__(self, figure):
17131713
figure.set_canvas(self)
17141714
self.figure = figure
17151715
self.manager = None
1716-
# a dictionary from event name to a dictionary that maps cid->func
1717-
self.callbacks = cbook.CallbackRegistry()
17181716
self.widgetlock = widgets.LockDraw()
17191717
self._button = None # the button pressed
17201718
self._key = None # the key pressed
@@ -1725,6 +1723,10 @@ def __init__(self, figure):
17251723
self.toolbar = None # NavigationToolbar2 will set me
17261724
self._is_idle_drawing = False
17271725

1726+
@property
1727+
def callbacks(self):
1728+
return self.figure._canvas_callbacks
1729+
17281730
@classmethod
17291731
@functools.lru_cache()
17301732
def _fix_ipython_backend2gui(cls):

‎lib/matplotlib/cbook/__init__.py

Copy file name to clipboardExpand all lines: lib/matplotlib/cbook/__init__.py
+33-7Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,16 @@ def __hash__(self):
104104
return hash(self._obj)
105105

106106

107+
def _weak_or_strong_ref(func, callback):
108+
"""
109+
Return a `WeakMethod` wrapping *func* if possible, else a `_StrongRef`.
110+
"""
111+
try:
112+
return weakref.WeakMethod(func, callback)
113+
except TypeError:
114+
return _StrongRef(func)
115+
116+
107117
class CallbackRegistry:
108118
"""
109119
Handle registering and disconnecting for a set of signals and callbacks:
@@ -157,21 +167,37 @@ def __init__(self, exception_handler=_exception_printer):
157167
self.callbacks = {}
158168
self._cid_gen = itertools.count()
159169
self._func_cid_map = {}
170+
# A hidden variable that marks cids that need to be pickled.
171+
self._pickled_cids = set()
160172

161173
def __getstate__(self):
162-
# In general, callbacks may not be pickled, so we just drop them.
163-
return {**vars(self), "callbacks": {}, "_func_cid_map": {}}
174+
return {
175+
**vars(self),
176+
# In general, callbacks may not be pickled, so we just drop them,
177+
# unless directed otherwise by self._pickled_cids.
178+
"callbacks": {s: {cid: proxy() for cid, proxy in d.items()
179+
if cid in self._pickled_cids}
180+
for s, d in self.callbacks.items()},
181+
# It is simpler to reconstruct this from callbacks in __setstate__.
182+
"_func_cid_map": None,
183+
}
184+
185+
def __setstate__(self, state):
186+
vars(self).update(state)
187+
self.callbacks = {
188+
s: {cid: _weak_or_strong_ref(func, self._remove_proxy)
189+
for cid, func in d.items()}
190+
for s, d in self.callbacks.items()}
191+
self._func_cid_map = {
192+
s: {proxy: cid for cid, proxy in d.items()}
193+
for s, d in self.callbacks.items()}
164194

165195
def connect(self, s, func):
166196
"""Register *func* to be called when signal *s* is generated."""
167197
self._func_cid_map.setdefault(s, {})
168-
try:
169-
proxy = weakref.WeakMethod(func, self._remove_proxy)
170-
except TypeError:
171-
proxy = _StrongRef(func)
198+
proxy = _weak_or_strong_ref(func, self._remove_proxy)
172199
if proxy in self._func_cid_map[s]:
173200
return self._func_cid_map[s][proxy]
174-
175201
cid = next(self._cid_gen)
176202
self._func_cid_map[s][proxy] = cid
177203
self.callbacks.setdefault(s, {})

‎lib/matplotlib/figure.py

Copy file name to clipboardExpand all lines: lib/matplotlib/figure.py
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2322,6 +2322,10 @@ def __init__(self,
23222322
super().__init__()
23232323

23242324
self.callbacks = cbook.CallbackRegistry()
2325+
# Callbacks traditionally associated with the canvas (and exposed with
2326+
# a proxy property), but that actually need to be on the figure for
2327+
# pickling.
2328+
self._canvas_callbacks = cbook.CallbackRegistry()
23252329

23262330
if figsize is None:
23272331
figsize = mpl.rcParams['figure.figsize']

‎lib/mpl_toolkits/mplot3d/axes3d.py

Copy file name to clipboardExpand all lines: lib/mpl_toolkits/mplot3d/axes3d.py
+8-6Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,14 @@ def __init__(
118118
self._zcid = None
119119

120120
self.mouse_init()
121-
self.figure.canvas.mpl_connect(
122-
'motion_notify_event', self._on_move),
123-
self.figure.canvas.mpl_connect(
124-
'button_press_event', self._button_press),
125-
self.figure.canvas.mpl_connect(
126-
'button_release_event', self._button_release),
121+
self.figure.canvas.callbacks._pickled_cids.update({
122+
self.figure.canvas.mpl_connect(
123+
'motion_notify_event', self._on_move),
124+
self.figure.canvas.mpl_connect(
125+
'button_press_event', self._button_press),
126+
self.figure.canvas.mpl_connect(
127+
'button_release_event', self._button_release),
128+
})
127129
self.set_top_view()
128130

129131
self.patch.set_linewidth(0)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.