Description
Issue:
Often when I'm using the widget backend in jupyterlab and want to do something like define a small layout of ipywidgets and plots I find myself using this pattern:
plt.ioff()
fig = plt.figure()
plt.ion()
....
widgets.VBox([slider1,slider2,fig.canvas])
ioff
is necessary because without it the below snippet will result in two identical plots showing up with the slider between them
%matplotlib widget
import ipywidgets as widgets
import numpy as np
import matplotlib.pyplot as plt
phase = widgets.FloatSlider(value=0, min=0, max=np.pi)
x = np.linspace(0, 2*np.pi)
fig = plt.figure()
ax = fig.gca()
line = ax.plot(x, np.cos(x))[0]
def update(change):
line.set_data(x, np.cos(x + phase.value))
fig.canvas.draw_idle()
phase.observe(update,names='value')
widgets.VBox([phase,fig.canvas])
I then always call ion
again because I prefer it for most of what I do in a notebook.
This is annoying because it requires two extra lines everytime I want to do this, and it doesn't seem necessary to constantly be calling (un)install_repl_displayhook
in ion
and ioff
I understand that I could use the object oriented interface to make my own figures using Figur e
but this would be somewhat signifcant change from how I naturally use matplotlib in the notebook and would require learning more about a non-trivial interface.
Potential solutions
1 - kwarg:
Add a kwarg to plt.figure
. Something like draw_on_create
or draw
or draw_immediately
e.g.:
diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py
index a097296dc..0f8d54380 100644
--- a/lib/matplotlib/pyplot.py
+++ b/lib/matplotlib/pyplot.py
@@ -491,6
660C
+491,7 @@ def figure(num=None, # autoincrement if None, else integer from 1-N
frameon=True,
FigureClass=Figure,
clear=False,
+ draw=True,
**kwargs
):
"""
@@ -613,7 +614,8 @@ def figure(num=None, # autoincrement if None, else integer from 1-N
# to be called in plotting commands to make the figure call show
# still work. There is probably a better way to do this in the
# FigureManager base class.
- draw_if_interactive()
+ if draw:
+ draw_if_interactive()
if _INSTALL_FIG_OBSERVER:
fig.stale_callback = _auto_draw_if_interactive
2 - contextmanager:
This one I can admittedly have in my own library, but would be nice to have provided by matplotlib.
There could be either be a context manager separate from ion/ioff
that toggles matplotlib.interactive
. Or plt.ioff
could be transformed in a contextmanager that can still be called like so:
class ioff:
def __call__(self):
matplotlib.interactive(False)
uninstall_repl_displayhook()
def __enter__(self):
self.call()
def __exit__(self):
ion()
3 - remove specific figures from interactive mode:
Maybe certain figures could be prevented from being redrawn in interactive mode, something like:
plt.figure(is_interactive=False)
which would then exclude that figure from being redrawn without an explicit call to fig.canvas.draw
This may already be possible using the Object Oriented interface but for me at least as noted I find digging that can be confusing.