8000 Merge pull request #17371 from ianhi/context-manager · matplotlib/matplotlib@89d13c3 · GitHub
[go: up one dir, main page]

Skip to content

Commit 89d13c3

Browse files
authored
Merge pull request #17371 from ianhi/context-manager
add context manager functionality to ion and ioff
2 parents 6062ff6 + a7eb704 commit 89d13c3

File tree

3 files changed

+175
-6
lines changed

3 files changed

+175
-6
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
ioff and ion can be used as context managers
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
`.pyplot.ion` and `.pyplot.ioff` may now be used as context managers to create
5+
a context with interactive mode on, or off respectively. The old behavior of
6+
calling these functions is maintained. To use the new functionality
7+
call as ``with plt.ioff():``

lib/matplotlib/pyplot.py

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -364,9 +364,61 @@ def isinteractive():
364364
return matplotlib.is_interactive()
365365

366366

367+
class _IoffContext:
368+
"""
369+
Context manager for `.ioff`.
370+
371+
The state is changed in ``__init__()`` instead of ``__enter__()``. The
372+
latter is a no-op. This allows using `.ioff` both as a function and
373+
as a context.
374+
"""
375+
376+
def __init__(self):
377+
self.wasinteractive = isinteractive()
378+
matplotlib.interactive(False)
379+
uninstall_repl_displayhook()
380+
381+
def __enter__(self):
382+
pass
383+
384+
def __exit__(self, exc_type, exc_value, traceback):
385+
if self.wasinteractive:
386+
matplotlib.interactive(True)
387+
install_repl_displayhook()
388+
else:
389+
matplotlib.interactive(False)
390+
uninstall_repl_displayhook()
391+
392+
393+
class _IonContext:
394+
"""
395+
Context manager for `.ion`.
396+
397+
The state is changed in ``__init__()`` instead of ``__enter__()``. The
398+
latter is a no-op. This allows using `.ion` both as a function and
399+
as a context.
400+
"""
401+
402+
def __init__(self):
403+
self.wasinteractive = isinteractive()
404+
matplotlib.interactive(True)
405+
install_repl_displayhook()
406+
407+
def __enter__(self):
408+
pass
409+
410+
def __exit__(self, exc_type, exc_value, traceback):
411+
if not self.wasinteractive:
412+
matplotlib.interactive(False)
413+
uninstall_repl_displayhook()
414+
else:
415+
matplotlib.interactive(True)
416+
install_repl_displayhook()
417+
418+
367419
def ioff():
368420
"""
369-
Turn the interactive mode off.
421+
Turn interactive mode off.
370422
371423
See Also
372424
--------
@@ -375,14 +427,33 @@ def ioff():
375427
376428
show : show windows (and maybe block)
377429
pause : show windows, run GUI event loop, and block for a time
430+
431+
Notes
432+
-----
433+
For a temporary change, this can be used as a context manager::
434+
435+
# if interactive mode is on
436+
# then figures will be shown on creation
437+
plt.ion()
438+
# This figure will be shown immediately
439+
fig = plt.figure()
440+
441+
with plt.ioff():
442+
# interactive mode will be off
443+
# figures will not automatically be shown
444+
fig2 = plt.figure()
445+
# ...
446+
447+
To enable usage as a context manager, this function returns an
448+
``_IoffContext`` object. The return value is not intended to be stored
449+
or accessed by the user.
378450
"""
379-
matplotlib.interactive(False)
380-
uninstall_repl_displayhook()
451+
return _IoffContext()
381452

382453

383454
def ion():
384455
"""
385-
Turn the interactive mode on.
456+
Turn interactive mode on.
386457
387458
See Also
388459
--------
@@ -391,9 +462,28 @@ def ion():
391462
392463
show : show windows (and maybe block)
393464
pause : show windows, run GUI event loop, and block for a time
465+
466+
Notes
467+
-----
468+
For a temporary change, this can be used as a context manager::
469+
470+
# if interactive mode is off
471+
# then figures will not be shown on creation
472+
plt.ioff()
473+
# This figure will not be shown immediately
474+
fig = plt.figure()
475+
476+
with plt.ion():
477+
# interactive mode will be on
478+
# figures will automatically be shown
479+
fig2 = plt.figure()
480+
# ...
481+
482+
To enable usage as a context manager, this function returns an
483+
``_IonContext`` object. The return value is not intended to be stored
484+
or accessed by the user.
394485
"""
395-
matplotlib.interactive(True)
396-
install_repl_displayhook()
486+
return _IonContext()
397487

398488

399489
def pause(interval):

lib/matplotlib/tests/test_pyplot.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,75 @@ def test_nrows_error():
8181
plt.subplot(nrows=1)
8282
with pytest.raises(TypeError):
8383
plt.subplot(ncols=1)
84+
85+
86+
def test_ioff():
87+
plt.ion()
88+
assert mpl.is_interactive()
89+
with plt.ioff():
90+
assert not mpl.is_interactive()
91+
assert mpl.is_interactive()
92+
93+
plt.ioff()
94+
assert not mpl.is_interactive()
95+
with plt.ioff():
96+
assert not mpl.is_interactive()
97+
assert not mpl.is_interactive()
98+
99+
100+
def test_ion():
101+
plt.ioff()
102+
assert not mpl.is_interactive()
103+
with plt.ion():
104+
assert mpl.is_interactive()
105+
assert not mpl.is_interactive()
106+
107+
plt.ion()
108+
assert mpl.is_interactive()
109+
with plt.ion():
110+
assert mpl.is_interactive()
111+
assert mpl.is_interactive()
112+
113+
114+
def test_nested_ion_ioff():
115+
# initial state is interactive
116+
plt.ion()
117+
118+
# mixed ioff/ion
119+
with plt.ioff():
120+
assert not mpl.is_interactive()
121+
with plt.ion():
122+
assert mpl.is_interactive()
123+
assert not mpl.is_interactive()
124+
assert mpl.is_interactive()
125+
126+
# redundant contexts
127+
with plt.ioff():
128+
with plt.ioff():
129+
assert not mpl.is_interactive()
130+
assert mpl.is_interactive()
131+
132+
with plt.ion():
133+
plt.ioff()
134+
assert mpl.is_interactive()
135+
136+
# initial state is not interactive
137+
plt.ioff()
138+
139+
# mixed ioff/ion
140+
with plt.ion():
141+
assert mpl.is_interactive()
142+
with plt.ioff():
143+
assert not mpl.is_interactive()
144+
assert mpl.is_interactive()
145+
assert not mpl.is_interactive()
146+
147+
# redunant contexts
148+
with plt.ion():
149+
with plt.ion():
150+
assert mpl.is_interactive()
151+
assert not mpl.is_interactive()
152+
153+
with plt.ioff():
154+
plt.ion()
155+
assert not mpl.is_interactive()

0 commit comments

Comments
 (0)
0