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, that happens on the
creation of the figure because pyplot does magic behind the curtain that is hard to
suppress. For the matplot interactive widget backend, named "nbagg", it wraps te
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_syle:
- **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 retainin axes. figure.clear() removes the axes
sometimes.
"""
for ax in self.figure.get_axes():
if ax.has_data() or len(ax.artists) > 0:
ax.clear()