|
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