|
@@ -1,3 +1,5 @@
|
|
|
+from __future__ import annotations
|
|
|
+
|
|
|
import asyncio
|
|
|
import io
|
|
|
import os
|
|
@@ -11,8 +13,22 @@ 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 = element
|
|
|
+
|
|
|
+ def __enter__(self) -> Self:
|
|
|
+ return self
|
|
|
+
|
|
|
+ def __exit__(self, *_) -> None:
|
|
|
+ self.element.update()
|
|
|
+
|
|
|
except ImportError:
|
|
|
pass
|
|
|
|
|
@@ -57,3 +73,30 @@ class Pyplot(Element):
|
|
|
while self.client.id in Client.instances:
|
|
|
await asyncio.sleep(1.0)
|
|
|
plt.close(self.fig)
|
|
|
+
|
|
|
+
|
|
|
+class Matplotlib(Element):
|
|
|
+
|
|
|
+ def __init__(self, **kwargs: Any) -> None:
|
|
|
+ """Matplotlib
|
|
|
+
|
|
|
+ Create a `Matplotlib <https://matplotlib.org/>`_ 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 <https://matplotlib.org/stable/api/figure_api.html#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:
|
|
|
+ self._convert_to_html()
|
|
|
+ return super().update()
|