Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c831910

Browse files
committed
Add style manager, fix issue where changing style resets backend
1 parent 0b6b711 commit c831910

File tree

1 file changed

+141
-4
lines changed

1 file changed

+141
-4
lines changed

‎proplot/config.py

Lines changed: 141 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import matplotlib.font_manager as mfonts
1818
import matplotlib.colors as mcolors
1919
import matplotlib.style.core as mstyle
20+
import matplotlib.cbook as cbook
2021
import numbers
2122
import cycler
2223
from collections import namedtuple
@@ -903,7 +904,7 @@ def reset(self, local=True, user=True, default=True):
903904
# "style", and do not use rc_quick to apply any default settings -- this
904905
# should be for user convenience only and shold not be used internally.
905906
if default:
906-
mstyle.use('default')
907+
rc_params.update(_get_style_dicts('original', infer=False))
907908
rc_params.update(defaults._rc_params_default) # proplot changes
908909
rc_added.update(defaults._rc_added_default) # proplot custom params
909910
rc_quick.update(defaults._rc_quick_default) # proplot quick params
@@ -1021,9 +1022,145 @@ def config_inline_backend(fmt=None):
10211022
ipython.magic('config InlineBackend.rc = {}')
10221023
ipython.magic('config InlineBackend.close_figures = True')
10231024
ipython.magic("config InlineBackend.print_figure_kwargs = {'bbox_inches': None}")
1024-
ipython.magic( # use ProPlot tight layout instead
1025-
'config InlineBackend.print_figure_kwargs = {"bbox_inches": None}'
1026-
)
1025+
1026+
1027+
def _get_default_dict():
1028+
"""
1029+
Get the default rc parameters dictionary with deprecated parameters filtered.
1030+
"""
1031+
# NOTE: Use RcParams update to filter and translate deprecated settings
1032+
# before actually applying them to rcParams down pipeline. This way we can
1033+
# suppress warnings for deprecated default params but still issue warnings
1034+
# when user-supplied stylesheets have deprecated params.
1035+
# WARNING: Some deprecated rc params remain in dictionary as None so we
1036+
# filter them out. Beware if hidden attribute changes.
1037+
rcdict = _get_filtered_dict(mpl.rcParamsDefault, warn=False)
1038+
with cbook._suppress_matplotlib_deprecation_warning():
1039+
rcdict = dict(mpl.RcParams(rcdict))
1040+
for attr in ('_deprecated_remain_as_none', '_deprecated_set'):
1041+
if hasattr(mpl, attr): # _deprecated_set is in matplotlib before v3
1042+
for deprecated in getattr(mpl, attr):
1043+
rcdict.pop(deprecated, None)
1044+
return rcdict
1045+
1046+
1047+
def _get_filtered_dict(rcdict, warn=True):
1048+
"""
1049+
Filter out blacklisted style parameters.
1050+
"""
1051+
# NOTE: This implements bugfix: https://github.com/matplotlib/matplotlib/pull/17252
1052+
# This fix is *critical* for proplot because we always run style.use()
1053+
# when the configurator is made. Without fix backend is reset every time
1054+
# you import proplot in jupyter notebooks. So apply retroactively.
1055+
rcdict_filtered = {}
1056+
for key in rcdict:
1057+
if key in mstyle.STYLE_BLACKLIST:
1058+
if warn:
1059+
warnings._warn_proplot(
1060+
f'Dictionary includes a parameter, {key!r}, that is not related '
1061+
'to style. Ignoring.'
1062+
)
1063+
else:
1064+
rcdict_filtered[key] = rcdict[key]
1065+
return rcdict_filtered
1066+
1067+
1068+
def _get_style_dicts(style, infer=False):
1069+
"""
1070+
Return a dictionary of settings belonging to the requested style(s). If `infer`
1071+
is ``True``, two dictionaries are returned, where the second contains custom
1072+
ProPlot settings "inferred" from the matplotlib settings.
1073+
"""
1074+
# NOTE: This is adapted from matplotlib source for the following changes:
1075+
# 1. Add 'original' option. Like rcParamsOrig except we also *reload*
1076+
# from user matplotlibrc file.
1077+
# 2. When the style is changed we reset to the *default* state ignoring
1078+
# matplotlibrc. Matplotlib applies styles on top of current state
1079+
# (including matplotlibrc changes and runtime rcParams changes) but
1080+
# IMO the word 'style' implies a *rigid* static format.
1081+
# 3. Add a separate function that returns lists of style dictionaries so
1082+
# that we can modify the active style in a context block. ProPlot context
1083+
# is more conservative than matplotlib's rc_context because it gets
1084+
# called a lot (e.g. every time you make an axes and every format() call).
1085+
# Instead of copying the entire rcParams dict we just track the keys
1086+
# that were changed.
1087+
style_aliases = {
1088+
'538': 'fivethirtyeight',
1089+
'mpl20': 'default',
1090+
'mpl15': 'classic',
1091+
'original': mpl.matplotlib_fname(),
1092+
}
1093+
if isinstance(style, str) or isinstance(style, dict):
1094+
styles = [style]
1095+
else:
1096+
styles = style
1097+
1098+
# Always apply the default style *first* so styles are rigid
1099+
kw_params = _get_default_dict()
1100+
if style == 'default' or style is mpl.rcParamsDefault:
1101+
return kw_params
1102+
1103+
# Apply "pseudo" default properties. Pretend some proplot settings are part of
1104+
# the matplotlib specification so they propagate to other styles.
1105+
kw_params['font.family'] = 'sans-serif'
1106+
kw_params['font.sans-serif'] = defaults._rc_params_default['font.sans-serif']
1107+
1108+
# Apply user input style(s) one by one
1109+
# NOTE: Always use proplot fonts if style does not explicitly set them.
1110+
for style in styles:
1111+
if isinstance(style, dict):
1112+
kw = style
1113+
elif isinstance(style, str):
1114+
style = style_aliases.get(style, style)
1115+
if style in mstyle.library:
1116+
kw = mstyle.library[style]
1117+
else:
1118+
try:
1119+
kw = mpl.rc_params_from_file(style, use_default_template=False)
1120+
except IOError:
1121+
raise IOError(
1122+
f'Style {style!r} not found in the style library and input is '
1123+
'not a valid URL or path. Available styles are: '
1124+
+ ', '.join(map(repr, mstyle.available)) + '.'
1125+
)
1126+
else:
1127+
raise ValueError(f'Invalid style {style!r}. Must be string or dictionary.')
1128+
kw = _get_filtered_dict(kw, warn=True)
1129+
kw_params.update(kw)
1130+
1131+
# Infer proplot params from stylesheet params
1132+
if infer:
1133+
kw_added = _infer_added_params(kw_params)
1134+
return kw_params, kw_added
1135+
else:
1136+
return kw_params
1137+
1138+
1139+
def _infer_added_params(kw_params):
1140+
"""
1141+
Infer values for proplot's "added" parameters from stylesheets.
1142+
"""
1143+
kw_added = {}
1144+
mpl_to_proplot = {
1145+
'font.size': ('tick.labelsize',),
1146+
'axes.titlesize': (
1147+
'abc.size', 'suptitle.size', 'title.size',
1148+
'leftlabel.size', 'rightlabel.size',
1149+
'toplabel.size', 'bottomlabel.size',
1150+
),
1151+
'axes.facecolor': ('geoaxes.facecolor',),
1152+
'text.color': (
1153+
'abc.color', 'suptitle.color', 'tick.labelcolor', 'title.color',
1154+
'leftlabel.color', 'rightlabel.color',
1155+
'toplabel.color', 'bottomlabel.color',
1156+
),
1157+
}
1158+
for key, params in mpl_to_proplot.items():
1159+
if key in kw_params:
1160+
value = kw_params[key]
1161+
for param in params:
1162+
kw_added[param] = value
1163+
return kw_added
10271164

10281165

10291166
@docstring.add_snippets

0 commit comments

Comments
 (0)