diff --git a/doc/conf.py b/doc/conf.py index c0aea51fb761..6bef97d750cc 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -334,5 +334,19 @@ def getapi(*args): sys.modules['sip'] = mocksip sys.modules['PyQt4'] = mockpyqt4 -################# numpydoc config #################### +# numpydoc config + numpydoc_show_class_members = False + +# Skip deprecated members + +def skip_deprecated(app, what, name, obj, skip, options): + if skip: + return skip + skipped = {"matplotlib.colors": ["ColorConverter", "hex2color", "rgb2hex"]} + skip_list = skipped.get(getattr(obj, "__module__", None)) + if skip_list is not None: + return getattr(obj, "__name__", None) in skip_list + +def setup(app): + app.connect('autodoc-skip-member', skip_deprecated) diff --git a/doc/users/colors.rst b/doc/users/colors.rst index f935edd5c935..4ae509601351 100644 --- a/doc/users/colors.rst +++ b/doc/users/colors.rst @@ -9,7 +9,7 @@ it can be provided as: * ``(r, g, b)`` tuples * ``(r, g, b, a)`` tuples -* hex string, ex ``#OFOFOF`` +* hex string, ex ``#0F0F0F``, or ``#0F0F0F0F`` (with alpha channel) * float value between [0, 1] for gray level * One of ``{'b', 'g', 'r', 'c', 'm', 'y', 'k', 'w'}`` * valid CSS4/X11 color names diff --git a/doc/users/whats_new/rgba-support.rst b/doc/users/whats_new/rgba-support.rst new file mode 100644 index 000000000000..9a844e3825ae --- /dev/null +++ b/doc/users/whats_new/rgba-support.rst @@ -0,0 +1,11 @@ +Improved color conversion API and RGBA support +---------------------------------------------- + +The :module:`~matplotlib.colors` gained a new color conversion API with +full support for the alpha channel. The main public functions are +:func:`~matplotlib.colors.is_color_like`, :func:`matplotlib.colors.to_rgba`, +:func:`matplotlib.colors.to_rgba_array` and :func:`~matplotlib.colors.to_hex`. +RGBA quadruplets are encoded in hex format as `#rrggbbaa`. + +A side benefit is that the Qt options editor now allows setting the alpha +channel of the artists as well. diff --git a/examples/api/collections_demo.py b/examples/api/collections_demo.py index 6d58b180855a..1a704f79563d 100644 --- a/examples/api/collections_demo.py +++ b/examples/api/collections_demo.py @@ -17,8 +17,7 @@ ''' import matplotlib.pyplot as plt -from matplotlib import collections, transforms -from matplotlib.colors import colorConverter +from matplotlib import collections, colors, transforms import numpy as np nverts = 50 @@ -38,7 +37,7 @@ xyo = list(zip(xo, yo)) # Make a list of colors cycling through the default series. -colors = [colorConverter.to_rgba(c) +colors = [colors.to_rgba(c) for c in plt.rcParams['axes.prop_cycle'].by_key()['color']] fig, axes = plt.subplots(2, 2) diff --git a/examples/color/named_colors.py b/examples/color/named_colors.py index 82ae356031d0..232b514eda30 100644 --- a/examples/color/named_colors.py +++ b/examples/color/named_colors.py @@ -11,36 +11,19 @@ import numpy as np import matplotlib.pyplot as plt -from matplotlib import colors +from matplotlib import colors as mcolors -colors_ = list(six.iteritems(colors.cnames)) - -# Add the single letter colors. -for name, rgb in six.iteritems(colors.ColorConverter.colors): - hex_ = colors.rgb2hex(rgb) - colors_.append((name, hex_)) - -# Transform to hex color values. -hex_ = [color[1] for color in colors_] -# Get the rgb equivalent. -rgb = [colors.hex2color(color) for color in hex_] -# Get the hsv equivalent. -hsv = [colors.rgb_to_hsv(color) for color in rgb] - -# Split the hsv values to sort. -hue = [color[0] for color in hsv] -sat = [color[1] for color in hsv] -val = [color[2] for color in hsv] - -# Get the color names by themselves. -names = [color[0] for color in colors_] +colors = dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS) # Sort by hue, saturation, value and name. -ind = np.lexsort((names, val, sat, hue)) -sorted_colors = [colors_[i] for i in ind] +by_hsv = sorted((tuple(mcolors.rgb_to_hsv(mcolors.to_rgba(color)[:3])), name) + for name, color in colors.items()) + +# Get the sorted color names. +sorted_names = [name for hsv, name in by_hsv] -n = len(sorted_colors) +n = len(sorted_names) ncols = 4 nrows = int(np.ceil(1. * n / ncols)) @@ -53,7 +36,7 @@ # col width w = X / ncols -for i, (name, color) in enumerate(sorted_colors): +for i, name in enumerate(sorted_names): col = i % ncols row = int(i / ncols) y = Y - (row * h) - h @@ -68,8 +51,10 @@ # Add extra black line a little bit thicker to make # clear colors more visible. - ax.hlines(y, xi_line, xf_line, color='black', linewidth=(h * 0.7)) - ax.hlines(y + h * 0.1, xi_line, xf_line, color=color, linewidth=(h * 0.6)) + ax.hlines( + y, xi_line, xf_line, color='black', linewidth=(h * 0.7)) + ax.hlines( + y + h * 0.1, xi_line, xf_line, color=colors[name], linewidth=(h * 0.6)) ax.set_xlim(0, X) ax.set_ylim(0, Y) diff --git a/examples/event_handling/lasso_demo.py b/examples/event_handling/lasso_demo.py index 75e44286c548..c704692e1277 100644 --- a/examples/event_handling/lasso_demo.py +++ b/examples/event_handling/lasso_demo.py @@ -7,9 +7,8 @@ usable as is). There will be some refinement of the API. """ from matplotlib.widgets import Lasso -from matplotlib.colors import colorConverter from matplotlib.collections import RegularPolyCollection -from matplotlib import path +from matplotlib import colors as mcolors, path import matplotlib.pyplot as plt from numpy import nonzero @@ -17,8 +16,8 @@ class Datum(object): - colorin = colorConverter.to_rgba('red') - colorout = colorConverter.to_rgba('blue') + colorin = mcolors.to_rgba("red") + colorout = mcolors.to_rgba("blue") def __init__(self, x, y, include=False): self.x = x diff --git a/examples/mplot3d/polys3d_demo.py b/examples/mplot3d/polys3d_demo.py index 304c310e682a..7b94a19d04cb 100644 --- a/examples/mplot3d/polys3d_demo.py +++ b/examples/mplot3d/polys3d_demo.py @@ -5,8 +5,8 @@ from mpl_toolkits.mplot3d import Axes3D from matplotlib.collections import PolyCollection -from matplotlib.colors import colorConverter import matplotlib.pyplot as plt +from matplotlib import colors as mcolors import numpy as np @@ -14,7 +14,7 @@ def cc(arg): ''' Shorthand to convert 'named' colors to rgba format at 60% opacity. ''' - return colorConverter.to_rgba(arg, alpha=0.6) + return mcolors.to_rgba(arg, alpha=0.6) def polygon_under_graph(xlist, ylist): diff --git a/examples/pylab_examples/colours.py b/examples/pylab_examples/colours.py index b34d6a6d97e6..adcbd4331317 100644 --- a/examples/pylab_examples/colours.py +++ b/examples/pylab_examples/colours.py @@ -3,12 +3,12 @@ Some simple functions to generate colours. """ import numpy as np -from matplotlib.colors import colorConverter +from matplotlib import colors as mcolors def pastel(colour, weight=2.4): """ Convert colour into a nice pastel shade""" - rgb = np.asarray(colorConverter.to_rgb(colour)) + rgb = np.asarray(mcolors.to_rgba(colour)[:3]) # scale colour maxc = max(rgb) if maxc < 1.0 and maxc > 0: diff --git a/examples/pylab_examples/demo_ribbon_box.py b/examples/pylab_examples/demo_ribbon_box.py index df39dede071a..59eeddaa9f48 100644 --- a/examples/pylab_examples/demo_ribbon_box.py +++ b/examples/pylab_examples/demo_ribbon_box.py @@ -18,7 +18,7 @@ class RibbonBox(object): nx = original_image.shape[1] def __init__(self, color): - rgb = matplotlib.colors.colorConverter.to_rgb(color) + rgb = matplotlib.colors.to_rgba(color)[:3] im = np.empty(self.original_image.shape, self.original_image.dtype) diff --git a/examples/pylab_examples/line_collection.py b/examples/pylab_examples/line_collection.py index aefa7d0fe389..22ccbc37c0bc 100644 --- a/examples/pylab_examples/line_collection.py +++ b/examples/pylab_examples/line_collection.py @@ -1,6 +1,6 @@ import matplotlib.pyplot as plt from matplotlib.collections import LineCollection -from matplotlib.colors import colorConverter +from matplotlib import colors as mcolors import numpy as np @@ -30,7 +30,7 @@ # where onoffseq is an even length tuple of on and off ink in points. # If linestyle is omitted, 'solid' is used # See matplotlib.collections.LineCollection for more information -colors = [colorConverter.to_rgba(c) +colors = [mcolors.to_rgba(c) for c in plt.rcParams['axes.prop_cycle'].by_key()['color']] line_segments = LineCollection(segs, linewidths=(0.5, 1, 1.5, 2), diff --git a/examples/widgets/menu.py b/examples/widgets/menu.py index d6c5be4dfb98..846dd5ce1917 100644 --- a/examples/widgets/menu.py +++ b/examples/widgets/menu.py @@ -17,8 +17,8 @@ def __init__(self, fontsize=14, labelcolor='black', bgcolor='yellow', self.bgcolor = bgcolor self.alpha = alpha - self.labelcolor_rgb = colors.colorConverter.to_rgb(labelcolor) - self.bgcolor_rgb = colors.colorConverter.to_rgb(bgcolor) + self.labelcolor_rgb = colors.to_rgba(labelcolor)[:3] + self.bgcolor_rgb = colors.to_rgba(bgcolor)[:3] class MenuItem(artist.Artist): diff --git a/lib/matplotlib/_color_data.py b/lib/matplotlib/_color_data.py index f19c3e5d4af6..5d6aae709971 100644 --- a/lib/matplotlib/_color_data.py +++ b/lib/matplotlib/_color_data.py @@ -3,6 +3,18 @@ from matplotlib.externals import six + +BASE_COLORS = { + 'b': (0, 0, 1), + 'g': (0, 0.5, 0), + 'r': (1, 0, 0), + 'c': (0, 0.75, 0.75), + 'm': (0.75, 0, 0.75), + 'y': (0.75, 0.75, 0), + 'k': (0, 0, 0), + 'w': (1, 1, 1)} + + # This mapping of color names -> hex values is taken from # a survey run by Randel Monroe see: # http://blog.xkcd.com/2010/05/03/color-survey-results/ diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 57c138f9e300..7ed60b66cdde 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -2066,7 +2066,7 @@ def make_iterable(x): if color is None: color = [None] * nbars else: - color = list(mcolors.colorConverter.to_rgba_array(color)) + color = list(mcolors.to_rgba_array(color)) if len(color) == 0: # until to_rgba_array is changed color = [[0, 0, 0, 0]] if len(color) < nbars: @@ -2075,7 +2075,7 @@ def make_iterable(x): if edgecolor is None: edgecolor = [None] * nbars else: - edgecolor = list(mcolors.colorConverter.to_rgba_array(edgecolor)) + edgecolor = list(mcolors.to_rgba_array(edgecolor)) if len(edgecolor) == 0: # until to_rgba_array is changed edgecolor = [[0, 0, 0, 0]] if len(edgecolor) < nbars: @@ -3844,7 +3844,7 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None, co = kwargs.pop('color', None) if co is not None: try: - mcolors.colorConverter.to_rgba_array(co) + mcolors.to_rgba_array(co) except ValueError: raise ValueError("'color' kwarg must be an mpl color" " spec or sequence of color specs.\n" @@ -6045,7 +6045,7 @@ def _normalize_input(inp, ename='input'): if color is None: color = [self._get_lines.get_next_color() for i in xrange(nx)] else: - color = mcolors.colorConverter.to_rgba_array(color) + color = mcolors.to_rgba_array(color) if len(color) != nx: raise ValueError("color kwarg must have one color per dataset") diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 74b84cc104b0..3aa9af474ed3 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -68,7 +68,7 @@ def _process_plot_format(fmt): # Is fmt just a colorspec? try: - color = mcolors.colorConverter.to_rgb(fmt) + color = mcolors.to_rgba(fmt) # We need to differentiate grayscale '1.0' from tri_down marker '1' try: @@ -112,14 +112,14 @@ def _process_plot_format(fmt): raise ValueError( 'Illegal format string "%s"; two marker symbols' % fmt) marker = c - elif c in mcolors.colorConverter.colors: + elif c in mcolors.get_named_colors_mapping(): if color is not None: raise ValueError( 'Illegal format string "%s"; two color symbols' % fmt) color = c elif c == 'C' and i < len(chars) - 1: color_cycle_number = int(chars[i + 1]) - color = mcolors.colorConverter._get_nth_color(color_cycle_number) + color = mcolors.to_rgba("C{}".format(color_cycle_number)) i += 1 else: raise ValueError( @@ -3687,7 +3687,7 @@ def set_cursor_props(self, *args): lw, c = args else: raise ValueError('args must be a (linewidth, color) tuple') - c = mcolors.colorConverter.to_rgba(c) + c = mcolors.to_rgba(c) self._cursorProps = lw, c def get_children(self): diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index fe7310155336..277a06b47a1b 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -1022,11 +1022,11 @@ def set_foreground(self, fg, isRGBA=False): if self._forced_alpha and isRGBA: self._rgb = fg[:3] + (self._alpha,) elif self._forced_alpha: - self._rgb = colors.colorConverter.to_rgba(fg, self._alpha) + self._rgb = colors.to_rgba(fg, self._alpha) elif isRGBA: self._rgb = fg else: - self._rgb = colors.colorConverter.to_rgba(fg) + self._rgb = colors.to_rgba(fg) def set_graylevel(self, frac): """ diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index ea4d78f2a1b3..aaa003804373 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -568,9 +568,8 @@ def print_jpg(self, filename_or_obj, *args, **kwargs): # The image is "pasted" onto a white background image to safely # handle any transparency image = Image.frombuffer('RGBA', size, buf, 'raw', 'RGBA', 0, 1) - color = mcolors.colorConverter.to_rgb( - rcParams.get('savefig.facecolor', 'white')) - color = tuple([int(x * 255.0) for x in color]) + rgba = mcolors.to_rgba(rcParams.get('savefig.facecolor', 'white')) + color = tuple([int(x * 255.0) for x in rgba[:3]]) background = Image.new('RGB', size, color) background.paste(image, image) options = restrict_dict(kwargs, ['quality', 'optimize', diff --git a/lib/matplotlib/backends/backend_gtk.py b/lib/matplotlib/backends/backend_gtk.py index 7ebca484d7b2..de5fcce665bf 100644 --- a/lib/matplotlib/backends/backend_gtk.py +++ b/lib/matplotlib/backends/backend_gtk.py @@ -35,15 +35,11 @@ def fn_name(): return sys._getframe(1).f_code.co_name from matplotlib.backends.backend_gdk import RendererGDK, FigureCanvasGDK from matplotlib.cbook import is_string_like, is_writable_file_like -from matplotlib.colors import colorConverter from matplotlib.figure import Figure from matplotlib.widgets import SubplotTool -from matplotlib import lines -from matplotlib import markers -from matplotlib import cbook -from matplotlib import verbose -from matplotlib import rcParams +from matplotlib import ( + cbook, colors as mcolors, lines, markers, rcParams, verbose) backend_version = "%d.%d.%d" % gtk.pygtk_version @@ -1003,13 +999,13 @@ def on_combobox_lineprops_changed(self, item): if marker is None: marker = 'None' self.cbox_markers.set_active(self.markerd[marker]) - r,g,b = colorConverter.to_rgb(line.get_color()) - color = gtk.gdk.Color(*[int(val*65535) for val in (r,g,b)]) + rgba = mcolors.to_rgba(line.get_color()) + color = gtk.gdk.Color(*[int(val*65535) for val in rgba[:3]]) button = self.wtree.get_widget('colorbutton_linestyle') button.set_color(color) - r,g,b = colorConverter.to_rgb(line.get_markerfacecolor()) - color = gtk.gdk.Color(*[int(val*65535) for val in (r,g,b)]) + rgba = mcolors.to_rgba(line.get_markerfacecolor()) + color = gtk.gdk.Color(*[int(val*65535) for val in rgba[:3]]) button = self.wtree.get_widget('colorbutton_markerface') button.set_color(color) self._updateson = True diff --git a/lib/matplotlib/backends/backend_gtk3.py b/lib/matplotlib/backends/backend_gtk3.py index d8f2910c7f35..4f2ef04c5873 100644 --- a/lib/matplotlib/backends/backend_gtk3.py +++ b/lib/matplotlib/backends/backend_gtk3.py @@ -36,14 +36,10 @@ def fn_name(): return sys._getframe(1).f_code.co_name from matplotlib import backend_tools from matplotlib.cbook import is_string_like, is_writable_file_like -from matplotlib.colors import colorConverter from matplotlib.figure import Figure from matplotlib.widgets import SubplotTool -from matplotlib import lines -from matplotlib import cbook -from matplotlib import verbose -from matplotlib import rcParams +from matplotlib import cbook, colors as mcolors, lines, verbose, rcParams backend_version = "%s.%s.%s" % (Gtk.get_major_version(), Gtk.get_micro_version(), Gtk.get_minor_version()) diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 5576def9d440..60fed3e41403 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -615,7 +615,7 @@ def set_facecolor(self, c): if c is None: c = mpl.rcParams['patch.facecolor'] self._facecolors_original = c - self._facecolors = mcolors.colorConverter.to_rgba_array(c, self._alpha) + self._facecolors = mcolors.to_rgba_array(c, self._alpha) self.stale = True def set_facecolors(self, c): @@ -663,7 +663,7 @@ def set_edgecolor(self, c): if c is None: c = mpl.rcParams['patch.edgecolor'] self._edgecolors_original = c - self._edgecolors = mcolors.colorConverter.to_rgba_array(c, self._alpha) + self._edgecolors = mcolors.to_rgba_array(c, self._alpha) self.stale = True def set_edgecolors(self, c): @@ -684,14 +684,14 @@ def set_alpha(self, alpha): raise TypeError('alpha must be a float or None') artist.Artist.set_alpha(self, alpha) try: - self._facecolors = mcolors.colorConverter.to_rgba_array( + self._facecolors = mcolors.to_rgba_array( self._facecolors_original, self._alpha) except (AttributeError, TypeError, IndexError): pass try: if (not isinstance(self._edgecolors_original, six.string_types) or self._edgecolors_original != str('face')): - self._edgecolors = mcolors.colorConverter.to_rgba_array( + self._edgecolors = mcolors.to_rgba_array( self._edgecolors_original, self._alpha) except (AttributeError, TypeError, IndexError): pass @@ -1137,7 +1137,7 @@ def __init__(self, segments, # Can be None. if antialiaseds is None: antialiaseds = (mpl.rcParams['lines.antialiased'],) - colors = mcolors.colorConverter.to_rgba_array(colors) + colors = mcolors.to_rgba_array(colors) Collection.__init__( self, diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index 5c5fa7fd83c8..680a7bf12c51 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -15,21 +15,23 @@ :class:`ListedColormap`, which is used for generating a custom colormap from a list of color specifications. -The module also provides a single instance, *colorConverter*, of the -:class:`ColorConverter` class providing methods for converting single color -specifications or sequences of them to *RGB* or *RGBA*. +The module also provides functions for checking whether an object can be +interpreted as a color (:func:`is_color_like`), for converting such an object +to an RGBA tuple (:func:`to_rgba`) or to an HTML-like hex string in the +`#rrggbb` format (:func:`to_hex`), and a sequence of colors to an `(n, 4)` +RGBA array (:func:`to_rgba_array`). Caching is used for efficiency. Commands which take color arguments can use several formats to specify the colors. For the basic built-in colors, you can use a single letter - - b: blue - - g: green - - r: red - - c: cyan - - m: magenta - - y: yellow - - k: black - - w: white + - `b`: blue + - `g`: green + - `r`: red + - `c`: cyan + - `m`: magenta + - `y`: yellow + - `k`: black + - `w`: white To use the colors that are part of the active color cycle in the current style, use `C` followed by a digit. For example: @@ -44,14 +46,16 @@ For a greater range of colors, you have two options. You can specify the color using an html hex string, as in:: - color = '#eeefff' + color = '#eeefff' -or you can pass an *R* , *G* , *B* tuple, where each of *R* , *G* , *B* are in -the range [0,1]. +(possibly specifying an alpha value as well), or you can pass an `(r, g, b)` +or `(r, g, b, a)` tuple, where each of `r`, `g`, `b` and `a` are in the range +[0,1]. Finally, legal html names for colors, like 'red', 'burlywood' and 'chartreuse' are supported. """ + from __future__ import (absolute_import, division, print_function, unicode_literals) import re @@ -62,55 +66,211 @@ import numpy as np from numpy import ma import matplotlib.cbook as cbook -from ._color_data import XKCD_COLORS, CSS4_COLORS +from ._color_data import BASE_COLORS, CSS4_COLORS, XKCD_COLORS -# for back copatibility -cnames = CSS4_COLORS -COLOR_NAMES = {'xkcd': XKCD_COLORS, - 'css4': CSS4_COLORS} +class _ColorMapping(dict): + def __init__(self, mapping): + super(_ColorMapping, self).__init__(mapping) + self.cache = {} + def __setitem__(self, key, value): + super(_ColorMapping, self).__setitem__(key, value) + self.cache.clear() -def is_color_like(c): - 'Return *True* if *c* can be converted to *RGB*' - - # Special-case the N-th color cycle syntax, because its parsing - # needs to be deferred. We may be reading a value from rcParams - # here before the color_cycle rcParam has been parsed. - if isinstance(c, bytes): - match = re.match(b'^C[0-9]$', c) - if match is not None: - return True - elif isinstance(c, six.text_type): - match = re.match('^C[0-9]$', c) - if match is not None: - return True + def __delitem__(self, key, value): + super(_ColorMapping, self).__delitem__(key, value) + self.cache.clear() - try: - colorConverter.to_rgb(c) + +_colors_full_map = {} +# Set by reverse priority order. +_colors_full_map.update(XKCD_COLORS) +_colors_full_map.update(CSS4_COLORS) +_colors_full_map.update(BASE_COLORS) +_colors_full_map = _ColorMapping(_colors_full_map) + + +def get_named_colors_mapping(): + """Return the global mapping of names to named colors. + """ + return _colors_full_map + + +def _is_nth_color(c): + """Return whether `c` can be interpreted as an item in the color cycle. + """ + return isinstance(c, six.string_types) and re.match(r"\AC[0-9]\Z", c) + + +def is_color_like(c): + """Return whether `c` can be interpreted as an RGB(A) color. + """ + # Special-case nth color syntax because it cannot be parsed during + # setup. + if _is_nth_color(c): return True + try: + to_rgba(c) except ValueError: return False + else: + return True -def rgb2hex(rgb): - 'Given an rgb or rgba sequence of 0-1 floats, return the hex string' - a = '#%02x%02x%02x' % tuple([int(np.round(val * 255)) for val in rgb[:3]]) - return a +def to_rgba(c, alpha=None): + """Convert `c` to an RGBA color. + + If `alpha` is not `None`, it forces the alpha value. + """ + # Special-case nth color syntax because it should not be cached. + if _is_nth_color(c): + from matplotlib import rcParams + from matplotlib.rcsetup import cycler + prop_cycler = rcParams['axes.prop_cycle'] + if prop_cycler is None and 'axes.color_cycle' in rcParams: + clist = rcParams['axes.color_cycle'] + prop_cycler = cycler('color', clist) + colors = prop_cycler._transpose().get('color', 'k') + c = colors[int(c[1]) % len(colors)] + try: + rgba = _colors_full_map.cache[c, alpha] + except (KeyError, TypeError): # Not in cache, or unhashable. + rgba = _to_rgba_no_colorcycle(c, alpha) + try: + _colors_full_map.cache[c, alpha] = rgba + except TypeError: + pass + return rgba + + +def _to_rgba_no_colorcycle(c, alpha=None): + """Convert `c` to an RGBA color, with no support for color-cycle syntax. + + If `alpha` is not `None`, it forces the alpha value. + """ + orig_c = c + if isinstance(c, six.string_types) and c.lower() == "none": + return (0., 0., 0., 0.) + if isinstance(c, six.string_types): + # Named color. + try: + c = _colors_full_map[c.lower()] + except KeyError: + pass + if isinstance(c, six.string_types): + # hex color with no alpha. + match = re.match(r"\A#[a-fA-F0-9]{6}\Z", c) + if match: + return (tuple(int(n, 16) / 255 + for n in [c[1:3], c[3:5], c[5:7]]) + + (alpha if alpha is not None else 1.,)) + if isinstance(c, six.string_types): + # hex color with alpha. + match = re.match(r"\A#[a-fA-F0-9]{8}\Z", c) + if match: + color = [int(n, 16) / 255 + for n in [c[1:3], c[3:5], c[5:7], c[7:9]]] + if alpha is not None: + color[-1] = alpha + return tuple(color) + if isinstance(c, six.string_types): + # string gray. + try: + return (float(c),) * 3 + (1.,) + except ValueError: + pass + raise ValueError("Invalid RGBA argument: {!r}".format(orig_c)) + # tuple color. + # Python 2.7 / numpy 1.6 apparently require this to return builtin floats, + # not numpy floats. + try: + c = tuple(map(float, c)) + except TypeError: + raise ValueError("Invalid RGBA argument: {!r}".format(orig_c)) + if len(c) not in [3, 4]: + raise ValueError("RGBA sequence should have length 3 or 4") + if len(c) == 3 and alpha is None: + alpha = 1 + if alpha is not None: + c = c[:3] + (alpha,) + if any(elem < 0 or elem > 1 for elem in c): + raise ValueError("RGBA values should be within 0-1 range") + return c + + +def to_rgba_array(c, alpha=None): + """Convert `c` to a (n, 4) array of RGBA colors. + + If `alpha` is not `None`, it forces the alpha value. If `c` is "none" + (case-insensitive) or an empty list, an empty array is returned. + """ + # Single value? + if isinstance(c, six.string_types) and c.lower() == "none": + return np.zeros((0, 4), float) + try: + return np.array([to_rgba(c, alpha)], float) + except (ValueError, TypeError): + pass + # Special-case inputs that are already arrays, for performance. (If the + # array has the wrong kind or shape, raise the error during one-at-a-time + # conversion.) + if (isinstance(c, np.ndarray) and c.dtype.kind in "if" + and c.ndim == 2 and c.shape[1] in [3, 4]): + if c.shape[1] == 3: + result = np.column_stack([c, np.zeros(len(c))]) + result[:, -1] = alpha if alpha is not None else 1. + elif c.shape[1] == 4: + result = c.copy() + if alpha is not None: + result[:, -1] = alpha + if np.any((result < 0) | (result > 1)): + raise ValueError("RGBA values should be within 0-1 range") + return result + # Convert one at a time. + result = np.empty((len(c), 4), float) + for i, cc in enumerate(c): + result[i] = to_rgba(cc, alpha) + return result + + +def to_rgb(c): + """Convert `c` to an RGB color, silently dropping the alpha channel. + """ + return to_rgba(c)[:3] + +def to_hex(c, keep_alpha=False): + """Convert `c` to a hex color. + + Uses the #rrggbb format if `keep_alpha` is False (the default), `#rrggbbaa` + otherwise. + """ + c = to_rgba(c) + if not keep_alpha: + c = c[:3] + return "#" + "".join(format(int(np.round(val * 255)), "02x") + for val in c) + + +### Backwards-compatible color-conversion API + +cnames = CSS4_COLORS +COLOR_NAMES = {'xkcd': XKCD_COLORS, 'css4': CSS4_COLORS} hexColorPattern = re.compile("\A#[a-fA-F0-9]{6}\Z") -def hex2color(s): +def rgb2hex(c): + 'Given an rgb or rgba sequence of 0-1 floats, return the hex string' + return to_hex(c) + + +def hex2color(c): """ Take a hex string *s* and return the corresponding rgb 3-tuple Example: #efefef -> (0.93725, 0.93725, 0.93725) """ - if not isinstance(s, six.string_types): - raise TypeError('hex2color requires a string argument') - if hexColorPattern.match(s) is None: - raise ValueError('invalid hex color string "%s"' % s) - return tuple([int(n, 16) / 255.0 for n in (s[1:3], s[3:5], s[5:7])]) + return ColorConverter.to_rgb(c) class ColorConverter(object): @@ -123,47 +283,12 @@ class ColorConverter(object): Ordinarily only the single instance instantiated in this module, *colorConverter*, is needed. """ - colors = { - 'b': (0, 0, 1), - 'g': (0, 0.5, 0), - 'r': (1, 0, 0), - 'c': (0, 0.75, 0.75), - 'm': (0.75, 0, 0.75), - 'y': (0.75, 0.75, 0), - 'k': (0, 0, 0), - 'w': (1, 1, 1)} - - _prop_cycler = None - cache = {} - CN_LOOKUPS = [COLOR_NAMES[k] for k in ['css4', 'xkcd']] + colors = _colors_full_map + cache = _colors_full_map.cache - @classmethod - def _get_nth_color(cls, val): - """ - Get the Nth color in the current color cycle. If N is greater - than the number of colors in the cycle, it is wrapped around. - """ - from matplotlib.rcsetup import cycler - from matplotlib import rcParams - - prop_cycler = rcParams['axes.prop_cycle'] - if prop_cycler is None and 'axes.color_cycle' in rcParams: - clist = rcParams['axes.color_cycle'] - prop_cycler = cycler('color', clist) - - colors = prop_cycler._transpose()['color'] - return colors[val % len(colors)] - - @classmethod - def _parse_nth_color(cls, val): - match = re.match('^C[0-9]$', val) - if match is not None: - return cls._get_nth_color(int(val[1])) - - raise ValueError("Not a color cycle color") - - def to_rgb(self, arg): + @staticmethod + def to_rgb(arg): """ Returns an *RGB* tuple of three floats from 0-1. @@ -178,70 +303,10 @@ def to_rgb(self, arg): if *arg* is *RGBA*, the *A* will simply be discarded. """ - # Gray must be a string to distinguish 3-4 grays from RGB or RGBA. + return to_rgb(arg) - try: - return self.cache[arg] - except KeyError: - pass - except TypeError: # could be unhashable rgb seq - arg = tuple(arg) - try: - return self.cache[arg] - except KeyError: - pass - except TypeError: - raise ValueError( - 'to_rgb: arg "%s" is unhashable even inside a tuple' - % (str(arg),)) - try: - if cbook.is_string_like(arg): - argl = arg.lower() - color = self.colors.get(argl, None) - if color is None: - try: - argl = self._parse_nth_color(arg) - # in this case we do not want to cache in case - # the rcparam changes, recurse with the actual color - # value - return self.to_rgb(argl) - except ValueError: - pass - for cmapping in self.CN_LOOKUPS: - str1 = cmapping.get(argl, argl) - if str1 != argl: - break - if str1.startswith('#'): - color = hex2color(str1) - else: - fl = float(argl) - if fl < 0 or fl > 1: - raise ValueError( - 'gray (string) must be in range 0-1') - color = (fl,) * 3 - elif cbook.iterable(arg): - if len(arg) > 4 or len(arg) < 3: - raise ValueError( - 'sequence length is %d; must be 3 or 4' % len(arg)) - color = tuple(arg[:3]) - if [x for x in color if (float(x) < 0) or (x > 1)]: - # This will raise TypeError if x is not a number. - raise ValueError( - 'number in rbg sequence outside 0-1 range') - else: - raise ValueError( - 'cannot convert argument to rgb sequence') - self.cache[arg] = color - - except (KeyError, ValueError, TypeError) as exc: - raise ValueError( - 'to_rgb: Invalid rgb arg "%s"\n%s' % (str(arg), exc)) - # Error messages could be improved by handling TypeError - # separately; but this should be rare and not too hard - # for the user to figure out as-is. - return color - - def to_rgba(self, arg, alpha=None): + @staticmethod + def to_rgba(arg, alpha=None): """ Returns an *RGBA* tuple of four floats from 0-1. @@ -251,42 +316,10 @@ def to_rgba(self, arg, alpha=None): If *arg* is an *RGBA* sequence and *alpha* is not *None*, *alpha* will replace the original *A*. """ - try: - if arg.lower() == 'none': - return (0.0, 0.0, 0.0, 0.0) - except AttributeError: - pass - - if alpha is not None and (alpha < 0.0 or alpha > 1.0): - raise ValueError("alpha must be in range 0-1") + return to_rgba(arg, alpha) - try: - if not cbook.is_string_like(arg) and cbook.iterable(arg): - if len(arg) == 4: - if any(float(x) < 0 or x > 1 for x in arg): - raise ValueError( - 'number in rbga sequence outside 0-1 range') - if alpha is None: - return tuple(arg) - return arg[0], arg[1], arg[2], alpha - if len(arg) == 3: - r, g, b = arg - if any(float(x) < 0 or x > 1 for x in arg): - raise ValueError( - 'number in rbg sequence outside 0-1 range') - else: - raise ValueError( - 'length of rgba sequence should be either 3 or 4') - else: - r, g, b = self.to_rgb(arg) - if alpha is None: - alpha = 1.0 - return r, g, b, alpha - except (TypeError, ValueError) as exc: - raise ValueError( - 'to_rgba: Invalid rgba arg "%s"\n%s' % (str(arg), exc)) - - def to_rgba_array(self, c, alpha=None): + @staticmethod + def to_rgba_array(arg, alpha=None): """ Returns a numpy array of *RGBA* tuples. @@ -295,47 +328,13 @@ def to_rgba_array(self, c, alpha=None): Special case to handle "no color": if *c* is "none" (case-insensitive), then an empty array will be returned. Same for an empty list. """ - try: - nc = len(c) - except TypeError: - raise ValueError( - "Cannot convert argument type %s to rgba array" % type(c)) - try: - if nc == 0 or c.lower() == 'none': - return np.zeros((0, 4), dtype=np.float) - except AttributeError: - pass - try: - # Single value? Put it in an array with a single row. - return np.array([self.to_rgba(c, alpha)], dtype=np.float) - except ValueError: - if isinstance(c, np.ndarray): - if c.ndim != 2 and c.dtype.kind not in 'SU': - raise ValueError("Color array must be two-dimensional") - if (c.ndim == 2 and c.shape[1] == 4 and c.dtype.kind == 'f'): - if (c.ravel() > 1).any() or (c.ravel() < 0).any(): - raise ValueError( - "number in rgba sequence is outside 0-1 range") - result = np.asarray(c, np.float) - if alpha is not None: - if alpha > 1 or alpha < 0: - raise ValueError("alpha must be in 0-1 range") - result[:, 3] = alpha - return result - # This alpha operation above is new, and depends - # on higher levels to refrain from setting alpha - # to values other than None unless there is - # intent to override any existing alpha values. - - # It must be some other sequence of color specs. - result = np.zeros((nc, 4), dtype=np.float) - for i, cc in enumerate(c): - result[i] = self.to_rgba(cc, alpha) - return result + return to_rgba_array(arg, alpha) colorConverter = ColorConverter() +### End of backwards-compatible color-conversion API + def makeMappingArray(N, data, gamma=1.0): """Create an *N* -element 1-d lookup table diff --git a/lib/matplotlib/finance.py b/lib/matplotlib/finance.py index 6b5b1b3e510d..cbdb592c8b3e 100644 --- a/lib/matplotlib/finance.py +++ b/lib/matplotlib/finance.py @@ -21,11 +21,10 @@ import numpy as np -from matplotlib import verbose, get_cachedir +from matplotlib import colors as mcolors, verbose, get_cachedir from matplotlib.dates import date2num from matplotlib.cbook import iterable, mkdirs from matplotlib.collections import LineCollection, PolyCollection -from matplotlib.colors import colorConverter from matplotlib.lines import Line2D, TICKLEFT, TICKRIGHT from matplotlib.patches import Rectangle from matplotlib.transforms import Affine2D @@ -969,13 +968,9 @@ def plot_day_summary2_ohlc(ax, opens, highs, lows, closes, ticksize=4, tickTransform = Affine2D().scale(scale, 0.0) - r, g, b = colorConverter.to_rgb(colorup) - colorup = r, g, b, 1 - r, g, b = colorConverter.to_rgb(colordown) - colordown = r, g, b, 1 - colord = {True: colorup, - False: colordown, - } + colorup = mcolors.to_rgba(colorup) + colordown = mcolors.to_rgba(colordown) + colord = {True: colorup, False: colordown} colors = [colord[open < close] for open, close in zip(opens, closes) if open != -1 and close != -1] @@ -1113,13 +1108,9 @@ def candlestick2_ohlc(ax, opens, highs, lows, closes, width=4, for i, low, high in zip(xrange(len(lows)), lows, highs) if low != -1] - r, g, b = colorConverter.to_rgb(colorup) - colorup = r, g, b, alpha - r, g, b = colorConverter.to_rgb(colordown) - colordown = r, g, b, alpha - colord = {True: colorup, - False: colordown, - } + colorup = mcolors.to_rgba(colorup, alpha) + colordown = mcolors.to_rgba(colordown, alpha) + colord = {True: colorup, False: colordown} colors = [colord[open < close] for open, close in zip(opens, closes) if open != -1 and close != -1] @@ -1186,13 +1177,9 @@ def volume_overlay(ax, opens, closes, volumes, """ - r, g, b = colorConverter.to_rgb(colorup) - colorup = r, g, b, alpha - r, g, b = colorConverter.to_rgb(colordown) - colordown = r, g, b, alpha - colord = {True: colorup, - False: colordown, - } + colorup = mcolors.to_rgba(colorup, alpha) + colordown = mcolors.to_rgba(colordown, alpha) + colord = {True: colorup, False: colordown} colors = [colord[open < close] for open, close in zip(opens, closes) if open != -1 and close != -1] @@ -1288,13 +1275,9 @@ def volume_overlay3(ax, quotes, """ - r, g, b = colorConverter.to_rgb(colorup) - colorup = r, g, b, alpha - r, g, b = colorConverter.to_rgb(colordown) - colordown = r, g, b, alpha - colord = {True: colorup, - False: colordown, - } + colorup = mcolors.to_rgba(colorup, alpha) + colordown = mcolors.to_rgba(colordown, alpha) + colord = {True: colorup, False: colordown} dates, opens, highs, lows, closes, volumes = list(zip(*quotes)) colors = [colord[close1 >= close0] @@ -1369,8 +1352,8 @@ def index_bar(ax, vals, """ - facecolors = (colorConverter.to_rgba(facecolor, alpha),) - edgecolors = (colorConverter.to_rgba(edgecolor, alpha),) + facecolors = (mcolors.to_rgba(facecolor, alpha),) + edgecolors = (mcolors.to_rgba(edgecolor, alpha),) right = width / 2.0 left = -width / 2.0 diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index a29e62cc78a7..e705f2bbf94c 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -907,7 +907,7 @@ def make_image(self, renderer, magnification=1.0, unsampled=False): if unsampled: raise ValueError('unsampled not supported on PColorImage') fc = self.axes.patch.get_facecolor() - bg = mcolors.colorConverter.to_rgba(fc, 0) + bg = mcolors.to_rgba(fc, 0) bg = (np.array(bg)*255).astype(np.uint8) l, b, r, t = self.axes.bbox.extents width = (np.round(r) + 0.5) - (np.round(l) - 0.5) diff --git a/lib/matplotlib/legend_handler.py b/lib/matplotlib/legend_handler.py index d3574bf48190..fb3f5238c4ec 100644 --- a/lib/matplotlib/legend_handler.py +++ b/lib/matplotlib/legend_handler.py @@ -635,7 +635,7 @@ class HandlerPolyCollection(HandlerBase): """ def _update_prop(self, legend_handle, orig_handle): def first_color(colors): - colors = mcolors.colorConverter.to_rgba_array(colors) + colors = mcolors.to_rgba_array(colors) if len(colors): return colors[0] else: diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index a594ee8be774..84fcd0c38056 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -14,12 +14,11 @@ import numpy as np from numpy import ma from matplotlib import verbose -from . import artist +from . import artist, colors as mcolors from .artist import Artist from .cbook import (iterable, is_string_like, is_numlike, ls_mapper_r, pts_to_prestep, pts_to_poststep, pts_to_midstep) -from .colors import colorConverter from .path import Path from .transforms import Bbox, TransformedPath, IdentityTransform @@ -1260,24 +1259,16 @@ def update_from(self, other): other._marker.get_fillstyle()) self._drawstyle = other._drawstyle - def _get_rgb_face(self, alt=False): - facecolor = self._get_markerfacecolor(alt=alt) - if is_string_like(facecolor) and facecolor.lower() == 'none': - rgbFace = None - else: - rgbFace = colorConverter.to_rgb(facecolor) - return rgbFace - def _get_rgba_face(self, alt=False): facecolor = self._get_markerfacecolor(alt=alt) if is_string_like(facecolor) and facecolor.lower() == 'none': rgbaFace = None else: - rgbaFace = colorConverter.to_rgba(facecolor, self._alpha) + rgbaFace = mcolors.to_rgba(facecolor, self._alpha) return rgbaFace def _get_rgba_ln_color(self, alt=False): - return colorConverter.to_rgba(self._color, self._alpha) + return mcolors.to_rgba(self._color, self._alpha) # some aliases.... def set_aa(self, val): diff --git a/lib/matplotlib/mathtext.py b/lib/matplotlib/mathtext.py index 5c3e57596699..2a9a75e5347e 100644 --- a/lib/matplotlib/mathtext.py +++ b/lib/matplotlib/mathtext.py @@ -3342,12 +3342,12 @@ def to_rgba(self, texstr, color='black', dpi=120, fontsize=14): """ x, depth = self.to_mask(texstr, dpi=dpi, fontsize=fontsize) - r, g, b = mcolors.colorConverter.to_rgb(color) + r, g, b, a = mcolors.to_rgba(color) RGBA = np.zeros((x.shape[0], x.shape[1], 4), dtype=np.uint8) - RGBA[:,:,0] = int(255*r) - RGBA[:,:,1] = int(255*g) - RGBA[:,:,2] = int(255*b) - RGBA[:,:,3] = x + RGBA[:, :, 0] = 255 * r + RGBA[:, :, 1] = 255 * g + RGBA[:, :, 2] = 255 * b + RGBA[:, :, 3] = x return RGBA, depth def to_png(self, filename, texstr, color='black', dpi=120, fontsize=14): diff --git a/lib/matplotlib/patches.py b/lib/matplotlib/patches.py index 7aebc7c5043a..fa22b681976a 100644 --- a/lib/matplotlib/patches.py +++ b/lib/matplotlib/patches.py @@ -278,7 +278,7 @@ def set_edgecolor(self, color): if color is None: color = mpl.rcParams['patch.edgecolor'] self._original_edgecolor = color - self._edgecolor = colors.colorConverter.to_rgba(color, self._alpha) + self._edgecolor = colors.to_rgba(color, self._alpha) self.stale = True def set_ec(self, color): @@ -295,7 +295,7 @@ def set_facecolor(self, color): color = mpl.rcParams['patch.facecolor'] # save: otherwise changing _fill may lose alpha information self._original_facecolor = color - self._facecolor = colors.colorConverter.to_rgba(color, self._alpha) + self._facecolor = colors.to_rgba(color, self._alpha) if not self._fill: self._facecolor = list(self._facecolor) self._facecolor[3] = 0 @@ -585,8 +585,7 @@ def _update(self): if self.props is not None: self.update(self.props) else: - r, g, b, a = colors.colorConverter.to_rgba( - self.patch.get_facecolor()) + r, g, b, a = colors.to_rgba(self.patch.get_facecolor()) rho = 0.3 r = rho * r g = rho * g diff --git a/lib/matplotlib/patheffects.py b/lib/matplotlib/patheffects.py index 5435bcc8bd00..a0c8933b9b86 100644 --- a/lib/matplotlib/patheffects.py +++ b/lib/matplotlib/patheffects.py @@ -10,9 +10,8 @@ from matplotlib.externals import six from matplotlib.backend_bases import RendererBase -import matplotlib.transforms as mtransforms -from matplotlib.colors import colorConverter -import matplotlib.patches as mpatches +from matplotlib import ( + colors as mcolors, patches as mpatches, transforms as mtransforms) class AbstractPathEffect(object): @@ -241,7 +240,7 @@ def __init__(self, offset=(2, -2), if shadow_rgbFace is None: self._shadow_rgbFace = shadow_rgbFace else: - self._shadow_rgbFace = colorConverter.to_rgba(shadow_rgbFace) + self._shadow_rgbFace = mcolors.to_rgba(shadow_rgbFace) if alpha is None: alpha = 0.3 @@ -322,7 +321,7 @@ def __init__(self, offset=(2,-2), if shadow_color is None: self._shadow_color = shadow_color else: - self._shadow_color = colorConverter.to_rgba(shadow_color) + self._shadow_color = mcolors.to_rgba(shadow_color) self._alpha = alpha self._rho = rho diff --git a/lib/matplotlib/tests/test_cbook.py b/lib/matplotlib/tests/test_cbook.py index 26d96e6dc97c..fe0377a72eba 100644 --- a/lib/matplotlib/tests/test_cbook.py +++ b/lib/matplotlib/tests/test_cbook.py @@ -86,7 +86,7 @@ def setUp(self): datetime(2008, 1, 5), datetime(2008, 1, 6)] self.arr_dt2 = np.array(self.arr_dt) self.arr_colors = ['r', 'g', 'b', 'c', 'm', 'y'] - self.arr_rgba = mcolors.colorConverter.to_rgba_array(self.arr_colors) + self.arr_rgba = mcolors.to_rgba_array(self.arr_colors) @raises(ValueError) def test_bad_first_arg(self): diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py index 193ce0094c15..f24f4bc64f77 100644 --- a/lib/matplotlib/tests/test_colors.py +++ b/lib/matplotlib/tests/test_colors.py @@ -260,7 +260,7 @@ def test_cmap_and_norm_from_levels_and_colors(): def test_cmap_and_norm_from_levels_and_colors2(): levels = [-1, 2, 2.5, 3] colors = ['red', (0, 1, 0), 'blue', (0.5, 0.5, 0.5), (0.0, 0.0, 0.0, 1.0)] - clr = mcolors.colorConverter.to_rgba_array(colors) + clr = mcolors.to_rgba_array(colors) bad = (0.1, 0.1, 0.1, 0.1) no_color = (0.0, 0.0, 0.0, 0.0) masked_value = 'masked_value' @@ -337,13 +337,9 @@ def test_autoscale_masked(): def test_colors_no_float(): # Gray must be a string to distinguish 3-4 grays from RGB or RGBA. - def gray_from_float_rgb(): - return mcolors.colorConverter.to_rgb(0.4) - def gray_from_float_rgba(): - return mcolors.colorConverter.to_rgba(0.4) + return mcolors.to_rgba(0.4) - assert_raises(ValueError, gray_from_float_rgb) assert_raises(ValueError, gray_from_float_rgba) @@ -559,12 +555,8 @@ def angled_plane(azimuth, elevation, angle, x, y): def test_xkcd(): - x11_blue = mcolors.rgb2hex( - mcolors.colorConverter.to_rgb('blue')) - assert x11_blue == '#0000ff' - XKCD_blue = mcolors.rgb2hex( - mcolors.colorConverter.to_rgb('xkcd:blue')) - assert XKCD_blue == '#0343df' + assert mcolors.to_hex("blue") == "#0000ff" + assert mcolors.to_hex("xkcd:blue") == "#0343df" def _sph2cart(theta, phi): @@ -615,17 +607,26 @@ def test_colormap_reversing(): def test_cn(): matplotlib.rcParams['axes.prop_cycle'] = cycler('color', ['blue', 'r']) - x11_blue = mcolors.rgb2hex(mcolors.colorConverter.to_rgb('C0')) - assert x11_blue == '#0000ff' - red = mcolors.rgb2hex(mcolors.colorConverter.to_rgb('C1')) - assert red == '#ff0000' + assert mcolors.to_hex("C0") == '#0000ff' + assert mcolors.to_hex("C1") == '#ff0000' matplotlib.rcParams['axes.prop_cycle'] = cycler('color', ['xkcd:blue', 'r']) - XKCD_blue = mcolors.rgb2hex(mcolors.colorConverter.to_rgb('C0')) - assert XKCD_blue == '#0343df' - red = mcolors.rgb2hex(mcolors.colorConverter.to_rgb('C1')) - assert red == '#ff0000' + assert mcolors.to_hex("C0") == '#0343df' + assert mcolors.to_hex("C1") == '#ff0000' + + +def test_conversions(): + # to_rgba_array("none") returns a (0, 4) array. + assert_array_equal(mcolors.to_rgba_array("none"), np.zeros((0, 4))) + # alpha is properly set. + assert_equal(mcolors.to_rgba((1, 1, 1), .5), (1, 1, 1, .5)) + # builtin round differs between py2 and py3. + assert_equal(mcolors.to_hex((.7, .7, .7)), "#b2b2b2") + # hex roundtrip. + hex_color = "#1234abcd" + assert_equal(mcolors.to_hex(mcolors.to_rgba(hex_color), keep_alpha=True), + hex_color) if __name__ == '__main__': diff --git a/lib/matplotlib/tests/test_rcparams.py b/lib/matplotlib/tests/test_rcparams.py index d37adfc2b00d..6b759aa9a509 100644 --- a/lib/matplotlib/tests/test_rcparams.py +++ b/lib/matplotlib/tests/test_rcparams.py @@ -202,15 +202,11 @@ def test_legend_facecolor(): get_func = 'get_facecolor' rcparam = 'legend.facecolor' test_values = [({rcparam: 'r'}, - mcolors.colorConverter.to_rgba('r')), - ({rcparam: 'inherit', - 'axes.facecolor': 'r' - }, - mcolors.colorConverter.to_rgba('r')), - ({rcparam: 'g', - 'axes.facecolor': 'r'}, - mcolors.colorConverter.to_rgba('g')) - ] + mcolors.to_rgba('r')), + ({rcparam: 'inherit', 'axes.facecolor': 'r'}, + mcolors.to_rgba('r')), + ({rcparam: 'g', 'axes.facecolor': 'r'}, + mcolors.to_rgba('g'))] for rc_dict, target in test_values: yield _legend_rcparam_helper, rc_dict, target, get_func @@ -219,15 +215,11 @@ def test_legend_edgecolor(): get_func = 'get_edgecolor' rcparam = 'legend.edgecolor' test_values = [({rcparam: 'r'}, - mcolors.colorConverter.to_rgba('r')), - ({rcparam: 'inherit', - 'axes.edgecolor': 'r' - }, - mcolors.colorConverter.to_rgba('r')), - ({rcparam: 'g', - 'axes.facecolor': 'r'}, - mcolors.colorConverter.to_rgba('g')) - ] + mcolors.to_rgba('r')), + ({rcparam: 'inherit', 'axes.edgecolor': 'r'}, + mcolors.to_rgba('r')), + ({rcparam: 'g', 'axes.facecolor': 'r'}, + mcolors.to_rgba('g'))] for rc_dict, target in test_values: yield _legend_rcparam_helper, rc_dict, target, get_func diff --git a/lib/mpl_toolkits/mplot3d/art3d.py b/lib/mpl_toolkits/mplot3d/art3d.py index 027d6a07eb76..b8a4619959ef 100755 --- a/lib/mpl_toolkits/mplot3d/art3d.py +++ b/lib/mpl_toolkits/mplot3d/art3d.py @@ -389,12 +389,12 @@ def do_3d_projection(self, renderer): fcs = (zalpha(self._facecolor3d, vzs) if self._depthshade else self._facecolor3d) - fcs = mcolors.colorConverter.to_rgba_array(fcs, self._alpha) + fcs = mcolors.to_rgba_array(fcs, self._alpha) self.set_facecolors(fcs) ecs = (zalpha(self._edgecolor3d, vzs) if self._depthshade else self._edgecolor3d) - ecs = mcolors.colorConverter.to_rgba_array(ecs, self._alpha) + ecs = mcolors.to_rgba_array(ecs, self._alpha) self.set_edgecolors(ecs) PatchCollection.set_offsets(self, list(zip(vxs, vys))) @@ -457,12 +457,12 @@ def do_3d_projection(self, renderer): fcs = (zalpha(self._facecolor3d, vzs) if self._depthshade else self._facecolor3d) - fcs = mcolors.colorConverter.to_rgba_array(fcs, self._alpha) + fcs = mcolors.to_rgba_array(fcs, self._alpha) self.set_facecolors(fcs) ecs = (zalpha(self._edgecolor3d, vzs) if self._depthshade else self._edgecolor3d) - ecs = mcolors.colorConverter.to_rgba_array(ecs, self._alpha) + ecs = mcolors.to_rgba_array(ecs, self._alpha) self.set_edgecolors(ecs) PathCollection.set_offsets(self, list(zip(vxs, vys))) @@ -684,12 +684,12 @@ def set_alpha(self, alpha): raise TypeError('alpha must be a float or None') artist.Artist.set_alpha(self, alpha) try: - self._facecolors = mcolors.colorConverter.to_rgba_array( + self._facecolors = mcolors.to_rgba_array( self._facecolors3d, self._alpha) except (AttributeError, TypeError, IndexError): pass try: - self._edgecolors = mcolors.colorConverter.to_rgba_array( + self._edgecolors = mcolors.to_rgba_array( self._edgecolors3d, self._alpha) except (AttributeError, TypeError, IndexError): pass @@ -764,7 +764,7 @@ def get_colors(c, num): """Stretch the color argument to provide the required number num""" if type(c) == type("string"): - c = mcolors.colorConverter.to_rgba(c) + c = mcolors.to_rgba(c) if iscolor(c): return [c] * num diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py index 283c17c6ea4d..b416446c2e07 100755 --- a/lib/mpl_toolkits/mplot3d/axes3d.py +++ b/lib/mpl_toolkits/mplot3d/axes3d.py @@ -29,7 +29,8 @@ import matplotlib.scale as mscale from matplotlib.tri.triangulation import Triangulation import numpy as np -from matplotlib.colors import Normalize, colorConverter, LightSource +from matplotlib import colors as mcolors +from matplotlib.colors import Normalize, LightSource from . import art3d from . import proj3d @@ -1596,7 +1597,7 @@ def plot_surface(self, X, Y, Z, *args, **kwargs): color = kwargs.pop('color', None) if color is None: color = self._get_lines.get_next_color() - color = np.array(colorConverter.to_rgba(color)) + color = np.array(mcolors.to_rgba(color)) fcolors = None cmap = kwargs.get('cmap', None) @@ -1714,7 +1715,7 @@ def _shade_colors(self, color, normals): if len(shade[mask]) > 0: norm = Normalize(min(shade[mask]), max(shade[mask])) shade[~mask] = min(shade[mask]) - color = colorConverter.to_rgba_array(color) + color = mcolors.to_rgba_array(color) # shape of color should be (M, 4) (where M is number of faces) # shape of shade should be (M,) # colors should have final shape of (M, 4) @@ -1868,7 +1869,7 @@ def plot_trisurf(self, *args, **kwargs): color = kwargs.pop('color', None) if color is None: color = self._get_lines.get_next_color() - color = np.array(colorConverter.to_rgba(color)) + color = np.array(mcolors.to_rgba(color)) cmap = kwargs.get('cmap', None) norm = kwargs.pop('norm', None) @@ -2450,7 +2451,7 @@ def bar3d(self, x, y, z, dx, dy, dz, color=None, facecolors.extend([c] * 6) else: # a single color specified, or face colors specified explicitly - facecolors = list(colorConverter.to_rgba_array(color)) + facecolors = list(mcolors.to_rgba_array(color)) if len(facecolors) < len(x): facecolors *= (6 * len(x)) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy