Skip to content

Handle Tick gridline properties like other Tick properties #10193

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

Merged
merged 3 commits into from
Jan 9, 2018
Merged
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
8 changes: 8 additions & 0 deletions doc/users/next_whats_new/2018_01_07_tick_params_gridlines.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
`Axes.tick_params` can set gridline properties
----------------------------------------------

`Tick` objects hold gridlines as well as the tick mark and its label.
`Axis.set_tick_params`, `Axes.tick_params` and `pyplot.tick_params`
now have keyword arguments 'grid_color', 'grid_alpha', 'grid_linewidth',
and 'grid_linestyle' for overriding the defaults in `rcParams`:
'grid.color', etc.
19 changes: 16 additions & 3 deletions lib/matplotlib/axes/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2790,7 +2790,7 @@ def locator_params(self, axis='both', tight=None, **kwargs):
self.autoscale_view(tight=tight, scalex=_x, scaley=_y)

def tick_params(self, axis='both', **kwargs):
"""Change the appearance of ticks and tick labels.
"""Change the appearance of ticks, tick labels, and gridlines.

Parameters
----------
Expand Down Expand Up @@ -2848,16 +2848,29 @@ def tick_params(self, axis='both', **kwargs):
labelrotation : float
Tick label rotation

grid_color : color
Changes the gridline color to the given mpl color spec.

grid_alpha : float
Transparency of gridlines: 0 (transparent) to 1 (opaque).

grid_linewidth : float
Width of gridlines in points.

grid_linestyle : string
Any valid :class:`~matplotlib.lines.Line2D` line style spec.

Examples
--------

Usage ::

ax.tick_params(direction='out', length=6, width=2, colors='r')
ax.tick_params(direction='out', length=6, width=2, colors='r',
grid_color='r', grid_alpha=0.5)

This will make all major ticks be red, pointing out of the box,
and with dimensions 6 points by 2 points. Tick labels will
also be red.
also be red. Gridlines will be red and translucent.

