Skip to content

Commit d0d28e0

Browse files
authored
automatically draw on savefig (#98)
* automatically draw on savefig * update docs
1 parent 7841abc commit d0d28e0

File tree

5 files changed

+157
-3
lines changed

5 files changed

+157
-3
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
### Enhancements
2525

26+
- Calling `f.canvas.draw()` before `plt.savefig` is no longer necessary. This now happens
27+
automatically ([#98](https://github.com/mathause/mplotutils/pull/98)).
2628
- Add python 3.12 to list of supported versions ([#89](https://github.com/mathause/mplotutils/pull/89)).
2729

2830
### Bug fixes

docs/example.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,6 @@ def plot_map_mpu():
4949
# ensure the figure has the correct size
5050
mpu.set_map_layout(axs)
5151

52-
# call a draw to ensure the colorbar is correct
53-
f.canvas.draw()
54-
5552
f.suptitle("With mplotutils")
5653

5754

mplotutils/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44

55
from . import _colorbar, cartopy_utils, colormaps
66
from ._colorbar import *
7+
from ._savefig import autodraw
78
from .cartopy_utils import *
89
from .colormaps import *
910
from .map_layout import set_map_layout
1011
from .xrcompat import *
1112

13+
autodraw(True)
14+
1215
try:
1316
__version__ = _get_version("mplotutils")
1417
except Exception:

mplotutils/_savefig.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from functools import wraps
2+
3+
from matplotlib.figure import Figure
4+
5+
# ensure the original implementation is not overwritten
6+
try:
7+
savefig_orig
8+
except NameError:
9+
savefig_orig = Figure.savefig
10+
11+
12+
def savefig(func):
13+
14+
@wraps(func)
15+
def inner(self, *args, **kwargs):
16+
17+
# call draw in any case
18+
self.canvas.draw()
19+
20+
return func(self, *args, **kwargs)
21+
22+
return inner
23+
24+
25+
class autodraw:
26+
27+
def __init__(self, /, toggle):
28+
29+
self.toggle = toggle
30+
if toggle:
31+
monkeypatch()
32+
else:
33+
undo()
34+
35+
def __enter__(self):
36+
return
37+
38+
def __exit__(self, type, value, traceback):
39+
40+
if self.toggle:
41+
undo()
42+
else:
43+
monkeypatch()
44+
45+
46+
def monkeypatch():
47+
# Monkey patch matplotlib to call our savefig instead of the standard
48+
49+
Figure.savefig = savefig(savefig_orig)
50+
51+
52+
def undo():
53+
Figure.savefig = savefig_orig
54+
55+
56+
# mpu.savefig.autodraw()

mplotutils/tests/test_savefig.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import io
2+
3+
import matplotlib.pyplot as plt
4+
import pytest
5+
6+
import mplotutils as mpu
7+
from mplotutils.tests.test_colorbar import create_fig_aspect
8+
9+
from . import figure_context
10+
11+
12+
def test_autodraw_orig_func():
13+
14+
# NOTE plt.Figure.savefig is the overwritten one
15+
assert mpu._savefig.savefig_orig is not plt.Figure.savefig
16+
17+
with mpu.autodraw(False):
18+
assert mpu._savefig.savefig_orig is plt.Figure.savefig
19+
20+
with mpu.autodraw(False):
21+
with mpu.autodraw(True):
22+
assert mpu._savefig.savefig_orig is not plt.Figure.savefig
23+
24+
with mpu.autodraw(False):
25+
savefig_autodraw = plt.Figure.savefig
26+
27+
with mpu.autodraw(True):
28+
savefig_no_autodraw = plt.Figure.savefig
29+
30+
assert savefig_no_autodraw is not savefig_autodraw
31+
32+
mpu.autodraw(True)
33+
assert mpu._savefig.savefig_orig is not plt.Figure.savefig
34+
35+
mpu.autodraw(False)
36+
assert mpu._savefig.savefig_orig is plt.Figure.savefig
37+
38+
# restore to default
39+
mpu.autodraw(True)
40+
41+
42+
def test_ensure_draw_method_called(monkeypatch):
43+
# this is a pseudo-mock test (I think the actual backend would need to be mocked)
44+
45+
class DrawMethodCalled(Exception):
46+
pass
47+
48+
def draw():
49+
raise DrawMethodCalled()
50+
51+
with figure_context() as f:
52+
53+
monkeypatch.setattr(f.canvas, "draw", draw)
54+
55+
# not called when not autodrawing
56+
with mpu.autodraw(False):
57+
f.savefig(io.BytesIO())
58+
59+
# called when autodrawing
60+
with pytest.raises(DrawMethodCalled):
61+
f.savefig(io.BytesIO())
62+
63+
64+
def test_saved_figure_not_the_same_vertical():
65+
66+
with figure_context() as f:
67+
create_fig_aspect(aspect=0.5, orientation="vertical")
68+
69+
file_no_autodraw = io.BytesIO()
70+
71+
with mpu.autodraw(False):
72+
f.savefig(file_no_autodraw)
73+
74+
file_autodraw = io.BytesIO()
75+
with mpu.autodraw(True):
76+
f.savefig(file_autodraw)
77+
78+
assert file_no_autodraw.getvalue() != file_autodraw.getvalue()
79+
80+
81+
def test_saved_figure_not_the_same_horizontal():
82+
83+
with figure_context() as f:
84+
create_fig_aspect(aspect=2, orientation="horizontal")
85+
# ensure the colorbar is actually on the figure
86+
f.subplots_adjust(bottom=0.5)
87+
88+
file_no_autodraw = io.BytesIO()
89+
with mpu.autodraw(False):
90+
f.savefig(file_no_autodraw)
91+
92+
file_autodraw = io.BytesIO()
93+
with mpu.autodraw(True):
94+
f.savefig(file_autodraw)
95+
96+
assert file_no_autodraw.getvalue() != file_autodraw.getvalue()

0 commit comments

Comments
 (0)