8000 Extract _TickCollection · timhoffm/matplotlib@8f69b0b · GitHub
[go: up one dir, main page]

Skip to content

Commit 8f69b0b

Browse files
committed
Extract _TickCollection
This is to establish a (preliminary) internal interface that does not need to access individual elements of the tick list.
1 parent c11175d commit 8f69b0b

File tree

3 files changed

+156
-69
lines changed

3 files changed

+156
-69
lines changed

lib/matplotlib/axis.py

Lines changed: 142 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,125 @@ def formatter(self, formatter):
502502
self._formatter = formatter
503503

504504

505+
class _TickCollection:
506+
"""
507+
A facade for tick lists based on _LazyTickList.
508+
509+
This provides an interface for manipulating the ticks collectively. It
510+
removes the need to address individual elements of the tick lists and thus
511+
opens up a path of replacing individual lines with collections while
512+
keeping the API stable::
513+
514+
- tick lists - tick collection
515+
- tick (Tick) - tick1lines (LineCollection)
516+
- tick1line (Line2D) - tick2lines (LineCollection)
517+
- tick2line (Line2D) ===> - tick1labels (TextCollection*)
518+
- tick1label (Text) - tick2labels (TextCollection*)
519+
- tick2label (Text) - gridline
520+
- gridline (Line2D)
521+
- tick (Tick)
522+
- ...
523+
- tick (Tick)
524+
- ...
525+
526+
(*) TextCollection does not yet exists. Probably worth implementing, but
527+
we can also use lists of Text for the time being.
528+
529+
"""
530+
def __init__(self, axis, ticklist_name):
531+
"""
532+
We cannot initialize this in Axis using
533+
``_TickCollection(self.majorTicks)``, because that would trigger the
534+
evaluation mechanism of the _LazyTickList. Therefore we delay the
535+
access to the latest possible point via the property
536+
``self._ticklist``.
537+
"""
538+
self._axis = axis
539+
self._ticklist_name = ticklist_name
540+
541+
def __len__(self):
542+
return len(self._ticklist)
543+
544+
@property
545+
def _ticklist(self):
546+
"""Delayed access to resolve _LazyTickList as late as possible."""
547+
return getattr(self._axis, self._ticklist_name)
548+
549+
def apply_params(self, **kwargs):
550+
"""Apply **kwargs to all ticks."""
551+
for tick in self._ticklist:
552+
tick._apply_params(**kwargs)
553+
554+
def set_clip_path(self, clippath, transform):
555+
"""
556+
Set the clip path for all ticks.
557+
558+
See `.Artist.set_clip_path`.
559+
"""
560+
for tick in self._ticklist:
561+
tick.set_clip_path(clippath, transform)
562+
563+
def _get_tick_position(self):
564+
"""See Axis._get_ticks_position()."""
565+
tick = self._ticklist[0]
566+
if (tick.tick1line.get_visible()
567+
and not tick.tick2line.get_visible()
568+
and tick.label1.get_visible()
569+
and not tick.label2.get_visible()):
570+
return 1
571+
elif (tick.tick2line.get_visible()
572+
and not tick.tick1line.get_visible()
573+
and tick.label2.get_visible()
574+
and not tick.label1.get_visible()):
575+
return 2
576+
elif (tick.tick1line.get_visible()
577+
and tick.tick2line.get_visible()
578+
and tick.label1.get_visible()
579+
and not tick.label2.get_visible()):
580+
return "default"
581+
else:
582+
return "unknown"
583+
584+
def get_ticks(self, numticks, tick_getter):
585+
#if numticks is None:
586+
# numticks = len(self.get_majorticklocs())
587+
588+
while len(self._ticklist) < numticks:
589+
# Update the new tick label properties from the old.
590+
tick = tick_getter()
591+
self._ticklist.append(tick)
592+
self._axis._copy_tick_props(self._ticklist[0], tick)
593+
594+
return self._ticklist[:numticks]
595+
596+
def get_tick_padding(self):
597+
if not self._ticklist:
598+
return 0
599+
else:
600+
return self._ticklist[0].get_tick_padding()
601+
602+
def get_pad_pixels(self):
603+
return self._ticklist[0].get_pad_pixels()
604+
605+
def set_label_alignment(self, label1_va, label1_ha, label2_va, label2_ha):
606+
for t in self._ticklist:
607+
t.label1.set_va(label1_va)
608+
t.label1.set_ha(label1_ha)
609+
t.label2.set_va(label2_va)
610+
t.label2.set_ha(label2_ha)
611+
612+
def get_grid_visible(self):
613+
# Return True/False if all grid lines are on or off, None if they are
614+
# not all in the same state.
615+
if all(tick.gridline.get_visible() for tick in self._ticklist):
616+
return True
617+
elif not any(tick.gridline.get_visible() for tick in self._ticklist):
618+
return False
619+
else:
620+
return None
621+
622+
623+
505624
class _LazyTickList:
506625
"""
507626
A descriptor for lazy instantiation of tick lists.
@@ -639,6 +758,9 @@ def __init__(self, axes, *, pickradius=15, clear=True):
639758
self._major_tick_kw = dict()
640759
self._minor_tick_kw = dict()
641760

761+
self._major_ticks = _TickCollection(self, "majorTicks")
762+
self._minor_ticks = _TickCollection(self, "minorTicks")
763+
642764
if clear:
643765
self.clear()
644766
else:
@@ -695,6 +817,8 @@ def _get_axis_name(self):
695817
return next(name for name, axis in self.axes._axis_map.items()
696818
if axis is self)
697819

820+
# Interface to address the ticklists via a single entity
821+
698822
# During initialization, Axis objects often create ticks that are later
699823
# unused; this turns out to be a very slow step. Instead, use a custom
700824
# descriptor to make the tick lists lazy and instantiate them as needed.
@@ -965,12 +1089,10 @@ def set_tick_params(self, which='major', reset=False, **kwargs):
9651089
else:
9661090
if which in ['major', 'both']:
9671091
self._major_tick_kw.update(kwtrans)
968-
for tick in self.majorTicks:
969-
tick._apply_params(**kwtrans)
1092+
self._major_ticks.apply_params(**kwtrans)
9701093
if which in ['minor', 'both']:
9711094
self._minor_tick_kw.update(kwtrans)
972-
for tick in self.minorTicks:
973-
tick._apply_params(**kwtrans)
1095+
self._minor_ticks.apply_params(**kwtrans)
9741096
# labelOn and labelcolor also apply to the offset text.
9751097
if 'label1On' in kwtrans or 'label2On' in kwtrans:
9761098
self.offsetText.set_visible(
@@ -1107,8 +1229,8 @@ def _translate_tick_params(cls, kw, reverse=False):
11071229

11081230
def set_clip_path(self, path, transform=None):
11091231
super().set_clip_path(path, transform)
1110-
for child in self.majorTicks + self.minorTicks:
1111-
child.set_clip_path(path, transform)
1232+
self._major_ticks.set_clip_path(path, transform)
1233+
self._minor_ticks.set_clip_path(path, transform)
11121234
self.stale = True
11131235

11141236
def get_view_interval(self):
@@ -1379,12 +1501,11 @@ def get_tightbbox(self, renderer=None, *, for_layout_only=False):
13791501
return None
13801502

13811503
def get_tick_padding(self):
1382-
values = []
1383-
if len(self.majorTicks):
1384-
values.append(self.majorTicks[0].get_tick_padding())
1385-
if len(self.minorTicks):
1386-
values.append(self.minorTicks[0].get_tick_padding())
1387-
return max(values, default=0)
1504+
pads = [
1505+
self._major_ticks.get_tick_padding(),
1506+
self._minor_ticks.get_tick_padding(),
1507+
]
1508+
return max((p for p in pads if p is not None), default=0)
13881509

13891510
@martist.allow_rasterization
13901511
def draw(self, renderer):
@@ -1651,14 +1772,8 @@ def get_major_ticks(self, numticks=None):
16511772
"""
16521773
if numticks is None:
16531774
numticks = len(self.get_majorticklocs())
1654-
1655-
while len(self.majorTicks) < numticks:
1656-
# Update the new tick label properties from the old.
1657-
tick = self._get_tick(major=True)
1658-
self.majorTicks.append(tick)
1659-
self._copy_tick_props(self.majorTicks[0], tick)
1660-
1661-
return self.majorTicks[:numticks]
1775+
return self._major_ticks.get_ticks(
1776+
numticks, tick_getter=functools.partial(self._get_tick, True))
16621777

