-
-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Closed
Milestone
Description
Bug summary
a SpanSelector created with useblit=True causes an error when trying to save the figure as SVG. Reproducer is below, though I haven't tested all backends (only Agg and qtagg; both fail) or all output formats (only SVG, PDF, and PNG; PNG works and the other two don't). useblit=False allows SVG and PDF saving to work.
first reported at mne-tools/mne-python#13242 by @AasmaGupta, who did much of the initial isolation work. Tested on MPL 3.10.1 on Linux.
Code for reproduction
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.widgets import SpanSelector
def onselect(xmin, xmax, **kwargs):
pass
fig, ax = plt.subplots()
ax.plot(np.random.randn(100, 3))
span_selector = SpanSelector(
ax,
onselect,
"horizontal",
useblit=True,
)
fig.savefig("test.svg") # 'FigureCanvasSVG' object has no attribute 'copy_from_bbox'Actual outcome
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
File ~/Desktop/svgmwe.py:22
14 ax.plot(np.random.randn(100, 3))
15 span_selector = SpanSelector(
16 ax,
17 onselect,
18 "horizontal",
19 useblit=True,
20 )
---> 22 fig.savefig("test.svg") # 'FigureCanvasSVG' object has no attribute 'copy_from_bbox'
File /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/figure.py:3490, in Figure.savefig(self, fname, transparent, **kwargs)
3488 for ax in self.axes:
3489 _recursively_make_axes_transparent(stack, ax)
-> 3490 self.canvas.print_figure(fname, **kwargs)
File /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/backend_bases.py:2184, in FigureCanvasBase.print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)
2180 try:
2181 # _get_renderer may change the figure dpi (as vector formats
2182 # force the figure dpi to 72), so we need to set it again here.
2183 with cbook._setattr_cm(self.figure, dpi=dpi):
-> 2184 result = print_method(
2185 filename,
2186 facecolor=facecolor,
2187 edgecolor=edgecolor,
2188 orientation=orientation,
2189 bbox_inches_restore=_bbox_inches_restore,
2190 **kwargs)
2191 finally:
2192 if bbox_inches and restore_bbox:
File /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/backend_bases.py:2040, in FigureCanvasBase._switch_canvas_and_return_print_method.<locals>.<lambda>(*args, **kwargs)
2036 optional_kws = { # Passed by print_figure for other renderers.
2037 "dpi", "facecolor", "edgecolor", "orientation",
2038 "bbox_inches_restore"}
2039 skip = optional_kws - {*inspect.signature(meth).parameters}
-> 2040 print_method = functools.wraps(meth)(lambda *args, **kwargs: meth(
2041 *args, **{k: v for k, v in kwargs.items() if k not in skip}))
2042 else: # Let third-parties do as they see fit.
2043 print_method = meth
File /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/backends/backend_svg.py:1351, in FigureCanvasSVG.print_svg(self, filename, bbox_inches_restore, metadata)
1346 w, h = width * 72, height * 72
1347 renderer = MixedModeRenderer(
1348 self.figure, width, height, dpi,
1349 RendererSVG(w, h, fh, image_dpi=dpi, metadata=metadata),
1350 bbox_inches_restore=bbox_inches_restore)
-> 1351 self.figure.draw(renderer)
1352 renderer.finalize()
File /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/artist.py:94, in _finalize_rasterization.<locals>.draw_wrapper(artist, renderer, *args, **kwargs)
92 @wraps(draw)
93 def draw_wrapper(artist, renderer, *args, **kwargs):
---> 94 result = draw(artist, renderer, *args, **kwargs)
95 if renderer._rasterizing:
96 renderer.stop_rasterizing()
File /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/artist.py:71, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
68 if artist.get_agg_filter() is not None:
69 renderer.start_filter()
---> 71 return draw(artist, renderer)
72 finally:
73 if artist.get_agg_filter() is not None:
File /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/figure.py:3264, in Figure.draw(self, renderer)
3261 finally:
3262 self.stale = False
-> 3264 DrawEvent("draw_event", self.canvas, renderer)._process()
File /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/backend_bases.py:1189, in Event._process(self)
1187 def _process(self):
1188 """Process this event on ``self.canvas``, then unset ``guiEvent``."""
-> 1189 self.canvas.callbacks.process(self.name, self)
1190 self.guiEvent = None
File /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/cbook.py:366, in CallbackRegistry.process(self, s, *args, **kwargs)
364 except Exception as exc:
365 if self.exception_handler is not None:
--> 366 self.exception_handler(exc)
367 else:
368 raise
File /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/cbook.py:110, in _exception_printer(exc)
108 def _exception_printer(exc):
109 if _get_running_interactive_framework() in ["headless", None]:
--> 110 raise exc
111 else:
112 traceback.print_exc()
File /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/cbook.py:361, in CallbackRegistry.process(self, s, *args, **kwargs)
359 if func is not None:
360 try:
--> 361 func(*args, **kwargs)
362 # this does not capture KeyboardInterrupt, SystemExit,
363 # and GeneratorExit
364 except Exception as exc:
File /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/widgets.py:2167, in _SelectorWidget.update_background(self, event)
2165 stack.enter_context(artist._cm_set(visible=False))
2166 self.canvas.draw()
-> 2167 self.background = self.canvas.copy_from_bbox(self.ax.bbox)
2168 if needs_redraw:
2169 for artist in artists:
AttributeError: 'FigureCanvasSVG' object has no attribute 'copy_from_bbox'
Expected outcome
Figure is saved normally.
Additional information
No response
Operating system
Linux
Matplotlib Version
3.10.1
Matplotlib Backend
Agg and QtAgg
Python version
3.13.2
Jupyter version
n/a
Installation
conda
Metadata
Metadata
Assignees
Labels
No labels