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

Skip to content

Support individual styling of major and minor grid through rcParams #29481

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion ci/mypy-stubtest-allowlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ matplotlib\.tests(\..*)?
matplotlib\.pylab(\..*)?
matplotlib\._.*
matplotlib\.rcsetup\._listify_validator
matplotlib\.rcsetup\._validate_linestyle
matplotlib\.ft2font\.Glyph
matplotlib\.testing\.jpl_units\..*
matplotlib\.sphinxext(\..*)?
Expand Down
33 changes: 33 additions & 0 deletions doc/users/next_whats_new/new_rcparams_grid_options.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Separate styling options for major/minor grid line in rcParams
--------------------------------------------------------------

Using :rc:`grid.major.*` or :rc:`grid.minor.*` will overwrite the value in
:rc:`grid.*` for the major and minor gridlines, respectively.

.. plot::
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example is a bit heavy. This should be illustrative easy to grap. It should give an idea what you can do and how. It's not an exhaustive documentation of the feature. The rendered entry should fit on a (large) screen. Please boil down to one figure (you can have two Axes side by side if really needed) and take a subset of rcParams only.

Typically, one figure should be enough. Select a few parameters.

:include-source: true
:alt: Modifying the gridlines using the new options `rcParams`

import matplotlib as mpl
import matplotlib.pyplot as plt


# Set visibility for major and minor gridlines
mpl.rcParams["axes.grid"] = True
mpl.rcParams["ytick.minor.visible"] = True
mpl.rcParams["xtick.minor.visible"] = True
mpl.rcParams["axes.grid.which"] = "both"

# Using old old values to set both major and minor properties
mpl.rcParams["grid.color"] = "red"
mpl.rcParams["grid.linewidth"] = 1

# Overwrite some values for major and minor separately
mpl.rcParams["grid.major.color"] = "black"
mpl.rcParams["grid.major.linewidth"] = 2
mpl.rcParams["grid.minor.linestyle"] = ":"
mpl.rcParams["grid.minor.alpha"] = 0.6

plt.plot([0, 1], [0, 1])

plt.show()
13 changes: 10 additions & 3 deletions lib/matplotlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1348,11 +1348,18 @@ def is_interactive():
return rcParams['interactive']


def _val_or_rc(val, rc_name):
def _val_or_rc(val, *rc_names):
"""
If *val* is None, return ``mpl.rcParams[rc_name]``, otherwise return val.
If *val* is None, the first not-None value in ``mpl.rcParams[rc_names[i]]``.
If all are None returns ``mpl.rcParams[rc_names[-1]]``.
"""
return val if val is not None else rcParams[rc_name]
if val is not None:
return val

for rc_name in rc_names[:-1]:
if rcParams[rc_name] is not None:
return rcParams[rc_name]
return rcParams[rc_names[-1]]