16631778
def get_minor_ticks(self, numticks=None):
16641779
r"""
@@ -1677,14 +1792,8 @@ def get_minor_ticks(self, numticks=None):
16771792
"""
16781793
if numticks is None:
16791794
numticks = len(self.get_minorticklocs())
1680-
1681-
while len(self.minorTicks) < numticks:
1682-
# Update the new tick label properties from the old.
1683-
tick = self._get_tick(major=False)
1684-
self.minorTicks.append(tick)
1685-
self._copy_tick_props(self.minorTicks[0], tick)
1686-
1687-
return self.minorTicks[:numticks]
1795+
return self._minor_ticks.get_ticks(
1796+
numticks, tick_getter=functools.partial(self._get_tick, False))
16881797

16891798
def grid(self, visible=None, which='major', **kwargs):
16901799
"""
@@ -2286,26 +2395,11 @@ def _get_ticks_position(self):
22862395
- "default" if only tick1line, tick2line and label1 are visible;
22872396
- "unknown" otherwise.
22882397
"""
2289-
major = self.majorTicks[0]
2290-
minor = self.minorTicks[0]
2291-
if all(tick.tick1line.get_visible()
2292-
and not tick.tick2line.get_visible()
2293-
and tick.label1.get_visible()
2294-
and not tick.label2.get_visible()
2295-
for tick in [major, minor]):
2296-
return 1
2297-
elif all(tick.tick2line.get_visible()
2298-
and not tick.tick1line.get_visible()
2299-
and tick.label2.get_visible()
2300-
and not tick.label1.get_visible()
2301-
for tick in [major, minor]):
2302-
return 2
2303-
elif all(tick.tick1line.get_visible()
2304-
and tick.tick2line.get_visible()
2305-
and tick.label1.get_visible()
2306-
and not tick.label2.get_visible()
2307-
for tick in [major, minor]):
2308-
return "default"
2398+
major_pos = self._major_ticks._get_tick_position()
2399+
minor_pos = self._minor_ticks._get_tick_position()
2400+
2401+
if major_pos == minor_pos:
2402+
return major_pos
23092403
else:
23102404
return "unknown"
23112405