"""
if axis in ['x', 'both']:
Expand Down
123 changes: 67 additions & 56 deletions lib/matplotlib/axis.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@

GRIDLINE_INTERPOLATION_STEPS = 180

# This list is being used for compatibility with Axes.grid, which
# allows all Line2D kwargs.
_line_AI = artist.ArtistInspector(mlines.Line2D)
_line_param_names = _line_AI.get_setters()
_line_param_aliases = [list(d.keys())[0] for d in _line_AI.aliasd.values()]
_gridline_param_names = ['grid_' + name
for name in _line_param_names + _line_param_aliases]


class Tick(artist.Artist):
"""
Expand Down Expand Up @@ -86,6 +94,11 @@ def __init__(self, axes, loc, label,
label2On=False,
major=True,
labelrotation=0,
grid_color=None,
grid_linestyle=None,
grid_linewidth=None,
grid_alpha=None,
**kw # Other Line2D kwargs applied to gridlines.
):
"""
bbox is the Bound2D bounding box in display coords of the Axes
Expand Down Expand Up @@ -153,6 +166,17 @@ def __init__(self, axes, loc, label,
zorder = mlines.Line2D.zorder
self._zorder = zorder

self._grid_color = (rcParams['grid.color']
if grid_color is None else grid_color)
self._grid_linestyle = (rcParams['grid.linestyle']
if grid_linestyle is None else grid_linestyle)
self._grid_linewidth = (rcParams['grid.linewidth']
if grid_linewidth is None else grid_linewidth)
self._grid_alpha = (rcParams['grid.alpha']
if grid_alpha is None else grid_alpha)

self._grid_kw = {k[5:]: v for k, v in kw.items()}

self.apply_tickdir(tickdir)

self.tick1line = self._get_tick1line()
Expand Down Expand Up @@ -368,6 +392,14 @@ def _apply_params(self, **kw):
v = getattr(self.label1, 'get_' + k)()
setattr(self, '_label' + k, v)

grid_list = [k for k in six.iteritems(kw)
if k[0] in _gridline_param_names]
if grid_list:
grid_kw = {k[5:]: v for k, v in grid_list}
self.gridline.set(**grid_kw)
for k, v in six.iteritems(grid_kw):
setattr(self, '_grid_' + k, v)

def update_position(self, loc):
'Set the location of tick in data coords with scalar *loc*'
raise NotImplementedError('Derived must override')
Expand Down Expand Up @@ -469,11 +501,12 @@ def _get_gridline(self):
'Get the default line2D instance'
# x in data coords, y in axes coords
l = mlines.Line2D(xdata=(0.0, 0.0), ydata=(0, 1.0),
color=rcParams['grid.color'],
linestyle=rcParams['grid.linestyle'],
linewidth=rcParams['grid.linewidth'],
alpha=rcParams['grid.alpha'],
markersize=0)
color=self._grid_color,
linestyle=self._grid_linestyle,
linewidth=self._grid_linewidth,
alpha=self._grid_alpha,
markersize=0,
**self._grid_kw)
l.set_transform(self.axes.get_xaxis_transform(which='grid'))
l.get_path()._interpolation_steps = GRIDLINE_INTERPOLATION_STEPS
self._set_artist_props(l)
Expand Down Expand Up @@ -592,12 +625,12 @@ def _get_gridline(self):
'Get the default line2D instance'
# x in axes coords, y in data coords
l = mlines.Line2D(xdata=(0, 1), ydata=(0, 0),
color=rcParams['grid.color'],
linestyle=rcParams['grid.linestyle'],
linewidth=rcParams['grid.linewidth'],
alpha=rcParams['grid.alpha'],
markersize=0)

color=self._grid_color,
linestyle=self._grid_linestyle,
linewidth=self._grid_linewidth,
alpha=self._grid_alpha,
markersize=0,
**self._grid_kw)
l.set_transform(self.axes.get_yaxis_transform(which='grid'))
l.get_path()._interpolation_steps = GRIDLINE_INTERPOLATION_STEPS
self._set_artist_props(l)
Expand Down Expand Up @@ -650,13 +683,6 @@ def __init__(self, axes, pickradius=15):
artist.Artist.__init__(self)
self.set_figure(axes.figure)

# Keep track of setting to the default value, this allows use to know
# if any of the following values is explicitly set by the user, so as
# to not overwrite their settings with any of our 'auto' settings.
self.isDefault_majloc = True
self.isDefault_minloc = True
self.isDefault_majfmt = True
self.isDefault_minfmt = True
self.isDefault_label = True

self.axes = axes
Expand Down Expand Up @@ -745,22 +771,10 @@ def get_children(self):

def cla(self):
'clear the current axis'
self.set_major_locator(mticker.AutoLocator())
self.set_major_formatter(mticker.ScalarFormatter())
self.set_minor_locator(mticker.NullLocator())
self.set_minor_formatter(mticker.NullFormatter())

self.set_label_text('')
self._set_artist_props(self.label)
self.label.set_text('') # self.set_label_text would change isDefault_

# Keep track of setting to the default value, this allows use to know
# if any of the following values is explicitly set by the user, so as
# to not overwrite their settings with any of our 'auto' settings.
self.isDefault_majloc = True
self.isDefault_minloc = True
self.isDefault_majfmt = True
self.isDefault_minfmt = True
self.isDefault_label = True
self._set_scale('linear')

# Clear the callback registry for this axis, or it may "leak"
self.callbacks = cbook.CallbackRegistry()
Expand All @@ -771,9 +785,6 @@ def cla(self):
self._gridOnMinor = (rcParams['axes.grid'] and
rcParams['axes.grid.which'] in ('both', 'minor'))

self.label.set_text('')
self._set_artist_props(self.label)

self.reset_ticks()

self.converter = None
Expand All @@ -782,9 +793,11 @@ def cla(self):
self.stale = True

def reset_ticks(self):
# build a few default ticks; grow as necessary later; only
# define 1 so properties set on ticks will be copied as they
# grow
"""
Re-initialize the major and minor Tick lists.

Each list starts with a single fresh Tick.
"""
del self.majorTicks[:]
del self.minorTicks[:]

Expand All @@ -793,9 +806,14 @@ def reset_ticks(self):
self._lastNumMajorTicks = 1
self._lastNumMinorTicks = 1

try:
self.set_clip_path(self.axes.patch)
Copy link
Member

Choose a reason for hiding this comment

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

It's been such a pain having to remember to do this for polar plots, so I'm happy to see it centralized.

Copy link
Member

Choose a reason for hiding this comment

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

OK, sorry to dredge this up:

This change is a bit strange because from a literal point of view most of the axis is drawn outside this clip_path. I was trying to short-circuit getting tightbboxes if the clip path was the axes, but with this change axis-es are marked as clipped to the axes. Which they aren't - only their gridlines are.

Not sure what the solution is, but wanted to point out that this is pretty confusing.

Copy link
Member

Choose a reason for hiding this comment

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

This isn't exactly new though; it simply makes reset_ticks consistent with what happens in __init__ (admittedly, of the Axes, not the Tick).

except AttributeError:
pass

def set_tick_params(self, which='major', reset=False, **kw):
"""
Set appearance parameters for ticks and ticklabels.
Set appearance parameters for ticks, ticklabels, and gridlines.

For documentation of keyword arguments, see
:meth:`matplotlib.axes.Axes.tick_params`.
Expand All @@ -810,6 +828,7 @@ def set_tick_params(self, which='major', reset=False, **kw):
if reset:
d.clear()
d.update(kwtrans)

