Skip to content

Commit 6e51bec

Browse files
konmeneltimhoffm
andauthored
Support individual styling of major and minor grid through rcParams (matplotlib#29481)
* Possible fix for issue matplotlib#13919 * fix flake warnings * Resolve suggested changes * Add new validators in stub file * make stub file arguments same as runtime * Add distinction for x and y axis in grid line options * fix flake8 * Add What's New note * Extend `_val_or_rc` to support multiply names `_val_or_rc` now accept multiple rc names and return val or the first non-None value in rcParams. Returns last rc name if all other are None. Also, simplified code in `Tick` for grid lines creatation * Fix linting for and _validate_linestyle_or_None to mypy allowlist * Remove validate linestyle functions from stubtest allow list * Revert change to just grid.major/minor distinction in rcParams * Update What's New note and reduced example * Add testing for `grid.major/minor.*` in rcParams * fix indentation and linting * Fix example description * Fix spelling * Fix type * Fix formatting Removed outer brackets * `validate_color_or_None` private and fix argument names * Fix validator name in stub file as well * correct validators for grid.*.color keys * Revert change in mypy-stubtest-allowlist.txt --------- Co-authored-by: Tim Hoffmann <[email protected]>
1 parent 9340190 commit 6e51bec

File tree

7 files changed

+127
-7
lines changed

7 files changed

+127
-7
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
Separate styling options for major/minor grid line in rcParams
2+
--------------------------------------------------------------
3+
4+
Using :rc:`grid.major.*` or :rc:`grid.minor.*` will overwrite the value in
5+
:rc:`grid.*` for the major and minor gridlines, respectively.
6+
7+
.. plot::
8+
:include-source: true
9+
:alt: Modifying the gridlines using the new options `rcParams`
10+
11+
import matplotlib as mpl
12+
import matplotlib.pyplot as plt
13+
14+
15+
# Set visibility for major and minor gridlines
16+
mpl.rcParams["axes.grid"] = True
17+
mpl.rcParams["ytick.minor.visible"] = True
18+
mpl.rcParams["xtick.minor.visible"] = True
19+
mpl.rcParams["axes.grid.which"] = "both"
20+
21+
# Using old values to set both major and minor properties
22+
mpl.rcParams["grid.color"] = "red"
23+
mpl.rcParams["grid.linewidth"] = 1
24+
25+
# Overwrite some values for major and minor separately
26+
mpl.rcParams["grid.major.color"] = "black"
27+
mpl.rcParams["grid.major.linewidth"] = 2
28+
mpl.rcParams["grid.minor.linestyle"] = ":"
29+
mpl.rcParams["grid.minor.alpha"] = 0.6
30+
31+
plt.plot([0, 1], [0, 1])
32+
33+
plt.show()

lib/matplotlib/__init__.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,11 +1309,18 @@ def is_interactive():
13091309
return rcParams['interactive']
13101310

13111311

1312-
def _val_or_rc(val, rc_name):
1312+
def _val_or_rc(val, *rc_names):
13131313
"""
1314-
If *val* is None, return ``mpl.rcParams[rc_name]``, otherwise return val.
1314+
If *val* is None, the first not-None value in ``mpl.rcParams[rc_names[i]]``.
1315+
If all are None returns ``mpl.rcParams[rc_names[-1]]``.
13151316
"""
1316-
return val if val is not None else rcParams[rc_name]
1317+
if val is not None:
1318+
return val
1319+
1320+
for rc_name in rc_names[:-1]:
1321+
if rcParams[rc_name] is not None:
1322+
return rcParams[rc_name]
1323+
return rcParams[rc_names[-1]]
13171324

13181325

13191326
def _init_tests():

lib/matplotlib/axis.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,16 +125,33 @@ def __init__(
125125
zorder = mlines.Line2D.zorder
126126
self._zorder = zorder
127127

128-
grid_color = mpl._val_or_rc(grid_color, "grid.color")
129-
grid_linestyle = mpl._val_or_rc(grid_linestyle, "grid.linestyle")
130-
grid_linewidth = mpl._val_or_rc(grid_linewidth, "grid.linewidth")
128+
grid_color = mpl._val_or_rc(
129+
grid_color,
130+
f"grid.{major_minor}.color",
131+
"grid.color",
132+
)
133+
grid_linestyle = mpl._val_or_rc(
134+
grid_linestyle,
135+
f"grid.{major_minor}.linestyle",
136+
"grid.linestyle",
137+
)
138+
grid_linewidth = mpl._val_or_rc(
139+
grid_linewidth,
140+
f"grid.{major_minor}.linewidth",
141+
"grid.linewidth",
142+
)
131143
if grid_alpha is None and not mcolors._has_alpha_channel(grid_color):
132144
# alpha precedence: kwarg > color alpha > rcParams['grid.alpha']
133145
# Note: only resolve to rcParams if the color does not have alpha
134146
# otherwise `grid(color=(1, 1, 1, 0.5))` would work like
135147
# grid(color=(1, 1, 1, 0.5), alpha=rcParams['grid.alpha'])
136148
# so the that the rcParams default would override color alpha.
137-
grid_alpha = mpl.rcParams["grid.alpha"]
149+
grid_alpha = mpl._val_or_rc(
150+
# grid_alpha is None so we can use the first key
151+
mpl.rcParams[f"grid.{major_minor}.alpha"],
152+
"grid.alpha",
153+
)
154+
138155
grid_kw = {k[5:]: v for k, v in kwargs.items() if k != "rotation_mode"}
139156

140157
self.tick1line = mlines.Line2D(

lib/matplotlib/mpl-data/matplotlibrc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,16 @@
543543
#grid.linewidth: 0.8 # in points
544544
#grid.alpha: 1.0 # transparency, between 0.0 and 1.0
545545

546+
#grid.major.color: None # If None defaults to grid.color
547+
#grid.major.linestyle: None # If None defaults to grid.linestyle
548+
#grid.major.linewidth: None # If None defaults to grid.linewidth
549+
#grid.major.alpha: None # If None defaults to grid.alpha
550+
551+
#grid.minor.color: None # If None defaults to grid.color
552+
#grid.minor.linestyle: None # If None defaults to grid.linestyle
553+
#grid.minor.linewidth: None # If None defaults to grid.linewidth
554+
#grid.minor.alpha: None # If None defaults to grid.alpha
555+
546556

547557
## ***************************************************************************
548558
## * LEGEND *

lib/matplotlib/rcsetup.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,12 @@ def validate_color(s):
361361
raise ValueError(f'{s!r} does not look like a color arg')
362362

363363

364+
def _validate_color_or_None(s):
365+
if s is None or cbook._str_equal(s, "None"):
366+
return None
367+
return validate_color(s)
368+
369+
364370
validate_colorlist = _listify_validator(
365371
validate_color, allow_stringlist=True, doc='return a list of colorspecs')
366372

@@ -515,6 +521,13 @@ def _is_iterable_not_string_like(x):
515521
raise ValueError(f"linestyle {ls!r} is not a valid on-off ink sequence.")
516522

517523

524+
def _validate_linestyle_or_None(s):
525+
if s is None or cbook._str_equal(s, "None"):
526+
return None
527+
528+
return _validate_linestyle(s)
529+
530+
518531
validate_fillstyle = ValidateInStrings(
519532
'markers.fillstyle', ['full', 'left', 'right', 'bottom', 'top', 'none'])
520533

@@ -1242,6 +1255,16 @@ def _convert_validator_spec(key, conv):
12421255
"grid.linewidth": validate_float, # in points
12431256
"grid.alpha": validate_float,
12441257

1258+
"grid.major.color": _validate_color_or_None, # grid color
1259+
"grid.major.linestyle": _validate_linestyle_or_None, # solid
1260+
"grid.major.linewidth": validate_float_or_None, # in points
1261+
"grid.major.alpha": validate_float_or_None,
1262+
1263+
"grid.minor.color": _validate_color_or_None, # grid color
1264+
"grid.minor.linestyle": _validate_linestyle_or_None, # solid
1265+
"grid.minor.linewidth": validate_float_or_None, # in points
1266+
"grid.minor.alpha": validate_float_or_None,
1267+
12451268
## figure props
12461269
# figure title
12471270
"figure.titlesize": validate_fontsize,

lib/matplotlib/rcsetup.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ def validate_color_or_auto(s: Any) -> ColorType | Literal["auto"]: ...
4848
def _validate_color_or_edge(s: Any) -> ColorType | Literal["edge"]: ...
4949
def validate_color_for_prop_cycle(s: Any) -> ColorType: ...
5050
def validate_color(s: Any) -> ColorType: ...
51+
def _validate_color_or_None(s: Any) -> ColorType | None: ...
5152
def validate_colorlist(s: Any) -> list[ColorType]: ...
5253
def _validate_color_or_linecolor(
5354
s: Any,
@@ -137,6 +138,7 @@ def validate_fillstylelist(
137138
) -> list[Literal["full", "left", "right", "bottom", "top", "none"]]: ...
138139
def validate_markevery(s: Any) -> MarkEveryType: ...
139140
def _validate_linestyle(s: Any) -> LineStyleType: ...
141+
def _validate_linestyle_or_None(s: Any) -> LineStyleType | None: ...
140142
def validate_markeverylist(s: Any) -> list[MarkEveryType]: ...
141143
def validate_bbox(s: Any) -> Literal["tight", "standard"] | None: ...
142144
def validate_sketch(s: Any) -> None | tuple[float, float, float]: ...

lib/matplotlib/tests/test_axis.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,31 @@ def test_get_tick_position_tick_params():
6767
right=True, labelright=True, left=False, labelleft=False)
6868
assert ax.xaxis.get_ticks_position() == "top"
6969
assert ax.yaxis.get_ticks_position() == "right"
70+
71+
72+
def test_grid_rcparams():
73+
"""Tests that `grid.major/minor.*` overwrites `grid.*` in rcParams."""
74+
plt.rcParams.update({
75+
"axes.grid": True, "axes.grid.which": "both",
76+
"ytick.minor.visible": True, "xtick.minor.visible": True,
77+
})
78+
def_linewidth = plt.rcParams["grid.linewidth"]
79+
def_linestyle = plt.rcParams["grid.linestyle"]
80+
def_alpha = plt.rcParams["grid.alpha"]
81+
82+
plt.rcParams.update({
83+
"grid.color": "gray", "grid.minor.color": "red",
84+
"grid.major.linestyle": ":", "grid.major.linewidth": 2,
85+
"grid.minor.alpha": 0.6,
86+
})
87+
_, ax = plt.subplots()
88+
ax.plot([0, 1])
89+
90+
assert ax.xaxis.get_major_ticks()[0].gridline.get_color() == "gray"
91+
assert ax.xaxis.get_minor_ticks()[0].gridline.get_color() == "red"
92+
assert ax.xaxis.get_major_ticks()[0].gridline.get_linewidth() == 2
93+
assert ax.xaxis.get_minor_ticks()[0].gridline.get_linewidth() == def_linewidth
94+
assert ax.xaxis.get_major_ticks()[0].gridline.get_linestyle() == ":"
95+
assert ax.xaxis.get_minor_ticks()[0].gridline.get_linestyle() == def_linestyle
96+
assert ax.xaxis.get_major_ticks()[0].gridline.get_alpha() == def_alpha
97+
assert ax.xaxis.get_minor_ticks()[0].gridline.get_alpha() == 0.6

0 commit comments

Comments
 (0)