Source code for edifice.extra.matplotlib_figure
from __future__ import annotations
import typing as tp
from edifice.base_components.base_components import CommandType, QtWidgetElement
from edifice.qt import QT_VERSION
if QT_VERSION == "PyQt6":
pass
else:
pass
from matplotlib.axes import Axes
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
from matplotlib.figure import Figure
if tp.TYPE_CHECKING:
from matplotlib.backend_bases import MouseEvent
[docs]
class MatplotlibFigure(QtWidgetElement):
"""
A **matplotlib** `Figure <https://matplotlib.org/stable/api/figure_api.html#matplotlib.figure.Figure>`_.
Requires `matplotlib <https://matplotlib.org/stable/>`_.
Example::
from matplotlib.axes import Axes
import numpy as np
from edifice.extra import MatplotlibFigure
def plot_fun(ax:Axes):
time_range = np.linspace(-10, 10, num=120)
ax.plot(time_range, np.sin(time_range))
MatplotlibFigure(plot_fun=plot_fun)
Args:
plot_fun:
Function which takes **matplotlib**
`Axes <https://matplotlib.org/stable/api/axes_api.html>`_
and calls
`Axes.plot <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.plot.html>`_.
on_figure_mouse_move:
Handler for mouse move
`MouseEvent <https://matplotlib.org/stable/api/backend_bases_api.html#matplotlib.backend_bases.MouseEvent>`_.
"""
[docs]
def __init__(
self,
plot_fun: tp.Callable[[Axes], None],
on_figure_mouse_move: tp.Callable[[MouseEvent], None] | None = None,
**kwargs,
):
super().__init__(**kwargs)
self._register_props(
{
"plot_fun": plot_fun,
"on_figure_mouse_move": on_figure_mouse_move,
},
)
self.underlying: FigureCanvasQTAgg | None = None
self.subplots: Axes | None = None
self.current_plot_fun: tp.Callable[[Axes], None] | None = None
self.on_mouse_move_connect_id: int | None = None
def _qt_update_commands(self, children, newprops):
if self.underlying is None:
# Default to maximum figsize https://matplotlib.org/stable/api/figure_api.html#matplotlib.figure.figaspect
# Constrain the Figure by putting it in a smaller View, it will resize itself correctly.
self.underlying = FigureCanvasQTAgg(Figure(figsize=(16.0, 16.0)))
self.subplots = tp.cast(Axes, self.underlying.figure.subplots()) # TODO is this cast valid?
assert self.underlying is not None
assert self.subplots is not None
commands = super()._qt_update_commands_super(children, newprops, self.underlying, None)
if "plot_fun" in newprops:
def _command_plot_fun(self):
self.current_plot_fun = tp.cast(tp.Callable[[Axes], None], self.props.plot_fun)
self.subplots.clear()
self.current_plot_fun(self.subplots)
self.underlying.draw()
# alternately we could do draw_idle() here, but I don't think it's
# any better and it messes up the mouse events.
commands.append(CommandType(_command_plot_fun, self))
if "on_figure_mouse_move" in newprops:
def _command_mouse_move(self):
if self.on_mouse_move_connect_id is not None:
self.underlying.mpl_disconnect(self.on_mouse_move_connect_id)
if newprops["on_figure_mouse_move"] is not None:
self.on_mouse_move_connect_id = self.underlying.mpl_connect(
"motion_notify_event",
newprops["on_figure_mouse_move"],
)
else:
self.on_mouse_move_connect_id = None
commands.append(CommandType(_command_mouse_move, self))
return commands