|
4 | 4 | from matplotlib.externals import six |
5 | 5 |
|
6 | 6 | import os |
7 | | -import numpy |
8 | 7 |
|
9 | 8 | from matplotlib._pylab_helpers import Gcf |
10 | | -from matplotlib.backend_bases import RendererBase, GraphicsContextBase,\ |
11 | | - FigureManagerBase, FigureCanvasBase, NavigationToolbar2, TimerBase |
| 9 | +from matplotlib.backend_bases import FigureManagerBase, FigureCanvasBase, \ |
| 10 | + NavigationToolbar2, TimerBase |
12 | 11 | from matplotlib.backend_bases import ShowBase |
13 | 12 |
|
14 | | -from matplotlib.cbook import maxdict |
15 | 13 | from matplotlib.figure import Figure |
16 | | -from matplotlib.path import Path |
17 | | -from matplotlib.mathtext import MathTextParser |
18 | | -from matplotlib.colors import colorConverter |
19 | 14 | from matplotlib import rcParams |
20 | 15 |
|
21 | 16 | from matplotlib.widgets import SubplotTool |
22 | 17 |
|
23 | 18 | import matplotlib |
24 | 19 | from matplotlib.backends import _macosx |
25 | 20 |
|
| 21 | +from .backend_agg import RendererAgg |
| 22 | + |
26 | 23 |
|
27 | 24 | class Show(ShowBase): |
28 | 25 | def mainloop(self): |
29 | 26 | _macosx.show() |
30 | 27 | show = Show() |
31 | 28 |
|
32 | 29 |
|
33 | | -class RendererMac(RendererBase): |
34 | | - """ |
35 | | - The renderer handles drawing/rendering operations. Most of the renderer's |
36 | | - methods forward the command to the renderer's graphics context. The |
37 | | - renderer does not wrap a C object and is written in pure Python. |
38 | | - """ |
39 | | - |
40 | | - texd = maxdict(50) # a cache of tex image rasters |
41 | | - |
42 | | - def __init__(self, dpi, width, height): |
43 | | - RendererBase.__init__(self) |
44 | | - self.dpi = dpi |
45 | | - self.width = width |
46 | | - self.height = height |
47 | | - self.gc = GraphicsContextMac() |
48 | | - self.gc.set_dpi(self.dpi) |
49 | | - self.mathtext_parser = MathTextParser('MacOSX') |
50 | | - |
51 | | - def set_width_height (self, width, height): |
52 | | - self.width, self.height = width, height |
53 | | - |
54 | | - def draw_path(self, gc, path, transform, rgbFace=None): |
55 | | - if rgbFace is not None: |
56 | | - rgbFace = tuple(rgbFace) |
57 | | - linewidth = gc.get_linewidth() |
58 | | - gc.draw_path(path, transform, linewidth, rgbFace) |
59 | | - |
60 | | - def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None): |
61 | | - if rgbFace is not None: |
62 | | - rgbFace = tuple(rgbFace) |
63 | | - linewidth = gc.get_linewidth() |
64 | | - gc.draw_markers(marker_path, marker_trans, path, trans, linewidth, rgbFace) |
65 | | - |
66 | | - def draw_path_collection(self, gc, master_transform, paths, all_transforms, |
67 | | - offsets, offsetTrans, facecolors, edgecolors, |
68 | | - linewidths, linestyles, antialiaseds, urls, |
69 | | - offset_position): |
70 | | - if offset_position=='data': |
71 | | - offset_position = True |
72 | | - else: |
73 | | - offset_position = False |
74 | | - path_ids = [] |
75 | | - for path, transform in self._iter_collection_raw_paths( |
76 | | - master_transform, paths, all_transforms): |
77 | | - path_ids.append((path, transform)) |
78 | | - master_transform = master_transform.get_matrix() |
79 | | - offsetTrans = offsetTrans.get_matrix() |
80 | | - gc.draw_path_collection(master_transform, path_ids, all_transforms, |
81 | | - offsets, offsetTrans, facecolors, edgecolors, |
82 | | - linewidths, linestyles, antialiaseds, |
83 | | - offset_position) |
84 | | - |
85 | | - def draw_quad_mesh(self, gc, master_transform, meshWidth, meshHeight, |
86 | | - coordinates, offsets, offsetTrans, facecolors, |
87 | | - antialiased, edgecolors): |
88 | | - gc.draw_quad_mesh(master_transform.get_matrix(), |
89 | | - meshWidth, |
90 | | - meshHeight, |
91 | | - coordinates, |
92 | | - offsets, |
93 | | - offsetTrans.get_matrix(), |
94 | | - facecolors, |
95 | | - antialiased, |
96 | | - edgecolors) |
97 | | - |
98 | | - def new_gc(self): |
99 | | - self.gc.save() |
100 | | - self.gc.set_hatch(None) |
101 | | - self.gc._alpha = 1.0 |
102 | | - self.gc._forced_alpha = False # if True, _alpha overrides A from RGBA |
103 | | - return self.gc |
104 | | - |
105 | | - def draw_gouraud_triangle(self, gc, points, colors, transform): |
106 | | - points = transform.transform(points) |
107 | | - gc.draw_gouraud_triangle(points, colors) |
108 | | - |
109 | | - def get_image_magnification(self): |
110 | | - return self.gc.get_image_magnification() |
111 | | - |
112 | | - def draw_image(self, gc, x, y, im): |
113 | | - nrows, ncols, data = im.as_rgba_str() |
114 | | - gc.draw_image(x, y, nrows, ncols, data) |
115 | | - |
116 | | - def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None): |
117 | | - # todo, handle props, angle, origins |
118 | | - scale = self.gc.get_image_magnification() |
119 | | - size = prop.get_size_in_points() |
120 | | - texmanager = self.get_texmanager() |
121 | | - key = s, size, self.dpi, angle, texmanager.get_font_config() |
122 | | - im = self.texd.get(key) # Not sure what this does; just copied from backend_agg.py |
123 | | - if im is None: |
124 | | - Z = texmanager.get_grey(s, size, self.dpi*scale) |
125 | | - Z = numpy.array(255.0 - Z * 255.0, numpy.uint8) |
126 | | - |
127 | | - gc.draw_mathtext(x, y, angle, Z) |
128 | | - |
129 | | - def _draw_mathtext(self, gc, x, y, s, prop, angle): |
130 | | - scale = self.gc.get_image_magnification() |
131 | | - ox, oy, width, height, descent, image, used_characters = \ |
132 | | - self.mathtext_parser.parse(s, self.dpi*scale, prop) |
133 | | - descent /= scale |
134 | | - xd = descent * numpy.sin(numpy.deg2rad(angle)) |
135 | | - yd = descent * numpy.cos(numpy.deg2rad(angle)) |
136 | | - x = numpy.round(x + ox + xd) |
137 | | - y = numpy.round(y + oy - yd) |
138 | | - gc.draw_mathtext(x, y, angle, 255 - image.as_array()) |
139 | | - |
140 | | - def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): |
141 | | - if ismath: |
142 | | - self._draw_mathtext(gc, x, y, s, prop, angle) |
143 | | - else: |
144 | | - family = prop.get_family() |
145 | | - weight = prop.get_weight() |
146 | | - # transform weight into string for the native backend |
147 | | - if weight >= 700: |
148 | | - weight = 'bold' |
149 | | - else: |
150 | | - weight = 'normal' |
151 | | - style = prop.get_style() |
152 | | - points = prop.get_size_in_points() |
153 | | - size = self.points_to_pixels(points) |
154 | | - gc.draw_text(x, y, six.text_type(s), family, size, weight, style, angle) |
155 | | - |
156 | | - def get_text_width_height_descent(self, s, prop, ismath): |
157 | | - if ismath=='TeX': |
158 | | - # todo: handle props |
159 | | - texmanager = self.get_texmanager() |
160 | | - fontsize = prop.get_size_in_points() |
161 | | - w, h, d = texmanager.get_text_width_height_descent(s, fontsize, |
162 | | - renderer=self) |
163 | | - return w, h, d |
164 | | - if ismath: |
165 | | - ox, oy, width, height, descent, fonts, used_characters = \ |
166 | | - self.mathtext_parser.parse(s, self.dpi, prop) |
167 | | - return width, height, descent |
168 | | - family = prop.get_family() |
169 | | - weight = prop.get_weight() |
170 | | - # transform weight into string for the native backend |
171 | | - if weight >= 700: |
172 | | - weight = 'bold' |
173 | | - else: |
174 | | - weight = 'normal' |
175 | | - style = prop.get_style() |
176 | | - points = prop.get_size_in_points() |
177 | | - size = self.points_to_pixels(points) |
178 | | - width, height, descent = self.gc.get_text_width_height_descent( |
179 | | - six.text_type(s), family, size, weight, style) |
180 | | - return width, height, descent |
181 | | - |
182 | | - def flipy(self): |
183 | | - return False |
184 | | - |
185 | | - def points_to_pixels(self, points): |
186 | | - return points/72.0 * self.dpi |
187 | | - |
188 | | - def option_image_nocomposite(self): |
189 | | - return True |
190 | | - |
191 | | - |
192 | | -class GraphicsContextMac(_macosx.GraphicsContext, GraphicsContextBase): |
193 | | - """ |
194 | | - The GraphicsContext wraps a Quartz graphics context. All methods |
195 | | - are implemented at the C-level in macosx.GraphicsContext. These |
196 | | - methods set drawing properties such as the line style, fill color, |
197 | | - etc. The actual drawing is done by the Renderer, which draws into |
198 | | - the GraphicsContext. |
199 | | - """ |
200 | | - def __init__(self): |
201 | | - GraphicsContextBase.__init__(self) |
202 | | - _macosx.GraphicsContext.__init__(self) |
203 | | - |
204 | | - def set_alpha(self, alpha): |
205 | | - GraphicsContextBase.set_alpha(self, alpha) |
206 | | - _alpha = self.get_alpha() |
207 | | - _macosx.GraphicsContext.set_alpha(self, _alpha, self.get_forced_alpha()) |
208 | | - rgb = self.get_rgb() |
209 | | - _macosx.GraphicsContext.set_foreground(self, rgb) |
210 | | - |
211 | | - def set_foreground(self, fg, isRGBA=False): |
212 | | - GraphicsContextBase.set_foreground(self, fg, isRGBA) |
213 | | - rgb = self.get_rgb() |
214 | | - _macosx.GraphicsContext.set_foreground(self, rgb) |
215 | | - |
216 | | - def set_graylevel(self, fg): |
217 | | - GraphicsContextBase.set_graylevel(self, fg) |
218 | | - _macosx.GraphicsContext.set_graylevel(self, fg) |
219 | | - |
220 | | - def set_clip_rectangle(self, box): |
221 | | - GraphicsContextBase.set_clip_rectangle(self, box) |
222 | | - if not box: return |
223 | | - _macosx.GraphicsContext.set_clip_rectangle(self, box.bounds) |
224 | | - |
225 | | - def set_clip_path(self, path): |
226 | | - GraphicsContextBase.set_clip_path(self, path) |
227 | | - if not path: return |
228 | | - path = path.get_fully_transformed_path() |
229 | | - _macosx.GraphicsContext.set_clip_path(self, path) |
230 | | - |
231 | | - |
232 | 30 | ######################################################################## |
233 | 31 | # |
234 | 32 | # The following functions and classes are for pylab and implement |
@@ -311,14 +109,42 @@ class FigureCanvasMac(_macosx.FigureCanvas, FigureCanvasBase): |
311 | 109 | def __init__(self, figure): |
312 | 110 | FigureCanvasBase.__init__(self, figure) |
313 | 111 | width, height = self.get_width_height() |
314 | | - self.renderer = RendererMac(figure.dpi, width, height) |
315 | 112 | _macosx.FigureCanvas.__init__(self, width, height) |
316 | 113 |
|
| 114 | + @property |
| 115 | + def renderer(self): |
| 116 | + l, b, w, h = self.figure.bbox.bounds |
| 117 | + key = w, h, self.figure.dpi |
| 118 | + try: |
| 119 | + self._lastKey, self._renderer |
| 120 | + except AttributeError: |
| 121 | + need_new_renderer = True |
| 122 | + else: |
| 123 | + need_new_renderer = (self._lastKey != key) |
| 124 | + |
| 125 | + if need_new_renderer: |
| 126 | + self._renderer = RendererAgg(w, h, self.figure.dpi) |
| 127 | + self._lastKey = key |
| 128 | + |
| 129 | + return self._renderer |
| 130 | + |
| 131 | + def _draw(self, device_scale): |
| 132 | + figure = self.figure |
| 133 | + |
| 134 | + orig_dpi = figure.dpi |
| 135 | + try: |
| 136 | + figure.dpi *= device_scale |
| 137 | + renderer = self.renderer |
| 138 | + figure.draw(renderer) |
| 139 | + finally: |
| 140 | + figure.dpi = orig_dpi |
| 141 | + |
| 142 | + return renderer |
| 143 | + |
317 | 144 | def draw_idle(self, *args, **kwargs): |
318 | 145 | self.invalidate() |
319 | 146 |
|
320 | 147 | def resize(self, width, height): |
321 | | - self.renderer.set_width_height(width, height) |
322 | 148 | dpi = self.figure.dpi |
323 | 149 | width /= dpi |
324 | 150 | height /= dpi |
|
0 commit comments