from __future__ import annotations
import io
import os
import weakref
from typing import Any
from typing_extensions import Self
from .. import optional_features
from ..element import Element
try:
if os.environ.get('MATPLOTLIB', 'true').lower() == 'true':
import matplotlib.figure
import matplotlib.pyplot as plt
optional_features.register('matplotlib')
class MatplotlibFigure(matplotlib.figure.Figure):
def __init__(self, element: Matplotlib, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
self._element = weakref.ref(element)
@property
def element(self) -> Matplotlib:
"""The element this matplotlib figure belongs to."""
element = self._element()
if element is None:
raise RuntimeError('The element this matplotlib figure belongs to has been deleted.')
return element
def __enter__(self) -> Self:
return self
def __exit__(self, *_) -> None:
self.element.update()
except ImportError:
pass
class Pyplot(Element, default_classes='nicegui-pyplot'):
def __init__(self, *, close: bool = True, **kwargs: Any) -> None:
"""Pyplot Context
Create a context to configure a `Matplotlib `_ plot.
:param close: whether the figure should be closed after exiting the context; set to `False` if you want to update it later (default: `True`)
:param kwargs: arguments like `figsize` which should be passed to `pyplot.figure `_
"""
if not optional_features.has('matplotlib'):
raise ImportError('Matplotlib is not installed. Please run "pip install matplotlib".')
super().__init__('div')
self.close = close
self.fig = plt.figure(**kwargs) # pylint: disable=possibly-used-before-assignment
self._convert_to_html()
def _convert_to_html(self) -> None:
with io.StringIO() as output:
self.fig.savefig(output, format='svg')
self._props['innerHTML'] = output.getvalue()
def __enter__(self) -> Self:
plt.figure(self.fig)
return self
def __exit__(self, *_) -> None:
self._convert_to_html()
if self.close:
plt.close(self.fig)
self.update()
def _handle_delete(self) -> None:
plt.close(self.fig)
super()._handle_delete()
class Matplotlib(Element, default_classes='nicegui-matplotlib'):
def __init__(self, **kwargs: Any) -> None:
"""Matplotlib
Create a `Matplotlib `_ element rendering a Matplotlib figure.
The figure is automatically updated when leaving the figure context.
:param kwargs: arguments like `figsize` which should be passed to `matplotlib.figure.Figure `_
"""
if not optional_features.has('matplotlib'):
raise ImportError('Matplotlib is not installed. Please run "pip install matplotlib".')
super().__init__('div')
self.figure = MatplotlibFigure(self, **kwargs)
self._convert_to_html()
def _convert_to_html(self) -> None:
with io.StringIO() as output:
self.figure.savefig(output, format='svg')
self._props['innerHTML'] = output.getvalue()
def update(self) -> None:
with self._props.suspend_updates():
self._convert_to_html()
super().update()