lib/matplotlib/backend_bases.py

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2438,25 +2438,18 @@ def key_press_handler(event, canvas=None, toolbar=None):
24382438
if event.inaxes is None:
24392439
return
24402440

2441-
# these bindings require the mouse to be over an Axes to trigger
2442-
def _get_uniform_gridstate(ticks):
2443-
# Return True/False if all grid lines are on or off, None if they are
2444-
# not all in the same state.
2445-
return (True if all(tick.gridline.get_visible() for tick in ticks) else
2446-
False if not any(tick.gridline.get_visible() for tick in ticks) else
2447-
None)
2448-
2441+
# these bindings require the mouse to be over an axes to trigger
24492442
ax = event.inaxes
24502443
# toggle major grids in current Axes (default key 'g')
24512444
# Both here and below (for 'G'), we do nothing if *any* grid (major or
24522445
# minor, x or y) is not in a uniform state, to avoid messing up user
24532446
# customization.
24542447
if (event.key in rcParams['keymap.grid']
24552448
# Exclude minor grids not in a uniform state.
2456-
and None not in [_get_uniform_gridstate(ax.xaxis.minorTicks),
2457-
_get_uniform_gridstate(ax.yaxis.minorTicks)]):
2458-
x_state = _get_uniform_gridstate(ax.xaxis.majorTicks)
2459-
y_state = _get_uniform_gridstate(ax.yaxis.majorTicks)
2449+
and None not in [ax.xaxis._minor_ticks.get_grid_visible(),
2450+
ax.yaxis._minor_ticks.get_grid_visible()]):
2451+
x_state = ax.xaxis._major_ticks.get_grid_visible()
2452+
y_state = ax.yaxis._major_ticks.get_grid_visible()
24602453
cycle = [(False, False), (True, False), (True, True), (False, True)]
24612454
try:
24622455
x_state, y_state = (
@@ -2472,10 +2465,10 @@ def _get_uniform_gridstate(ticks):
24722465
# toggle major and minor grids in current Axes (default key 'G')
24732466
if (event.key in rcParams['keymap.grid_minor']
24742467
# Exclude major grids not in a uniform state.
2475-
and None not in [_get_uniform_gridstate(ax.xaxis.majorTicks),
2476-
_get_uniform_gridstate(ax.yaxis.majorTicks)]):
2477-
x_state = _get_uniform_gridstate(ax.xaxis.minorTicks)
2478-
y_state = _get_uniform_gridstate(ax.yaxis.minorTicks)
2468+
and None not in [ax.xaxis._major_ticks.get_grid_visible(),
2469+
ax.yaxis._major_ticks.get_grid_visible()]):
2470+
x_state = ax.xaxis._minor_ticks.get_grid_visible()
2471+
y_state = ax.yaxis._minor_ticks.get_grid_visible()
24792472
cycle = [(False, False), (True, False), (True, True), (False, True)]
24802473
try:
24812474
x_state, y_state = (

lib/matplotlib/projections/polar.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,11 +1528,11 @@ def drag_pan(self, button, key, x, y):
15281528

15291529
trans, vert1, horiz1 = self.get_yaxis_text1_transform(0.0)
15301530
trans, vert2, horiz2 = self.get_yaxis_text2_transform(0.0)
1531-
for t in self.yaxis.majorTicks + self.yaxis.minorTicks:
1532-
t.label1.set_va(vert1)
1533-
t.label1.set_ha(horiz1)
1534-
t.label2.set_va(vert2)
1535-
t.label2.set_ha(horiz2)
1531+
for tick_collection in [
1532+
self.yaxis._major_ticks, self.yaxis._minor_ticks]:
1533+
tick_collection.set_label_alignments(
1534+
label1_va=vert1, label1_ha=horiz1,
1535+
label2_va=vert2, label2_ha=horiz2)
15361536

15371537
elif p.mode == 'zoom':
15381538
(startt, startr), (t, r) = p.trans_inverse.transform(

0 commit comments

Comments
 (0)
0