if reset:
self.reset_ticks()
else:
Expand All @@ -833,7 +852,8 @@ def _translate_tick_kw(kw, to_init_kw=True):
kwkeys1 = ['length', 'direction', 'left', 'bottom', 'right', 'top',
'labelleft', 'labelbottom', 'labelright', 'labeltop',
'labelrotation']
kwkeys = kwkeys0 + kwkeys1
kwkeys2 = _gridline_param_names
kwkeys = kwkeys0 + kwkeys1 + kwkeys2
kwtrans = dict()
if to_init_kw:
if 'length' in kw:
Expand Down Expand Up @@ -975,7 +995,7 @@ def _update_ticks(self, renderer):
"""

interval = self.get_view_interval()
tick_tups = list(self.iter_ticks())
tick_tups = list(self.iter_ticks()) # iter_ticks calls the locator
if self._smart_bounds and tick_tups:
# handle inverted limits
view_low, view_high = sorted(interval)
Expand Down Expand Up @@ -1401,30 +1421,21 @@ def grid(self, b=None, which='major', **kwargs):
if len(kwargs):
b = True
which = which.lower()
gridkw = {'grid_' + item[0]: item[1] for item in kwargs.items()}
if which in ['minor', 'both']:
if b is None:
self._gridOnMinor = not self._gridOnMinor
else:
self._gridOnMinor = b
for tick in self.minorTicks: # don't use get_ticks here!
if tick is None:
continue
tick.gridOn = self._gridOnMinor
if len(kwargs):
tick.gridline.update(kwargs)
self._minor_tick_kw['gridOn'] = self._gridOnMinor
self.set_tick_params(which='minor', gridOn=self._gridOnMinor,
**gridkw)
if which in ['major', 'both']:
if b is None:
self._gridOnMajor = not self._gridOnMajor
else:
self._gridOnMajor = b
for tick in self.majorTicks: # don't use get_ticks here!
if tick is None:
continue
tick.gridOn = self._gridOnMajor
if len(kwargs):
tick.gridline.update(kwargs)
self._major_tick_kw['gridOn'] = self._gridOnMajor
self.set_tick_params(which='major', gridOn=self._gridOnMajor,
**gridkw)
self.stale = True

def update_units(self, data):
Expand Down Expand Up @@ -1454,11 +1465,11 @@ def _update_axisinfo(self):
check the axis converter for the stored units to see if the
axis info needs to be updated
"""

if self.converter is None:
return

info = self.converter.axisinfo(self.units, self)

if info is None:
return
if info.majloc is not None and \
Expand Down
11 changes: 8 additions & 3 deletions lib/matplotlib/scale.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
import numpy as np
from numpy import ma

from matplotlib import cbook, docstring
from matplotlib import cbook, docstring, rcParams
from matplotlib.ticker import (
NullFormatter, ScalarFormatter, LogFormatterSciNotation, LogitFormatter,
NullLocator, LogLocator, AutoLocator, SymmetricalLogLocator, LogitLocator)
NullLocator, LogLocator, AutoLocator, AutoMinorLocator,
SymmetricalLogLocator, LogitLocator)
from matplotlib.transforms import Transform, IdentityTransform


Expand Down Expand Up @@ -71,8 +72,12 @@ def set_default_locators_and_formatters(self, axis):
"""
axis.set_major_locator(AutoLocator())
axis.set_major_formatter(ScalarFormatter())
axis.set_minor_locator(NullLocator())
axis.set_minor_formatter(NullFormatter())
# update the minor locator for x and y axis based on rcParams
if rcParams['xtick.minor.visible']:
axis.set_minor_locator(AutoMinorLocator())
else:
axis.set_minor_locator(NullLocator())

def get_transform(self):
"""
Expand Down
12 changes: 12 additions & 0 deletions lib/matplotlib/tests/test_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5152,6 +5152,18 @@ def test_axis_set_tick_params_labelsize_labelcolor():
assert axis_1.yaxis.majorTicks[0]._labelcolor == 'red'


def test_axes_tick_params_gridlines():
# Now treating grid params like other Tick params
ax = plt.subplot()
ax.tick_params(grid_color='b', grid_linewidth=5, grid_alpha=0.5,
grid_linestyle='dashdot')
for axis in ax.xaxis, ax.yaxis:
assert axis.majorTicks[0]._grid_color == 'b'
assert axis.majorTicks[0]._grid_linewidth == 5
assert axis.majorTicks[0]._grid_alpha == 0.5
assert axis.majorTicks[0]._grid_linestyle == 'dashdot'


def test_none_kwargs():
fig, ax = plt.subplots()
ln, = ax.plot(range(32), linestyle=None)
Expand Down