Source code for scwidgets.cue._widget_cue_figure
# postpones evaluation of annotations
# see https://stackoverflow.com/a/33533514
from __future__ import annotations
from typing import List, Optional, Union
import matplotlib
import matplotlib.pyplot as plt
from IPython.display import display
from ipywidgets import Widget
from matplotlib.figure import Figure
from traitlets.utils.sentinel import Sentinel
from ._widget_cue_output import CueOutput
[docs]
class CueFigure(CueOutput):
"""
A cued, displayable `ipywidget.Output` for a `matplotlib` figure. Provides
utilities to clear and draw the updated figure. For the `matplotlib inline`
backend, it closes the active figure to prevent any display outside of the
container, which happens on the creation of the figure because `pyplot` does magic
behind the curtain that is hard to suppress. For the matplotlib interactive widget
backend, named "nbagg", it wraps the figure within.
:param figure:
The `matplotlib` figure
:param widgets_to_observe:
The widget to observe if the :param traits_to_observe: has changed.
:param traits_to_observe:
The trait from the :param widgets_to_observe: to observe if changed.
Specify `traitlets.All` to observe all traits.
:param cued:
Specifies if it is cued on initialization
:param show_toolbars:
Hide toolbars and headers when using in widget mode.
:param css_style:
- **base**: the css style of the box during initialization
- **cue**: the css style that is added when :param
traits_to_observe: in widget :param widgets_to_observe: changes.
It is supposed to change the style of the box such that the user has a visual
cue that :param widget_to_cue: has changed.
"""
def __init__(
self,
figure: Figure,
widgets_to_observe: Union[None, List[Widget], Widget] = None,
traits_to_observe: Union[
None, str, Sentinel, List[Union[str, Sentinel, List[str]]]
] = None,
cued: bool = True,
show_toolbars: bool = False,
css_style: Optional[dict] = None,
**kwargs,
):
CueOutput.__init__(
self,
widgets_to_observe,
traits_to_observe,
cued,
css_style,
**kwargs,
)
self.figure = figure
if matplotlib.backends.backend in [
"module://matplotlib_inline.backend_inline",
"macosx",
"agg",
]:
# we close the figure so the figure is only contained in this widget
# and not shown using plt.show()
plt.close(self.figure)
elif (
matplotlib.backends.backend == "module://ipympl.backend_nbagg"
or matplotlib.backends.backend == "widget"
):
# jupyter lab 3 uses "module://ipympl.backend_nbagg"
# jupyter lab 4 uses "widget"
with self:
self.figure.canvas.show()
else:
raise NotImplementedError(
f"matplotlib backend {matplotlib.backends.backend!r} not supported. "
"Please change backend to 'widget' by running `%matplotlib widget` "
"that should be supported on all systems."
)
if show_toolbars:
# hides unnecessary elements shown with %matplotlib widget
self.figure.canvas.header_visible = False
self.figure.canvas.footer_visible = False
self.figure.canvas.toolbar_visible = False
self.figure.canvas.resizable = False
self.draw_display()
[docs]
def clear_display(self, wait=False):
"""
:param wait:
same meaning as for the `wait` parameter in the `ipywidgets.clear_output`
function
"""
if matplotlib.backends.backend in [
"module://matplotlib_inline.backend_inline",
"macosx",
"agg",
]:
self.clear_figure()
self.clear_output(wait=wait)
elif (
matplotlib.backends.backend == "module://ipympl.backend_nbagg"
or matplotlib.backends.backend == "widget"
):
# jupyter lab 3 uses "module://ipympl.backend_nbagg"
# jupyter lab 4 uses "widget"
self.clear_figure()
if not (wait):
self.figure.canvas.draw_idle()
self.figure.canvas.flush_events()
else:
raise NotImplementedError(
f"matplotlib backend {matplotlib.backends.backend!r} not supported. "
"Please change backend to 'widget' by running `%matplotlib widget` "
"that should be supported on all systems."
)
[docs]
def draw_display(self):
"""
Enforces redrawing the figure
"""
if matplotlib.backends.backend in [
"module://matplotlib_inline.backend_inline",
"macosx",
"agg",
]:
with self:
display(self.figure)
elif (
matplotlib.backends.backend == "module://ipympl.backend_nbagg"
or matplotlib.backends.backend == "widget"
):
# jupyter lab 3 uses "module://ipympl.backend_nbagg"
# jupyter lab 4 uses "widget"
self.figure.canvas.draw_idle()
self.figure.canvas.flush_events()
else:
raise NotImplementedError(
f"matplotlib backend {matplotlib.backends.backend!r} not supported. "
"Please change backend to 'widget' by running `%matplotlib widget` "
"that should be supported on all systems."
)
[docs]
def clear_figure(self):
"""
Clears the figure while retaining axes as figure.clear() removes the axes
sometimes.
"""
for ax in self.figure.get_axes():
if ax.has_data() or len(ax.artists) > 0:
ax.clear()