8000 Request: provide a contextmanager for ioff or allow plt.figure(draw_on_create=False) · Issue #17013 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content
Request: provide a contextmanager for ioff or allow plt.figure(draw_on_create=False) #17013
Closed
@ianhi

Description

@ianhi

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])

image

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0