|
18 | 18 | import matplotlib.transforms as transforms
|
19 | 19 | import matplotlib.axis as maxis
|
20 | 20 | import matplotlib.spines as mspines
|
21 |
| -import matplotlib.path as mpath |
22 | 21 | from matplotlib.projections import register_projection
|
23 | 22 |
|
| 23 | + |
24 | 24 | # The sole purpose of this class is to look at the upper, lower, or total
|
25 | 25 | # interval as appropriate and see what parts of the tick to draw, if any.
|
| 26 | +class SkewXTick(maxis.XTick): |
| 27 | + def update_position(self, loc): |
| 28 | + # This ensures that the new value of the location is set before |
| 29 | + # any other updates take place |
| 30 | + self._loc = loc |
| 31 | + super(SkewXTick, self).update_position(loc) |
26 | 32 |
|
| 33 | + def _has_default_loc(self): |
| 34 | + return self.get_loc() is None |
27 | 35 |
|
28 |
| -class SkewXTick(maxis.XTick): |
29 |
| - def draw(self, renderer): |
30 |
| - if not self.get_visible(): |
31 |
| - return |
32 |
| - renderer.open_group(self.__name__) |
| 36 | + def _need_lower(self): |
| 37 | + return (self._has_default_loc() or |
| 38 | + transforms.interval_contains(self.axes.lower_xlim, |
| 39 | + self.get_loc())) |
| 40 | + |
| 41 | + def _need_upper(self): |
| 42 | + return (self._has_default_loc() or |
| 43 | + transforms.interval_contains(self.axes.upper_xlim, |
| 44 | + self.get_loc())) |
33 | 45 |
|
34 |
| - lower_interval = self.axes.xaxis.lower_interval |
35 |
| - upper_interval = self.axes.xaxis.upper_interval |
| 46 | + @property |
| 47 | + def gridOn(self): |
| 48 | + return (self._gridOn and (self._has_default_loc() or |
| 49 | + transforms.interval_contains(self.get_view_interval(), |
| 50 | + self.get_loc()))) |
| 51 | + |
| 52 | + @gridOn.setter |
| 53 | + def gridOn(self, value): |
| 54 | + self._gridOn = value |
36 | 55 |
|
37 |
| - if self.gridOn and transforms.interval_contains( |
38 |
| - self.axes.xaxis.get_view_interval(), self.get_loc()): |
39 |
| - self.gridline.draw(renderer) |
| 56 | + @property |
| 57 | + def tick1On(self): |
| 58 | + return self._tick1On and self._need_lower() |
40 | 59 |
|
41 |
| - if transforms.interval_contains(lower_interval, self.get_loc()): |
42 |
| - if self.tick1On: |
43 |
| - self.tick1line.draw(renderer) |
44 |
| - if self.label1On: |
45 |
| - self.label1.draw(renderer) |
| 60 | + @tick1On.setter |
| 61 | + def tick1On(self, value): |
| 62 | + self._tick1On = value |
46 | 63 |
|
47 |
| - if transforms.interval_contains(upper_interval, self.get_loc()): |
48 |
| - if self.tick2On: |
49 |
| - self.tick2line.draw(renderer) |
50 |
| - if self.label2On: |
51 |
| - self.label2.draw(renderer) |
| 64 | + @property |
| 65 | + def label1On(self): |
| 66 | + return self._label1On and self._need_lower() |
52 | 67 |
|
53 |
| - renderer.close_group(self.__name__) |
| 68 | + @label1On.setter |
| 69 | + def label1On(self, value): |
| 70 | + self._label1On = value |
| 71 | + |
| 72 | + @property |
| 73 | + def tick2On(self): |
| 74 | + return self._tick2On and self._need_upper() |
| 75 | + |
| 76 | + @tick2On.setter |
| 77 | + def tick2On(self, value): |
| 78 | + self._tick2On = value |
| 79 | + |
| 80 | + @property |
| 81 | + def label2On(self): |
| 82 | + return self._label2On and self._need_upper() |
| 83 | + |
| 84 | + @label2On.setter |
| 85 | + def label2On(self, value): |
| 86 | + self._label2On = value |
| 87 | + |
| 88 | + def get_view_interval(self): |
| 89 | + return self.axes.xaxis.get_view_interval() |
54 | 90 |
|
55 | 91 |
|
56 | 92 | # This class exists to provide two separate sets of intervals to the tick,
|
57 | 93 | # as well as create instances of the custom tick
|
58 | 94 | class SkewXAxis(maxis.XAxis):
|
59 |
| - def __init__(self, *args, **kwargs): |
60 |
| - maxis.XAxis.__init__(self, *args, **kwargs) |
61 |
| - self.upper_interval = 0.0, 1.0 |
62 |
| - |
63 | 95 | def _get_tick(self, major):
|
64 |
| - return SkewXTick(self.axes, 0, '', major=major) |
65 |
| - |
66 |
| - @property |
67 |
| - def lower_interval(self): |
68 |
| - return self.axes.viewLim.intervalx |
| 96 | + return SkewXTick(self.axes, None, '', major=major) |
69 | 97 |
|
70 | 98 | def get_view_interval(self):
|
71 |
| - return self.upper_interval[0], self.axes.viewLim.intervalx[1] |
| 99 | + return self.axes.upper_xlim[0], self.axes.lower_xlim[1] |
72 | 100 |
|
73 | 101 |
|
74 | 102 | # This class exists to calculate the separate data range of the
|
75 | 103 | # upper X-axis and draw the spine there. It also provides this range
|
76 | 104 | # to the X-axis artist for ticking and gridlines
|
77 | 105 | class SkewSpine(mspines.Spine):
|
78 | 106 | def _adjust_location(self):
|
79 |
| - trans = self.axes.transDataToAxes.inverted() |
| 107 | + pts = self._path.vertices |
80 | 108 | if self.spine_type == 'top':
|
81 |
| - yloc = 1.0 |
| 109 | + pts[:, 0] = self.axes.upper_xlim |
82 | 110 | else:
|
83 |
| - yloc = 0.0 |
84 |
| - left = trans.transform_point((0.0, yloc))[0] |
85 |
| - right = trans.transform_point((1.0, yloc))[0] |
86 |
| - |
87 |
| - pts = self._path.vertices |
88 |
| - pts[0, 0] = left |
89 |
| - pts[1, 0] = right |
90 |
| - self.axis.upper_interval = (left, right) |
| 111 | + pts[:, 0] = self.axes.lower_xlim |
91 | 112 |
|
92 | 113 |
|
93 | 114 | # This class handles registration of the skew-xaxes as a projection as well
|
@@ -143,13 +164,24 @@ def _set_lim_and_transforms(self):
|
143 | 164 | transforms.IdentityTransform()) +
|
144 | 165 | transforms.Affine2D().skew_deg(rot, 0)) + self.transAxes
|
145 | 166 |
|
| 167 | + @property |
| 168 | + def lower_xlim(self): |
| 169 | + return self.axes.viewLim.intervalx |
| 170 | + |
| 171 | + @property |
| 172 | + def upper_xlim(self): |
| 173 | + pts = [[0., 1.], [1., 1.]] |
| 174 | + return self.transDataToAxes.inverted().transform(pts)[:, 0] |
| 175 | + |
| 176 | + |
146 | 177 | # Now register the projection with matplotlib so the user can select
|
147 | 178 | # it.
|
148 | 179 | register_projection(SkewXAxes)
|
149 | 180 |
|
150 | 181 | if __name__ == '__main__':
|
151 | 182 | # Now make a simple example using the custom projection.
|
152 |
| - from matplotlib.ticker import ScalarFormatter, MultipleLocator |
| 183 | + from matplotlib.ticker import (MultipleLocator, NullFormatter, |
| 184 | + ScalarFormatter) |
153 | 185 | import matplotlib.pyplot as plt
|
154 | 186 | from six import StringIO
|
155 | 187 | import numpy as np
|
@@ -242,15 +274,16 @@ def _set_lim_and_transforms(self):
|
242 | 274 | plt.grid(True)
|
243 | 275 |
|
244 | 276 | # Plot the data using normal plotting functions, in this case using
|
245 |
| - # log scaling in Y, as dicatated by the typical meteorological plot |
246 |
| - ax.semilogy(T, p) |
247 |
| - ax.semilogy(Td, p) |
| 277 | + # log scaling in Y, as dictated by the typical meteorological plot |
| 278 | + ax.semilogy(T, p, color='C3') |
| 279 | + ax.semilogy(Td, p, color='C2') |
248 | 280 |
|
249 | 281 | # An example of a slanted line at constant X
|
250 |
| - l = ax.axvline(0) |
| 282 | + l = ax.axvline(0, color='C0') |
251 | 283 |
|
252 | 284 | # Disables the log-formatting that comes with semilogy
|
253 | 285 | ax.yaxis.set_major_formatter(ScalarFormatter())
|
| 286 | + ax.yaxis.set_minor_formatter(NullFormatter()) |
254 | 287 | ax.set_yticks(np.linspace(100, 1000, 10))
|
255 | 288 | ax.set_ylim(1050, 100)
|
256 | 289 |
|
|
0 commit comments