## Interactive Widgets

Currently, visualization in SageMath is mostly not interactive. However, there are approaches to build interactive widgets for the Jupyter notebook:

• @interact creates configurable widgets.

• ipyμvue can be used to write Jupyter widgets in Python that work with SageMath.

• matplotlib plots can be made interactive.

### Example: Interactive matplotlib Plots

```   1 # Enable interactive matplotlib output in Jupyter.
2 %matplotlib nbagg
3
4 # Better, sage -pip install ipympl and then
5 # %matplotlib ipympl
6
7 class DynamicPlot(sage.plot.primitive.GraphicPrimitive):
8     r"""
9     A dynamic 2D plot that redraws when it is dragged around.
10
11     INPUT:
12
13     - ``create_plot`` -- a callable that creates a plot for given ``xmin``, ``ymin``, ``xmax``, ``ymax`` bounds.
14
15     - ``xmin``, ``ymin``, ``xmax``, ``ymax`` -- initial bounds of the 2D plot.
16
17     """
18     def __init__(self, create_plot, xmin=-1, xmax=1, ymin=-1, ymax=1, options={}):
19         self._create_plot = create_plot
20
21         self._xmin = xmin
22         self._xmax = xmax
23         self._ymin = ymin
24         self._ymax = ymax
25
26         super().__init__(options)
27
28     def _render_on_subplot(self, subplot):
29         def redraw(_=None):
30             try:
31                 # Clear the subplot before redrawing. Otherwise, we would pile up lots
32                 # of identical plots that take more and more time to draw.
33                 # Note that this will also clear other primitives from this subplot.
34                 subplot._children = []
35
36                 xlim = subplot.axes.get_xlim()
37                 ylim = subplot.axes.get_ylim()
38
39                 import sage.misc.verbose
40                 verbose = sage.misc.verbose.get_verbose()
41                 # Silence warnings about undefined values in the plotted function.
42                 sage.misc.verbose.set_verbose(-1)
43                 try:
44                     # Plot all the objects produced by create_plot().
45                     for object in self._create_plot(xmin=xlim[0], xmax=xlim[1], ymin=ylim[0], ymax=ylim[1])._objects:
46                         object._render_on_subplot(subplot)
47                 finally:
48                     sage.misc.verbose.set_verbose(verbose)
49             except Exception:
50                 # Unfortunately, there is no easy way to display an error message in a matplotlib callback.
51                 # Errors are shown in the terminal where Jupyter was started.
52                 subplot.clear()
53                 raise
54
55         # Redraw when the plot is dragged around.
56         subplot.axes.callbacks.connect("ylim_changed", redraw)
57         subplot.axes.callbacks.connect("xlim_changed", redraw)
58
59         # Draw the plot in the initial bounds.
60         redraw()
61
62     def get_minmax_data(self):
63         r"""
64         Return the initial bounds of this plot to focus the camera here.
65         """
66         return dict(xmin=self._xmin, ymin=self._ymin, xmax=self._xmax, ymax=self._ymax)
67
68     def show(self):
69         r"""
70         Create a matplotlib figure and show this plot.
71         """
72         g = Graphics()
74
75         import matplotlib.pyplot as plt
76         figure = plt.figure()
77         g.matplotlib(figure=figure)
```

```   1 # We plot an infinite ray from the origin.
2 def create_plot(*, xmin, ymin, xmax, ymax):
3     def ray(x):
4         if x > 0:
5             return x + sin(x)
6
7     return plot(ray, alpha=.5, xmin=xmin, ymin=ymin, xmax=xmax, ymax=ymax)
8
9 DynamicPlot(create_plot).show()
```

```   1 # We plot a parabola
2 def create_plot(*, xmin, ymin, xmax, ymax):
3     return plot(x^2, alpha=.5, xmin=xmin, ymin=ymin, xmax=xmax, ymax=ymax,
4                # Adaptive recursion slows down the plotting when we zoom out a lot, so we disable
5                # it for this simple function.