Skip to content

[Bug]: rcParams["legend.labelcolor"] treats "none" and "None" differently #28153

Open

Description

@anntzer

Bug summary

The "standard" matplotlib color semantics is that the strings "none" and "None" both mean fully transparent (as can be checked by mpl.colors.to_rgba); the object None can sometimes be used to mark a fallback. Because the matplotlibrc format cannot distinguish between None and "None" (due the use of unquoted strings), it normally uses "auto" or "inherit" to mark a fallback.
However, rcParams["legend.labelcolor"] (a relatively recent introduction -- matplotlib 3.5) tries to use "None" as default, and as a result ends up having inconsistent behavior between "None" and "none".

Code for reproduction

from pylab import *

rcdefaults()
print(repr(rcParams["legend.labelcolor"]))
# default is the string "None"

rcParams["legend.labelcolor"] = "None"; plot([0, 1], label="foo"); legend()
# labels are black

rcParams["legend.labelcolor"] = "none"; plot([0, 1], label="foo"); legend()
# labels are transparent

rcParams["legend.labelcolor"] = None; plot([0, 1], label="foo"); legend()
# labels are black

Actual outcome

See above.

Expected outcome

"None" and "none" should mean the same (likely both should mean "transparent", not so much for usefulness but rather for consistency.

Additional information

I suspect trying to support the object None as well here basically cannot be made to work (as long as matplotlibrc uses unquoted strings); the default should switch to be "auto" (or "inherit").

As a further point, the implementation of the rcParams validator _validate_color_or_linecolor seems wrong:

    elif isinstance(s, str) and len(s) == 6 or len(s) == 8:  # (1)
        stmp = '#' + s
        if is_color_like(stmp):
            return stmp
        if s.lower() == 'none':  # (2)
            return None

Likely parentheses are missing at (1); also (2) can never hold due to the len() check.
As a side point, 3 and 4-hex color codes ("abc(d)", meaning "#abc(d)", meaning "#aabbcc(dd)") are also not supported by rcParams (this is also the case for the more general _validate_color), even though the normal color machinery does support them. At least this should be documented; I don't know if this can actually be supported without introducing an ambiguity with named colors.

Operating system

macos

Matplotlib Version

3.9.0.dev1542+geb62d6951a

Matplotlib Backend

any

Python version

3.12

Jupyter version

no

Installation

git checkout

Activity

  1. OdileVidrine commented on May 15, 2024

    @OdileVidrine
    Contributor

    I would assume that users would prefer the default be black rather than transparent. Is there any reason to keep the transparent as default behavior, or can all variations of "none" be removed and replaced with "auto"? To me it makes sense to have the axis label visible by default, however, I do not know what happens if the user does not provide an axis label- would changing the default to black make axis labels required?

  2. OdileVidrine commented on May 15, 2024

    @OdileVidrine
    Contributor

    I am looking at rcsetup.py, and I see xtick.labelcolor and ytick.labelcolor (I assume these are similar to axes.labelcolor) use a different function for validation code. I am going to look into changing axes.labelcolor to follow the pattern established by xtick.labelcolor and ytick.labelcolor and see if that fixes this.

  3. OdileVidrine commented on May 15, 2024

    @OdileVidrine
    Contributor

    I have found something strange:

    ## ***************************************************************************
    ## * AXES *
    ## ***************************************************************************
    ## Following are default face and edge colors, default tick sizes,
    ## default font sizes for tick labels, and so on. See
    ## https://matplotlib.org/stable/api/axes_api.html#module-matplotlib.axes
    #axes.facecolor: white # axes background color
    #axes.edgecolor: black # axes edge color
    #axes.linewidth: 0.8 # edge line width
    #axes.grid: False # display grid or not
    #axes.grid.axis: both # which axis the grid should apply to
    #axes.grid.which: major # grid lines at {major, minor, both} ticks
    #axes.titlelocation: center # alignment of the title: {left, right, center}
    #axes.titlesize: large # font size of the axes title
    #axes.titleweight: normal # font weight of title
    #axes.titlecolor: auto # color of the axes title, auto falls back to
    # text.color as default value
    #axes.titley: None # position title (axes relative units). None implies auto
    #axes.titlepad: 6.0 # pad between axes and title in points
    #axes.labelsize: medium # font size of the x and y labels
    #axes.labelpad: 4.0 # space between label and axis
    #axes.labelweight: normal # weight of the x and y labels
    #axes.labelcolor: black

    axes.titlecolor uses auto as its default color, but axes.labelcolor uses black. Is there any reason to not use auto?

  4. OdileVidrine commented on May 15, 2024

    @OdileVidrine
    Contributor

    I just noticed the title says axes.labelcolor but the body says legend.labelcolor, can you edit either the title or the body to refer to the right thing?

  5. changed the title [-][Bug]: rcParams["axes.labelcolor"] treats "none" and "None" differently[/-] [+][Bug]: rcParams["legend.labelcolor"] treats "none" and "None" differently[/+] on Jun 11, 2024
  6. anntzer commented on Jun 11, 2024

    @anntzer
    ContributorAuthor

    Oops, fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions