Skip to content

Commit 3e752a5

Browse files
committed
Adds metadata to help display matplotlib figures legibly
1 parent 0c5ff18 commit 3e752a5

File tree

1 file changed

+34
-3
lines changed

1 file changed

+34
-3
lines changed

backend_inline.py

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import matplotlib
99
from matplotlib.backends.backend_agg import new_figure_manager, FigureCanvasAgg # analysis: ignore
10+
from matplotlib import colors
1011
from matplotlib._pylab_helpers import Gcf
1112

1213
from IPython.core.getipython import get_ipython
@@ -33,7 +34,10 @@ def show(close=None, block=None):
3334
close = InlineBackend.instance().close_figures
3435
try:
3536
for figure_manager in Gcf.get_all_fig_managers():
36-
display(figure_manager.canvas.figure)
37+
display(
38+
figure_manager.canvas.figure,
39+
metadata=_fetch_figure_metadata(figure_manager.canvas.figure)
40+
)
3741
finally:
3842
show._to_draw = []
3943
# only call close('all') if any to close
@@ -72,7 +76,7 @@ def draw_if_interactive():
7276

7377
if not hasattr(fig, 'show'):
7478
# Queue up `fig` for display
75-
fig.show = lambda *a: display(fig)
79+
fig.show = lambda *a: display(fig, metadata=_fetch_figure_metadata(fig))
7680

7781
# If matplotlib was manually set to non-interactive mode, this function
7882
# should be a no-op (otherwise we'll generate duplicate plots, since a user
@@ -124,7 +128,7 @@ def flush_figures():
124128
active = set([fm.canvas.figure for fm in Gcf.get_all_fig_managers()])
125129
for fig in [ fig for fig in show._to_draw if fig in active ]:
126130
try:
127-
display(fig)
131+
display(fig, metadata=_fetch_figure_metadata(fig))
128132
except Exception as e:
129133
# safely show traceback if in IPython, else raise
130134
ip = get_ipython()
@@ -163,3 +167,30 @@ def configure_once(*args):
163167
ip.events.register('post_run_cell', configure_once)
164168

165169
_enable_matplotlib_integration()
170+
171+
def _fetch_figure_metadata(fig):
172+
"""Get some metadata to help with displaying a figure."""
173+
# determine if a background is needed for legibility
174+
if _is_transparent(fig.get_facecolor()):
175+
# the background is transparent
176+
ticksLight = _is_light([label.get_color()
177+
for axes in fig.axes
178+
for axis in (axes.xaxis, axes.yaxis)
179+
for label in axis.get_ticklabels()])
180+
if ticksLight.size and (ticksLight == ticksLight[0]).all():
181+
# there are one or more tick labels, all with the same lightness
182+
return {'needs_background': 'dark' if ticksLight[0] else 'light'}
183+
184+
return None
185+
186+
def _is_light(color):
187+
"""Determines if a color (or each of a sequence of colors) is light (as
188+
opposed to dark). Based on ITU BT.601 luminance formula (see
189+
https://stackoverflow.com/a/596241)."""
190+
rgbaArr = colors.to_rgba_array(color)
191+
return rgbaArr[:,:3].dot((.299, .587, .114)) > .5
192+
193+
def _is_transparent(color):
194+
"""Determine transparency from alpha."""
195+
rgba = colors.to_rgba(color)
196+
return rgba[3] < .5

0 commit comments

Comments
 (0)