def _init_tests():
Expand Down
33 changes: 29 additions & 4 deletions lib/matplotlib/axis.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,16 +124,41 @@ def __init__(
zorder = mlines.Line2D.zorder
self._zorder = zorder

grid_color = mpl._val_or_rc(grid_color, "grid.color")
grid_linestyle = mpl._val_or_rc(grid_linestyle, "grid.linestyle")
grid_linewidth = mpl._val_or_rc(grid_linewidth, "grid.linewidth")
grid_color = (
mpl._val_or_rc(
grid_color,
f"grid.{major_minor}.color",
"grid.color",
)
)
grid_linestyle = (
mpl._val_or_rc(
grid_linestyle,
f"grid.{major_minor}.linestyle",
"grid.linestyle",
)
)
grid_linewidth = (
mpl._val_or_rc(
grid_linewidth,
f"grid.{major_minor}.linewidth",
"grid.linewidth",
)
)
if grid_alpha is None and not mcolors._has_alpha_channel(grid_color):
# alpha precedence: kwarg > color alpha > rcParams['grid.alpha']
# Note: only resolve to rcParams if the color does not have alpha
# otherwise `grid(color=(1, 1, 1, 0.5))` would work like
# grid(color=(1, 1, 1, 0.5), alpha=rcParams['grid.alpha'])
# so the that the rcParams default would override color alpha.
grid_alpha = mpl.rcParams["grid.alpha"]
grid_alpha = (
# grid_alpha is None so we can use the first key
mpl._val_or_rc(
mpl.rcParams[f"grid.{major_minor}.alpha"],
"grid.alpha",
)
)

grid_kw = {k[5:]: v for k, v in kwargs.items() if k != "rotation_mode"}

self.tick1line = mlines.Line2D(
Expand Down
10 changes: 10 additions & 0 deletions lib/matplotlib/mpl-data/matplotlibrc
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,16 @@
#grid.linewidth: 0.8 # in points
#grid.alpha: 1.0 # transparency, between 0.0 and 1.0

#grid.major.color: None # If None defaults to grid.color
#grid.major.linestyle: None # If None defaults to grid.linestyle
#grid.major.linewidth: None # If None defaults to grid.linewidth
#grid.major.alpha: None # If None defaults to grid.alpha

#grid.minor.color: None # If None defaults to grid.color
#grid.minor.linestyle: None # If None defaults to grid.linestyle
#grid.minor.linewidth: None # If None defaults to grid.linewidth
#grid.minor.alpha: None # If None defaults to grid.alpha


## ***************************************************************************
## * LEGEND *
Expand Down
23 changes: 23 additions & 0 deletions lib/matplotlib/rcsetup.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,12 @@ def validate_color(s):
raise ValueError(f'{s!r} does not look like a color arg')


def validate_color_or_None(s):
if s is None or cbook._str_equal(s, "None"):
return None
return validate_color(s)


validate_colorlist = _listify_validator(
validate_color, allow_stringlist=True, doc='return a list of colorspecs')

Expand Down Expand Up @@ -522,6 +528,13 @@ def _is_iterable_not_string_like(x):
raise ValueError(f"linestyle {ls!r} is not a valid on-off ink sequence.")


def _validate_linestyle_or_None(ls):
if ls is None or cbook._str_equal(ls, "None"):
return None

return _validate_linestyle(ls)


validate_fillstyle = ValidateInStrings(
'markers.fillstyle', ['full', 'left', 'right', 'bottom', 'top', 'none'])

Expand Down Expand Up @@ -1248,6 +1261,16 @@ def _convert_validator_spec(key, conv):
"grid.linewidth": validate_float, # in points
"grid.alpha": validate_float,

"grid.major.color": validate_color_or_None, # grid color
"grid.major.linestyle": _validate_linestyle_or_None, # solid
"grid.major.linewidth": validate_float_or_None, # in points
"grid.major.alpha": validate_float_or_None,

"grid.minor.color": validate_color_or_None, # grid color
"grid.minor.linestyle": _validate_linestyle_or_None, # solid
"grid.minor.linewidth": validate_float_or_None, # in points
"grid.minor.alpha": validate_float_or_None,

## figure props
# figure title
"figure.titlesize": validate_fontsize,
Expand Down
4 changes: 3 additions & 1 deletion lib/matplotlib/rcsetup.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def validate_color_or_auto(s: Any) -> ColorType | Literal["auto"]: ...
def _validate_color_or_edge(s: Any) -> ColorType | Literal["edge"]: ...
def validate_color_for_prop_cycle(s: Any) -> ColorType: ...
def validate_color(s: Any) -> ColorType: ...
def validate_color_or_None(s: Any) -> ColorType | None: ...
def validate_colorlist(s: Any) -> list[ColorType]: ...
def _validate_color_or_linecolor(
s: Any,
Expand Down Expand Up @@ -139,7 +140,8 @@ def validate_fillstylelist(
s: Any,
) -> list[Literal["full", "left", "right", "bottom", "top", "none"]]: ...
def validate_markevery(s: Any) -> MarkEveryType: ...
def _validate_linestyle(s: Any) -> LineStyleType: ...
def _validate_linestyle(ls: Any) -> LineStyleType: ...
def _validate_linestyle_or_None(ls: Any) -> LineStyleType | None: ...
def validate_markeverylist(s: Any) -> list[MarkEveryType]: ...
def validate_bbox(s: Any) -> Literal["tight", "standard"] | None: ...
def validate_sketch(s: Any) -> None | tuple[float, float, float]: ...
Expand Down
28 changes: 28 additions & 0 deletions lib/matplotlib/tests/test_axis.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,31 @@ def test_get_tick_position_tick_params():
right=True, labelright=True, left=False, labelleft=False)
assert ax.xaxis.get_ticks_position() == "top"
assert ax.yaxis.get_ticks_position() == "right"


def test_grid_rcparams():
"""Tests that `grid.major/minor.*` overwrites `grid.*` in rcParams."""
plt.rcParams.update({
"axes.grid": True, "axes.grid.which": "both",
"ytick.minor.visible": True, "xtick.minor.visible": True,
})
def_linewidth = plt.rcParams["grid.linewidth"]
def_linestyle = plt.rcParams["grid.linestyle"]
def_alpha = plt.rcParams["grid.alpha"]

plt.rcParams.update({
"grid.color": "gray", "grid.minor.color": "red",
"grid.major.linestyle": ":", "grid.major.linewidth": 2,
"grid.minor.alpha": 0.6,
})
_, ax = plt.subplots()
ax.plot([0, 1])

assert ax.xaxis.get_major_ticks()[0].gridline.get_color() == "gray"
assert ax.xaxis.get_minor_ticks()[0].gridline.get_color() == "red"
assert ax.xaxis.get_major_ticks()[0].gridline.get_linewidth() == 2
assert ax.xaxis.get_minor_ticks()[0].gridline.get_linewidth() == def_linewidth
assert ax.xaxis.get_major_ticks()[0].gridline.get_linestyle() == ":"
assert ax.xaxis.get_minor_ticks()[0].gridline.get_linestyle() == def_linestyle
assert ax.xaxis.get_major_ticks()[0].gridline.get_alpha() == def_alpha
assert ax.xaxis.get_minor_ticks()[0].gridline.get_alpha() == 0.6