diff --git a/proplot/cycles/538.hex b/proplot/cycles/538.hex index 52f7cec0b..a799a8f73 100644 --- a/proplot/cycles/538.hex +++ b/proplot/cycles/538.hex @@ -1 +1,2 @@ +# From the 538 matplotlib stylesheet '#008fd5', '#fc4f30', '#e5ae38', '#6d904f', '#8b8b8b', '#810f7c', diff --git a/proplot/cycles/Qual1.rgb b/proplot/cycles/Qual1.rgb index fea52946f..5fb0b09c4 100644 --- a/proplot/cycles/Qual1.rgb +++ b/proplot/cycles/Qual1.rgb @@ -1,3 +1,4 @@ +# Unknown source 91,190,148 145,190,100 229,207,108 diff --git a/proplot/cycles/Qual2.rgb b/proplot/cycles/Qual2.rgb index 592d6c8e4..689b8592a 100644 --- a/proplot/cycles/Qual2.rgb +++ b/proplot/cycles/Qual2.rgb @@ -1,3 +1,4 @@ +# Unknown source 91,190,148 182,227,209 145,190,100 diff --git a/proplot/cycles/bmh.hex b/proplot/cycles/bmh.hex new file mode 100644 index 000000000..52ec87ab4 --- /dev/null +++ b/proplot/cycles/bmh.hex @@ -0,0 +1,2 @@ +# From bmh stylesheet +'#348ABD', '#A60628', '#7A68A6', '#467821', '#D55E00', '#CC79A7', '#56B4E9', '#009E73', '#F0E442', '#0072B2' diff --git a/proplot/cycles/classic.hex b/proplot/cycles/classic.hex new file mode 100644 index 000000000..79fa00326 --- /dev/null +++ b/proplot/cycles/classic.hex @@ -0,0 +1,2 @@ +# Matplotlib classic 'bgrcmyk' style +'#0000ff', '#008000', '#ff0000', '#00bfbf', '#bf00bf', '#bfbf00', '#000000' diff --git a/proplot/cycles/colorblind.hex b/proplot/cycles/colorblind.hex index 7d7218dd4..23c0ad4fa 100644 --- a/proplot/cycles/colorblind.hex +++ b/proplot/cycles/colorblind.hex @@ -1 +1,2 @@ +# Seaborn and proplot default style '#0072B2', '#D55E00', '#009E73', '#CC79A7', '#F0E442', '#56B4E9', diff --git a/proplot/cycles/colorblind10.hex b/proplot/cycles/colorblind10.hex index 7d356f95b..d20d0a59a 100644 --- a/proplot/cycles/colorblind10.hex +++ b/proplot/cycles/colorblind10.hex @@ -1 +1,2 @@ +# Expanded seaborn default style "#0173B2", "#DE8F05", "#029E73", "#D55E00", "#CC78BC", "#CA9161", "#FBAFE4", "#949494", "#ECE133", "#56B4E9" diff --git a/proplot/cycles/default.hex b/proplot/cycles/default.hex index 9d8b2e450..cc5f3ddf0 100644 --- a/proplot/cycles/default.hex +++ b/proplot/cycles/default.hex @@ -1 +1,2 @@ +# Matplotlib latest default style '#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf', diff --git a/proplot/cycles/solarized.hex b/proplot/cycles/solarized.hex new file mode 100644 index 000000000..6417b59fb --- /dev/null +++ b/proplot/cycles/solarized.hex @@ -0,0 +1,2 @@ +# From solarized stylesheet +'#268BD2', '#2AA198', '#859900', '#B58900', '#CB4B16', '#DC322F', '#D33682', '#6C71C4' diff --git a/proplot/styletools.py b/proplot/styletools.py index 4e83e8c7e..20a581118 100644 --- a/proplot/styletools.py +++ b/proplot/styletools.py @@ -39,6 +39,26 @@ ] # Colormap stuff +CYCLES_TABLE = { + 'Matplotlib originals': ( + 'default', 'classic', + ), + 'Matplotlib stylesheets': ( + 'colorblind', 'colorblind10', 'ggplot', 'bmh', 'solarized', '538', + ), + 'ColorBrewer2.0 qualitative': ( + 'Accent', 'Dark2', + 'Paired', 'Pastel1', 'Pastel2', + 'Set1', 'Set2', 'Set3', + ), + 'Other qualitative': ( + 'FlatUI', 'Qual1', 'Qual2', 'Viz', + ), + 'ProPlot originals': ( + 'Cool', 'Warm', 'Hot', + 'Floral', 'Contrast', 'Sharp', + ), +} CMAPS_TABLE = { # Assorted origin, but these belong together 'Grayscale': ( @@ -126,7 +146,7 @@ ), 'Other': ( 'binary', 'bwr', 'brg', # appear to be custom matplotlib - 'cubehelix', 'wistia', 'CMRmap', # individually released + 'cubehelix', 'Wistia', 'CMRmap', # individually released 'seismic', 'terrain', 'nipy_spectral', # origin ambiguous 'tab10', 'tab20', 'tab20b', 'tab20c', # merged colormap cycles ) @@ -1835,17 +1855,23 @@ def __init__(self, kwargs): if not isinstance(key, str): raise KeyError(f'Invalid key {key}. Must be string.') self.__setitem__(key, value, sort=False) - for record in (cmaps, cycles): - record[:] = sorted(record) + try: + for record in (cmaps, cycles): + record[:] = sorted(record) + except NameError: + pass def __delitem__(self, key): """Delete the item from the list records.""" super().__delitem__(self, key) - for record in (cmaps, cycles): - try: - record.remove(key) - except ValueError: - pass + try: + for record in (cmaps, cycles): + try: + record.remove(key) + except ValueError: + pass + except NameError: + pass def __getitem__(self, key): """Retrieve the colormap associated with the sanitized key name. The @@ -1904,10 +1930,13 @@ def __setitem__(self, key, item, sort=True): 'matplotlib.colors.LinearSegmentedColormap.' ) key = self._sanitize_key(key, mirror=False) - record = cycles if isinstance(item, ListedColormap) else cmaps - record.append(key) - if sort: - record[:] = sorted(record) + try: + record = cycles if isinstance(item, ListedColormap) else cmaps + record.append(key) + if sort: + record[:] = sorted(record) + except NameError: + pass return super().__setitem__(key, item) def __contains__(self, item): @@ -1950,11 +1979,14 @@ def get(self, key, *args): def pop(self, key, *args): """Pop the sanitized colormap name.""" key = self._sanitize_key(key, mirror=True) - for record in (cmaps, cycles): - try: - record.remove(key) - except ValueError: - pass + try: + for record in (cmaps, cycles): + try: + record.remove(key) + except ValueError: + pass + except NameError: + pass return super().pop(key, *args) def update(self, *args, **kwargs): @@ -2836,8 +2868,11 @@ def _warn_or_raise(msg): # NOTE: This appears to be biggest import time bottleneck! Increases # time from 0.05s to 0.2s, with numpy loadtxt or with this regex thing. delim = re.compile(r'[,\s]+') - data = [delim.split(line.strip()) - for line in open(filename).readlines() if line.strip()] + data = [ + delim.split(line.strip()) + for line in open(filename).readlines() + if line.strip() and line.strip()[0] != '#' + ] try: data = [[float(num) for num in line] for line in data] except ValueError: @@ -3149,12 +3184,24 @@ def register_fonts(): fonts[:] = [*fonts_proplot, *fonts_system] -def _draw_bars(cmapdict, length=4.0, width=0.2): +def _draw_bars(names, *, source, unknown='User', length=4.0, width=0.2): """ Draw colorbars for "colormaps" and "color cycles". This is called by `show_cycles` and `show_cmaps`. """ - # Figure + # Categorize the input names + cmapdict = {} + names_all = list(map(str.lower, names)) + names_known = list(map(str.lower, sum(map(list, source.values()), []))) + names_unknown = [name for name in names if name not in names_known] + if names_unknown: + cmapdict[unknown] = names_unknown + for cat, names in source.items(): + names_cat = [name for name in names if name.lower() in names_all] + if names_cat: + cmapdict[cat] = names_cat + + # Draw figure from . import subplots naxs = len(cmapdict) + sum(map(len, cmapdict.values())) fig, axs = subplots( @@ -3166,8 +3213,6 @@ def _draw_bars(cmapdict, length=4.0, width=0.2): a = np.linspace(0, 1, 257).reshape(1, -1) a = np.vstack((a, a)) for cat, names in cmapdict.items(): - if not names: - continue nheads += 1 for imap, name in enumerate(names): iax += 1 @@ -3511,10 +3556,10 @@ def _color_filter(i, hcl): # noqa: E306 return figs -def show_cmaps(*args, N=None, unknown='User', **kwargs): +def show_cmaps(*args, N=None, **kwargs): """ - Generate a table of the registered colormaps or the input colormaps. - Adapted from `this example \ + Generate a table of the registered colormaps or the input colormaps + categorized by source. Adapted from `this example \ `__. Parameters @@ -3549,26 +3594,24 @@ def show_cmaps(*args, N=None, unknown='User', **kwargs): isinstance(mcm.cmap_d[name], LinearSegmentedColormap) ] - # Get dictionary of registered colormaps and their categories - cmapdict = {} - names_all = list(map(str.lower, names)) - names_known = sum(map(list, CMAPS_TABLE.values()), []) - cmapdict[unknown] = [name for name in names if name not in names_known] - for cat, names in CMAPS_TABLE.items(): - cmapdict[cat] = [name for name in names if name.lower() in names_all] - # Return figure of colorbars - return _draw_bars(cmapdict, **kwargs) + kwargs.setdefault('source', CMAPS_TABLE) + return _draw_bars(names, **kwargs) def show_cycles(*args, **kwargs): """ - Generate a table of registered color cycles or the input color cycles. + Generate a table of registered color cycles or the input color cycles + categorized by source. Adapted from `this example \ +`__. Parameters ---------- *args : colormap-spec, optional Cycle names or objects. + unknown : str, optional + Category name for cycles that are unknown to ProPlot. The + default is ``'User'``. length : float or str, optional The length of the colorbars. Units are interpreted by `~proplot.utils.units`. @@ -3591,8 +3634,8 @@ def show_cycles(*args, **kwargs): ] # Return figure of colorbars - cmapdict = {'Color cycles': names} - return _draw_bars(cmapdict, **kwargs) + kwargs.setdefault('source', CYCLES_TABLE) + return _draw_bars(names, **kwargs) def show_fonts(*args, size=12, text=None): @@ -3654,9 +3697,10 @@ def show_fonts(*args, size=12, text=None): # Apply custom changes -mcm.cmap_d['Grays'] = mcm.cmap_d.pop('Greys', None) # 'Murica (and consistency with registered colors) # noqa -mcm.cmap_d['Spectral'] = mcm.cmap_d['Spectral'].reversed( - name='Spectral') # make spectral go from 'cold' to 'hot' +if 'Greys' in mcm.cmap_d: # 'Murica (and consistency with registered colors) + mcm.cmap_d['Grays'] = mcm.cmap_d.pop('Greys') +if 'Spectral' in mcm.cmap_d: # make spectral go from 'cold' to 'hot' + mcm.cmap_d['Spectral'] = mcm.cmap_d['Spectral'].reversed(name='Spectral') for _name in CMAPS_TABLE['Matplotlib originals']: # initialize as empty lists if _name == 'twilight_shifted': mcm.cmap_d.pop(_name, None)