@@ -550,36 +550,19 @@ def __init__(self, major):
550550 def __get__ (self , instance , owner ):
551551 if instance is None :
552552 return self
553- else :
554- # instance._get_tick() can itself try to access the majorTicks
555- # attribute (e.g. in certain projection classes which override
556- # e.g. get_xaxis_text1_transform). In order to avoid infinite
557- # recursion, first set the majorTicks on the instance temporarily
558- # to an empty list. Then create the tick; note that _get_tick()
559- # may call reset_ticks(). Therefore, the final tick list is
560- # created and assigned afterwards.
561- if self ._major :
562- instance .majorTicks = []
563- tick = instance ._get_tick (major = True )
564- tick .clipbox = instance .clipbox
565- tick ._clippath = instance ._clippath
566- tick ._clipon = instance ._clipon
567- tick .gridline .clipbox = instance .clipbox
568- tick .gridline ._clippath = instance ._clippath
569- tick .gridline ._clipon = instance ._clipon
570- instance .majorTicks = [tick ]
571- return instance .majorTicks
572- else :
573- instance .minorTicks = []
574- tick = instance ._get_tick (major = False )
575- tick .clipbox = instance .clipbox
576- tick ._clippath = instance ._clippath
577- tick ._clipon = instance ._clipon
578- tick .gridline .clipbox = instance .clipbox
579- tick .gridline ._clippath = instance ._clippath
580- tick .gridline ._clipon = instance ._clipon
581- instance .minorTicks = [tick ]
582- return instance .minorTicks
553+ # instance._get_tick() can itself try to access the majorTicks
554+ # attribute (e.g. in certain projection classes which override
555+ # e.g. get_xaxis_text1_transform). In order to avoid infinite
556+ # recursion, first set the majorTicks on the instance temporarily
557+ # to an empty list. Then create the tick; note that _get_tick()
558+ # may call reset_ticks(). Therefore, the final tick list is
559+ # created and assigned afterwards.
560+ attr = 'majorTicks' if self ._major else 'minorTicks'
561+ setattr (instance , attr , [])
562+ tick = instance ._get_tick (major = self ._major )
563+ instance ._propagate_axis_state_to_tick (tick )
564+ setattr (instance , attr , [tick ])
565+ return getattr (instance , attr )
583566
584567
585568class Axis (martist .Artist ):
@@ -858,17 +841,76 @@ def get_children(self):
858841 return [self .label , self .offsetText ,
859842 * self .get_major_ticks (), * self .get_minor_ticks ()]
860843
844+ def _propagate_axis_state_to_tick (self , tick ):
845+ """
846+ Copy Axis state that a lazily-created Tick would otherwise miss.
847+
848+ Two things on the Axis were captured at Axes-construction time and
849+ need to flow through to a Tick built later by `_LazyTickList`:
850+
851+ 1. Clip state (``clipbox`` / ``_clippath`` / ``_clipon``) -- set on
852+ the Axis by `Axis.set_clip_path`, which runs during
853+ ``Axes.__clear`` before the Tick exists. Only the Tick itself
854+ and its gridline receive clip (matching `Tick.set_clip_path`).
855+ 2. Per-Artist rcParams (`path.sketch`, `path.effects`) -- captured
856+ by `Artist.__init__` from the current `rcParams`. The Axis
857+ captured them at Axes-construction time, which may have been
858+ inside a ``plt.xkcd()`` / ``rc_context`` that has since exited;
859+ re-stamp them on every sub-artist of the fresh Tick so the
860+ rendered output matches the pre-lazy code path.
861+ """
862+ for artist in (tick , tick .gridline ):
863+ artist .clipbox = self .clipbox
864+ artist ._clippath = self ._clippath
865+ artist ._clipon = self ._clipon
866+ for artist in (tick , tick .tick1line , tick .tick2line , tick .gridline ,
867+ tick .label1 , tick .label2 ):
868+ artist ._sketch = self ._sketch
869+ artist ._path_effects = self ._path_effects
870+
871+ def _tick_kw_from_rcparams (self , major ):
872+ """
873+ Snapshot the rcParam-derived kwargs for a fresh `.Tick`.
874+
875+ `Tick.__init__` reads a handful of rcParams (size, width, color,
876+ grid styling, ...) when the corresponding kwarg is None. With lazy
877+ tick lists the Tick can be materialized long after the Axis was
878+ created -- e.g. inside a ``plt.xkcd()`` context whose ``__exit__``
879+ has already run -- which would silently yield different values
880+ than the eager pre-lazy behaviour. Freezing the relevant rcParams
881+ into the kw dict at init/clear time restores parity with that
882+ pre-lazy path.
883+ """
884+ name = f"{ self .axis_name } tick" # 'xtick' or 'ytick'
885+ major_minor = "major" if major else "minor"
886+ rc = mpl .rcParams
887+ labelcolor = rc [f"{ name } .labelcolor" ]
888+ if cbook ._str_equal (labelcolor , "inherit" ):
889+ labelcolor = rc [f"{ name } .color" ]
890+ return {
891+ "gridOn" : rc ["axes.grid" ] and rc ["axes.grid.which" ] in ("both" , major_minor ),
892+ "size" : rc [f"{ name } .{ major_minor } .size" ],
893+ "width" : rc [f"{ name } .{ major_minor } .width" ],
894+ "pad" : rc [f"{ name } .{ major_minor } .pad" ],
895+ "color" : rc [f"{ name } .color" ],
896+ "labelcolor" : labelcolor ,
897+ "labelsize" : rc [f"{ name } .labelsize" ],
898+ "tickdir" : rc [f"{ name } .direction" ],
899+ "grid_color" : mpl ._val_or_rc (
900+ None , f"grid.{ major_minor } .color" , "grid.color" ),
901+ "grid_linestyle" : mpl ._val_or_rc (
902+ None , f"grid.{ major_minor } .linestyle" , "grid.linestyle" ),
903+ "grid_linewidth" : mpl ._val_or_rc (
904+ None , f"grid.{ major_minor } .linewidth" , "grid.linewidth" ),
905+ "grid_alpha" : mpl ._val_or_rc (
906+ None , f"grid.{ major_minor } .alpha" , "grid.alpha" ),
907+ }
908+
861909 def _reset_major_tick_kw (self ):
862- self ._major_tick_kw .clear ()
863- self ._major_tick_kw ['gridOn' ] = (
864- mpl .rcParams ['axes.grid' ] and
865- mpl .rcParams ['axes.grid.which' ] in ('both' , 'major' ))
910+ self ._major_tick_kw = self ._tick_kw_from_rcparams (major = True )
866911
867912 def _reset_minor_tick_kw (self ):
868- self ._minor_tick_kw .clear ()
869- self ._minor_tick_kw ['gridOn' ] = (
870- mpl .rcParams ['axes.grid' ] and
871- mpl .rcParams ['axes.grid.which' ] in ('both' , 'minor' ))
913+ self ._minor_tick_kw = self ._tick_kw_from_rcparams (major = False )
872914
873915 def clear (self ):
874916 """
@@ -899,13 +941,10 @@ def clear(self):
899941 # Clear the callback registry for this axis, or it may "leak"
900942 self .callbacks = cbook .CallbackRegistry (signals = ["units" ])
901943
902- # whether the grids are on
903- self ._major_tick_kw ['gridOn' ] = (
904- mpl .rcParams ['axes.grid' ] and
905- mpl .rcParams ['axes.grid.which' ] in ('both' , 'major' ))
906- self ._minor_tick_kw ['gridOn' ] = (
907- mpl .rcParams ['axes.grid' ] and
908- mpl .rcParams ['axes.grid.which' ] in ('both' , 'minor' ))
944+ # Snapshot rcParams-derived tick kwargs so lazily-materialized
945+ # Ticks see the same values the eager (pre-lazy) code path did.
946+ self ._reset_major_tick_kw ()
947+ self ._reset_minor_tick_kw ()
909948 self .reset_ticks ()
910949
911950 self ._converter = None
@@ -939,9 +978,7 @@ def _existing_ticks(self, major=None):
939978 doing so would
940979
941980 (a) create throwaway Tick objects during ``Axes.__init__`` and
942- ``Axes.__clear`` -- defeating the whole point of the lazy lists --
943- and
944-
981+ ``Axes.__clear``
945982 (b) risk re-entering the
946983 ``Spine.set_position -> Axis.reset_ticks -> Axis.set_clip_path
947984 -> _LazyTickList.__get__ -> Tick.__init__ -> Spine.set_position``
0 commit comments