Skip to content

Commit ce7f656

Browse files
committed
Add a dedicated ColormapRegistry class
1 parent 2967365 commit ce7f656

File tree

5 files changed

+120
-3
lines changed

5 files changed

+120
-3
lines changed

.flake8

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ per-file-ignores =
4545
setupext.py: E501
4646
tests.py: F401
4747

48-
lib/matplotlib/__init__.py: F401
48+
lib/matplotlib/__init__.py: E401, F401
4949
lib/matplotlib/_api/__init__.py: F401
5050
lib/matplotlib/_cm.py: E122, E202, E203, E302
5151
lib/matplotlib/_mathtext.py: E221, E251

doc/api/matplotlib_configuration_api.rst

+21
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,27 @@ Logging
5252

5353
.. autofunction:: set_loglevel
5454

55+
Colormaps
56+
=========
57+
58+
.. py:data:: colormaps
59+
60+
A `.ColormapRegistry` instance holding all known colormaps.
61+
62+
Get a `.Colormap` using item access::
63+
64+
import matplotlib as mpl
65+
cmap = mpl.colormaps['viridis']
66+
67+
Returned `.Colormap`\s are copies, so that their modification does not
68+
change the global definition of the colormap.
69+
70+
Additional colormaps can be added via `.ColormapRegistry.register`::
71+
72+
mpl.colormaps.register(my_colormap)
73+
74+
.. autodata:: colormaps
75+
5576
Miscellaneous
5677
=============
5778

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Colormaps registry
2+
------------------
3+
4+
Colormaps are now managed via `matplotlib.colormaps`, which is a
5+
`.ColormapRegistry`.
6+
7+
Colormaps can be obtained using item access::
8+
9+
import matplotlib as mpl
10+
cmap = mpl.colormaps['viridis']
11+
12+
To register new colormaps use::
13+
14+
mpl.colormaps.register(my_colormap)
15+
16+
The use of `matplotlib.cm.get_cmap` and `matplotlib.cm.register_cmap` is
17+
discouraged in favor of the above. Within `.pyplot` the use of
18+
``plt.get_cmap()`` and ``plt.register_cmap()`` will continue to be supported.

lib/matplotlib/__init__.py

+5
Original file line numberDiff line numberDiff line change
@@ -1445,3 +1445,8 @@ def inner(ax, *args, data=None, **kwargs):
14451445
_log.debug('interactive is %s', is_interactive())
14461446
_log.debug('platform is %s', sys.platform)
14471447
_log.debug('loaded modules: %s', list(sys.modules))
1448+
1449+
1450+
# workaround: we must defer colormaps import to after loading rcParams, becuase
1451+
# colormap creation depends on rcParams
1452+
from matplotlib.cm import _colormaps as colormaps

lib/matplotlib/cm.py

+75-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
normalization.
1616
"""
1717

18-
from collections.abc import MutableMapping
18+
from collections.abc import Mapping, MutableMapping
1919

2020
import numpy as np
2121
from numpy import ma
@@ -91,13 +91,86 @@ def _warn_deprecated(self):
9191
)
9292

9393

94+
class ColormapRegistry(Mapping):
95+
r"""
96+
Container for colormaps that are known to Matplotlib by name.
97+
98+
There should be no need for users to instantiate this class themselves.
99+
100+
The universal registry instance is `matplotlib.colormaps`.
101+
102+
Read access uses a dict-like interface mapping names to `.Colormap`\s::
103+
104+
import matplotlib as mpl
105+
cmap = mpl.colormaps['viridis']
106+
107+
Returned `.Colormap`\s are copies, so that their modification does not
108+
change the global definition of the colormap.
109+
110+
Additional colormaps can be added via `.ColormapRegistry.register`::
111+
112+
mpl.colormaps.register(my_colormap)
113+
"""
114+
def __init__(self, cmaps):
115+
self._cmaps = cmaps
116+
117+
def __getitem__(self, item):
118+
try:
119+
return self._cmaps[item].copy()
120+
except KeyError:
121+
raise KeyError(f"{item!r} is not a known colormap name")
122+
123+
def __iter__(self):
124+
return iter(self._cmaps)
125+
126+
def __len__(self):
127+
return len(self._cmaps)
128+
129+
def __str__(self):
130+
return ('ColormapRegistry; available colormaps:\n' +
131+
', '.join(f"'{name}'" for name in self))
132+
133+
def register(self, cmap, *, name=None, force=False):
134+
"""
135+
Register a new colormap.
136+
137+
The colormap name can then be used as a string argument to any ``cmap``
138+
parameter in Matplotlib. It is also available in `.pyplot.get_cmap`.
139+
140+
The colormap registry stores a copy of the given colormap, so that
141+
future changes to the original colormap instance do not affect the
142+
registered colormap. Think of this as the registry taking a snapshot
143+
of the colormap at registration.
144+
145+
Parameters
146+
----------
147+
cmap : matplotlib.colors.Colormap
148+
The colormap to register.
149+
150+
name : str, optional
151+
The name for the colormap. If not given, the
152+
:attr:`~.Colormap.name` attribute of *cmap* is used.
153+
154+
force: bool, default: False
155+
156+
"""
157+
name_ = name or cmap.name
158+
if name_ in self and not force:
159+
raise ValueError(
160+
f'A colormap named "{name}" is already registered.')
161+
register_cmap(name, cmap.copy())
162+
163+
94164
_cmap_registry = _gen_cmap_registry()
95165
globals().update(_cmap_registry)
96166
# This is no longer considered public API
97167
cmap_d = _DeprecatedCmapDictWrapper(_cmap_registry)
98168
__builtin_cmaps = tuple(_cmap_registry)
99169

100-
# Continue with definitions ...
170+
# public acces to the colormaps should be via `matplotlib.colormaps`. For now,
171+
# we still create the registry here, but that should stay an implementation
172+
# detail.
173+
_colormaps = ColormapRegistry(_cmap_registry)
101174

102175

103176
def register_cmap(name=None, cmap=None, *, override_builtin=False):

0 commit comments

Comments
 (0)