Title: [Bug]: figures with SpanSelector(..., useblit=True) can't be saved to SVG or PDF · Issue #30485 · matplotlib/matplotlib · GitHub
Open Graph Title: [Bug]: figures with SpanSelector(..., useblit=True) can't be saved to SVG or PDF · Issue #30485 · matplotlib/matplotlib
X Title: [Bug]: figures with SpanSelector(..., useblit=True) can't be saved to SVG or PDF · Issue #30485 · matplotlib/matplotlib
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, a...
Open Graph 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)...
X 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 f...
Opengraph URL: https://github.com/matplotlib/matplotlib/issues/30485
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"[Bug]: figures with SpanSelector(..., useblit=True) can't be saved to SVG or PDF","articleBody":"### Bug summary\n\na 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.\n\nfirst reported at https://github.com/mne-tools/mne-python/issues/13242 by @AasmaGupta, who did much of the initial isolation work. Tested on MPL 3.10.1 on Linux.\n\n### Code for reproduction\n\n```Python\nimport numpy as np\nimport matplotlib\n\nmatplotlib.use(\"Agg\")\nimport matplotlib.pyplot as plt\nfrom matplotlib.widgets import SpanSelector\n\n\ndef onselect(xmin, xmax, **kwargs):\n pass\n\n\nfig, ax = plt.subplots()\nax.plot(np.random.randn(100, 3))\nspan_selector = SpanSelector(\n ax,\n onselect,\n \"horizontal\",\n useblit=True,\n)\n\nfig.savefig(\"test.svg\") # 'FigureCanvasSVG' object has no attribute 'copy_from_bbox'\n```\n\n### Actual outcome\n\n```\n---------------------------------------------------------------------------\nAttributeError Traceback (most recent call last)\nFile ~/Desktop/svgmwe.py:22\n 14 ax.plot(np.random.randn(100, 3))\n 15 span_selector = SpanSelector(\n 16 ax,\n 17 onselect,\n 18 \"horizontal\",\n 19 useblit=True,\n 20 )\n---\u003e 22 fig.savefig(\"test.svg\") # 'FigureCanvasSVG' object has no attribute 'copy_from_bbox'\n\nFile /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/figure.py:3490, in Figure.savefig(self, fname, transparent, **kwargs)\n 3488 for ax in self.axes:\n 3489 _recursively_make_axes_transparent(stack, ax)\n-\u003e 3490 self.canvas.print_figure(fname, **kwargs)\n\nFile /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)\n 2180 try:\n 2181 # _get_renderer may change the figure dpi (as vector formats\n 2182 # force the figure dpi to 72), so we need to set it again here.\n 2183 with cbook._setattr_cm(self.figure, dpi=dpi):\n-\u003e 2184 result = print_method(\n 2185 filename,\n 2186 facecolor=facecolor,\n 2187 edgecolor=edgecolor,\n 2188 orientation=orientation,\n 2189 bbox_inches_restore=_bbox_inches_restore,\n 2190 **kwargs)\n 2191 finally:\n 2192 if bbox_inches and restore_bbox:\n\nFile /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/backend_bases.py:2040, in FigureCanvasBase._switch_canvas_and_return_print_method.\u003clocals\u003e.\u003clambda\u003e(*args, **kwargs)\n 2036 optional_kws = { # Passed by print_figure for other renderers.\n 2037 \"dpi\", \"facecolor\", \"edgecolor\", \"orientation\",\n 2038 \"bbox_inches_restore\"}\n 2039 skip = optional_kws - {*inspect.signature(meth).parameters}\n-\u003e 2040 print_method = functools.wraps(meth)(lambda *args, **kwargs: meth(\n 2041 *args, **{k: v for k, v in kwargs.items() if k not in skip}))\n 2042 else: # Let third-parties do as they see fit.\n 2043 print_method = meth\n\nFile /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)\n 1346 w, h = width * 72, height * 72\n 1347 renderer = MixedModeRenderer(\n 1348 self.figure, width, height, dpi,\n 1349 RendererSVG(w, h, fh, image_dpi=dpi, metadata=metadata),\n 1350 bbox_inches_restore=bbox_inches_restore)\n-\u003e 1351 self.figure.draw(renderer)\n 1352 renderer.finalize()\n\nFile /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/artist.py:94, in _finalize_rasterization.\u003clocals\u003e.draw_wrapper(artist, renderer, *args, **kwargs)\n 92 @wraps(draw)\n 93 def draw_wrapper(artist, renderer, *args, **kwargs):\n---\u003e 94 result = draw(artist, renderer, *args, **kwargs)\n 95 if renderer._rasterizing:\n 96 renderer.stop_rasterizing()\n\nFile /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/artist.py:71, in allow_rasterization.\u003clocals\u003e.draw_wrapper(artist, renderer)\n 68 if artist.get_agg_filter() is not None:\n 69 renderer.start_filter()\n---\u003e 71 return draw(artist, renderer)\n 72 finally:\n 73 if artist.get_agg_filter() is not None:\n\nFile /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/figure.py:3264, in Figure.draw(self, renderer)\n 3261 finally:\n 3262 self.stale = False\n-\u003e 3264 DrawEvent(\"draw_event\", self.canvas, renderer)._process()\n\nFile /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/backend_bases.py:1189, in Event._process(self)\n 1187 def _process(self):\n 1188 \"\"\"Process this event on ``self.canvas``, then unset ``guiEvent``.\"\"\"\n-\u003e 1189 self.canvas.callbacks.process(self.name, self)\n 1190 self.guiEvent = None\n\nFile /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/cbook.py:366, in CallbackRegistry.process(self, s, *args, **kwargs)\n 364 except Exception as exc:\n 365 if self.exception_handler is not None:\n--\u003e 366 self.exception_handler(exc)\n 367 else:\n 368 raise\n\nFile /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/cbook.py:110, in _exception_printer(exc)\n 108 def _exception_printer(exc):\n 109 if _get_running_interactive_framework() in [\"headless\", None]:\n--\u003e 110 raise exc\n 111 else:\n 112 traceback.print_exc()\n\nFile /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/cbook.py:361, in CallbackRegistry.process(self, s, *args, **kwargs)\n 359 if func is not None:\n 360 try:\n--\u003e 361 func(*args, **kwargs)\n 362 # this does not capture KeyboardInterrupt, SystemExit,\n 363 # and GeneratorExit\n 364 except Exception as exc:\n\nFile /opt/mambaforge/envs/mnedev/lib/python3.13/site-packages/matplotlib/widgets.py:2167, in _SelectorWidget.update_background(self, event)\n 2165 stack.enter_context(artist._cm_set(visible=False))\n 2166 self.canvas.draw()\n-\u003e 2167 self.background = self.canvas.copy_from_bbox(self.ax.bbox)\n 2168 if needs_redraw:\n 2169 for artist in artists:\n\nAttributeError: 'FigureCanvasSVG' object has no attribute 'copy_from_bbox'\n```\n\n### Expected outcome\n\nFigure is saved normally.\n\n### Additional information\n\n_No response_\n\n### Operating system\n\nLinux\n\n### Matplotlib Version\n\n3.10.1\n\n### Matplotlib Backend\n\nAgg and QtAgg\n\n### Python version\n\n3.13.2\n\n### Jupyter version\n\nn/a\n\n### Installation\n\nconda","author":{"url":"https://github.com/drammock","@type":"Person","name":"drammock"},"datePublished":"2025-08-29T15:09:31.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":2},"url":"https://github.com/30485/matplotlib/issues/30485"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:5a7fc19b-abc6-28a4-75bd-5bb995238b7c |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | 98DE:F0C06:A375C9:DEED02:6A520448 |
| html-safe-nonce | 680a2a880e436b32d7d687271a35c0356e2542991965ba3c0d28d6ec189f0d54 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI5OERFOkYwQzA2OkEzNzVDOTpERUVEMDI6NkE1MjA0NDgiLCJ2aXNpdG9yX2lkIjoiNDE0MjEzNDE1OTMyMDY3OTQ5NiIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | 97acf68788a5db27883db39559696b70bfd5dde7700c8e59a6f22d106b28a480 |
| hovercard-subject-tag | issue:3367213684 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/matplotlib/matplotlib/30485/issue_layout |
| twitter:image | https://opengraph.githubassets.com/e3b05bdb0ef8a058c27093e3c8e1262692a0bee730c91e3fda575959ac74d939/matplotlib/matplotlib/issues/30485 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/e3b05bdb0ef8a058c27093e3c8e1262692a0bee730c91e3fda575959ac74d939/matplotlib/matplotlib/issues/30485 |
| og:image:alt | 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)... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | drammock |
| hostname | github.com |
| expected-hostname | github.com |
| None | b9a586c06a05a7a86fc7e3f4dbd03e42f6869085879aa184aa6369456dbd50fb |
| turbo-cache-control | no-preview |
| go-import | github.com/matplotlib/matplotlib git https://github.com/matplotlib/matplotlib.git |
| octolytics-dimension-user_id | 215947 |
| octolytics-dimension-user_login | matplotlib |
| octolytics-dimension-repository_id | 1385122 |
| octolytics-dimension-repository_nwo | matplotlib/matplotlib |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 1385122 |
| octolytics-dimension-repository_network_root_nwo | matplotlib/matplotlib |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 7aed05249554b889eb33d002851a973eebcc7e91 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width