diff --git a/src/plopp/backends/matplotlib/canvas.py b/src/plopp/backends/matplotlib/canvas.py index a4a29e9c..75be143a 100644 --- a/src/plopp/backends/matplotlib/canvas.py +++ b/src/plopp/backends/matplotlib/canvas.py @@ -746,6 +746,12 @@ def toggle_logy(self): """ self.yscale = 'log' if self.yscale == 'linear' else 'linear' + def toggle_grid(self): + """ + Toggle the visibility of the grid. + """ + self.grid = not self.grid + def has_user_xlabel(self) -> bool: """ Return ``True`` if the user has set an x-axis label. diff --git a/src/plopp/backends/matplotlib/figure.py b/src/plopp/backends/matplotlib/figure.py index 50aa5e9d..10c84157 100644 --- a/src/plopp/backends/matplotlib/figure.py +++ b/src/plopp/backends/matplotlib/figure.py @@ -2,6 +2,8 @@ # Copyright (c) 2024 Scipp contributors (https://github.com/scipp) from __future__ import annotations +from functools import partial + from matplotlib.axes import Axes from matplotlib.figure import Figure as MplFigure @@ -137,7 +139,7 @@ def __init__(self, View, *args, **kwargs): self.interactive = True self.toolbar = make_toolbar_canvas2d(view=self.view) - self._setup_linked_home_buttons() + self._setup_linked_buttons() self.left_bar = VBar([self.toolbar]) self.right_bar = VBar() @@ -158,31 +160,38 @@ def _make_children(self): self.bottom_bar, ] - def _setup_linked_home_buttons(self): + def _setup_linked_buttons(self): """ - Link home buttons across all Plopp figures sharing this matplotlib figure. + Link some buttons buttons across all Plopp figures sharing this matplotlib + figure (e.g. the 'home' or 'grid' buttons). This is needed when multiple Plopp figures are created from matplotlib axes that belong to the same figure, e.g. subplots. """ - if not hasattr(self.fig, '_plopp_home_data'): - self.fig._plopp_home_data = {'callbacks': {}, 'toolbars': {}} - data = self.fig._plopp_home_data + keys = ('home', 'grid') + if not hasattr(self.fig, '_plopp_buttons_data'): + self.fig._plopp_buttons_data = { + 'callbacks': {key: {} for key in keys}, + 'toolbars': {}, + } + data = self.fig._plopp_buttons_data axes_id = id(self.ax) - # Store the original callback. + # Store the original callbacks # We use a dict to make sure that for repeated use of the same axes via # successive figure creations, we do not grow the list of callbacks # indefinitely. - data['callbacks'][axes_id] = self.toolbar['home'].callback + for key in keys: + data['callbacks'][key][axes_id] = self.toolbar[key].callback data['toolbars'][axes_id] = self.toolbar # Create a linked callback that calls ALL original callbacks - def linked_home(): - for callback in data['callbacks'].values(): + def linked_home(key): + for callback in data['callbacks'][key].values(): callback() # Update all toolbars (including this one) to use the linked callback for toolbar in data['toolbars'].values(): - toolbar['home'].callback = linked_home + for key in keys: + toolbar[key].callback = partial(linked_home, key) class StaticFigure(MplBaseFig): diff --git a/src/plopp/widgets/toolbar.py b/src/plopp/widgets/toolbar.py index 26555272..9026df5f 100644 --- a/src/plopp/widgets/toolbar.py +++ b/src/plopp/widgets/toolbar.py @@ -63,10 +63,15 @@ def autoscale_axes() -> None: view.autoscale() view.canvas.draw() + def grid() -> None: + view.canvas.toggle_grid() + view.canvas.draw() + return Toolbar( tools={ "home": tools.HomeTool(autoscale_axes, tooltip="Autoscale axes range"), "panzoom": tools.PanZoomTool(view.canvas.panzoom), + "grid": tools.GridTool(grid, value=view.canvas.grid), "save": tools.SaveTool(view.canvas.download_figure), } ) diff --git a/src/plopp/widgets/tools.py b/src/plopp/widgets/tools.py index e5058c2e..7a71157e 100644 --- a/src/plopp/widgets/tools.py +++ b/src/plopp/widgets/tools.py @@ -152,6 +152,9 @@ def value(self) -> str | None: HomeTool = partial(ButtonTool, icon='home') """Return home tool.""" +GridTool = partial(ToggleTool, icon='th', tooltip='Toggle grid visibility') +"""Toggle grid visibility tool.""" + SaveTool = partial(ButtonTool, icon='save', tooltip='Save figure') """Save figure to png tool."""