Added TODO and DEVELOPMENT.
[faces-project.git] / faces / charting / printer.py
blobce9bb35c09d6580d7b4abe64a278fddc919d0264
1 ############################################################################
2 # Copyright (C) 2005 by Reithinger GmbH
3 # mreithinger@web.de
5 # This file is part of faces.
6 #
7 # faces is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # faces is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the
19 # Free Software Foundation, Inc.,
20 # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 ############################################################################
23 import matplotlib.figure as figure
24 import matplotlib.font_manager as font
25 import renderer
26 import tools
27 import widgets
28 import tempfile
29 import os.path
32 class ChartPrinter(object):
33 """
34 Prints out a chart to a specific format
35 """
37 _point_factor = { "cm" : 72.0/2.54,
38 "mm" : 72.0/25.4,
39 "inch" : 72.0,
40 "point" : 1.0 }
42 _extensions = ( "eps", "svg", "bmp", "png", "pdf" )
44 def __init__(self, chart, **kwargs):
45 self.unit = kwargs.get("unit", 'point')
46 self._type = kwargs.get("type", "eps")
47 self.linewidth = kwargs.get("linewidth", 1.0)
48 self.edgecolor = kwargs.get("edgecolor", 'black')
49 dpi = kwargs.get("dpi", 72)
50 size = kwargs.get("size", (10, 10))
51 self._figure = figure.Figure(size, dpi, linewidth=0.0)
52 self._chart = chart
53 self._chart_instance = None
54 self._default_font_size = font.fontManager.get_default_size()
55 self._filename = ""
56 self.orientation = "portrait"
59 def flipy(self):
60 return True
63 def __del__(self):
64 self._figure.set_canvas(None)
65 self.end()
68 def to_point(self, value):
69 if self.unit == "pixel":
70 return (72 * value) / self.dpi
72 return value * self._point_factor[self.unit]
75 def from_point(self, value):
76 if self.unit == "pixel": return (value * self.dpi) / 72
77 return value / self._point_factor[self.unit]
80 def get_dpi(self):
81 return self._figure.get_dpi()
84 def set_dpi(self, dpi):
85 if self._type == "eps": dpi = 72
86 self._figure.set_dpi(dpi)
87 widgets.LazyText.height_cache.clear()
90 dpi = property(get_dpi, set_dpi)
93 def set_filename(self, filename):
94 self._filename = filename
96 for e in self._extensions:
97 if self._filename.endswith(e) \
98 or self._filename.endswith(e.upper()):
99 if e != self._type:
100 self.type = e
102 return
104 if not self._filename.endswith(self._type) \
105 and not self._filename.endswith(self._type.upper()):
106 self._filename += "." + self._type
109 def get_filename(self):
110 return self._filename
112 filename = property(get_filename, set_filename)
115 def set_type(self, type):
116 if type not in self._extensions:
117 raise ValueError("type '%s' not known" % type)
119 self._type = type
121 if type == "eps" and self.dpi != 72:
122 self.dpi = 72
124 self._chart_instance = None
127 def get_type(self):
128 return self._type
130 type = property(get_type, set_type)
133 def set_font_size(self, font_size):
134 tools.set_default_size(font_size)
135 self._chart_instance = None
138 def get_font_size(self):
139 return font.fontManager.get_default_size()
141 font_size = property(get_font_size, set_font_size)
144 def get_valid(self):
145 return bool(self._chart_instance)
148 valid = property(get_valid)
150 def refresh(self):
151 canvas = renderer.PatchedFigureCanvasAgg
152 from charts import _figure_manager
153 self._figure.clf()
154 _figure_manager.canvas = canvas(self._figure)
155 self._figure.set_canvas(_figure_manager.canvas)
156 self._chart_instance = self._chart(self._figure)
159 def save(self):
160 if not self.valid: self.refresh()
161 self._chart_instance._check_limits(False)
162 self._set_frame()
163 self._figure.figurePatch.set_linewidth(self.linewidth)
164 canvas = self._figure.canvas
166 if self.type == "pdf":
167 from matplotlib.backends.backend_pdf import FigureCanvasPdf
168 canvas = canvas.switch_backends(FigureCanvasPdf)
170 canvas.print_figure(self.filename, self.dpi,
171 facecolor=self._figure.get_facecolor(),
172 edgecolor=self.edgecolor,
173 orientation=self.orientation)
176 def check_valid(self):
177 if self.valid: return
178 raise RuntimeError("The printer state is invalid call 'refresh' "\
179 "to make it valid")
182 def end(self):
183 tools.set_default_size(self._default_font_size)
184 widgets.LazyText.height_cache.clear()
187 def get_margins(self):
188 return 0, 0, 0, 0
191 def _set_frame(self):
192 pass
195 class FreePrinter(ChartPrinter):
196 #equally to 1024 x 786 pixels with 64 dpi
197 _width = 1152
198 _height = 864
200 def __init__(self, chart, **kwargs):
201 ChartPrinter.__init__(self, chart, **kwargs)
202 size = kwargs.get("size")
203 if size:
204 self._width = size[0] * 72
205 self._height = size[1] * 72
208 def flipy(self):
209 return True
212 def get_width(self):
213 return self.from_point(self._width)
216 def set_width(self, width):
217 self._width = self.to_point(width)
218 self._figure.set_figsize_inches(self._width/72.0, self._height/72.0)
221 width = property(get_width, set_width)
224 def get_height(self):
225 return self.from_point(self._height)
228 def set_height(self, height):
229 self._height = self.to_point(height)
230 self._figure.set_figsize_inches(self._width/72.0, self._height/72.0)
233 height = property(get_height, set_height)
237 class TimePlotPrinter(FreePrinter):
238 def __init__(self, chart, **kwargs):
239 FreePrinter.__init__(self, chart, **kwargs)
240 self.set_ylimits(*kwargs.get("ylimits", (None, None)))
241 self.set_xlimits(*kwargs.get("xlimits", (None, None)))
244 def refresh(self):
245 FreePrinter.refresh(self)
246 self.autoscale(False)
247 self._set_limits()
250 def _set_limits(self):
251 if not self.valid: return False
252 chart = self._chart_instance
253 chart._set_ylim(ymin=self._ylimits[0], ymax=self._ylimits[1])
254 chart._set_time_lim(*self._time_limits)
255 return True
258 def set_time_limits(self, min=None, max=None):
259 self._time_limits = (min, max)
260 self._set_limits()
263 def get_time_limits(self):
264 self.check_valid()
265 return self._chart_instance._get_time_lim()
268 def set_xlimits(self, min=None, max=None):
269 self._time_limits = (min and int(min), max and int(max))
270 self._set_limits()
273 def get_xlimits(self):
274 self.check_valid()
275 return self._chart_instance._get_xlim()
278 def get_ylimits(self):
279 self.check_valid()
280 return self._chart_instance._get_ylim()
283 def set_ylimits(self, ymin=None, ymax=None):
284 self._ylimits = (ymin, ymax)
285 self._set_limits()
288 def _set_frame(self):
289 chart = self._chart_instance
290 patch = chart._axes_patch
291 chart._set_frame_on(True)
292 patch.set_fill(False)
293 self._figure.set_facecolor(patch.get_facecolor())
294 patch.set_linewidth(self.linewidth)
295 patch.set_edgecolor(self.edgecolor)
298 def autoscale(self, set=True):
299 if not self.valid: self.refresh()
300 chart = self._chart_instance
301 chart._set_autoscale_on(True)
302 chart._autoscale_view()
303 if set:
304 self._ylimits = chart._get_ylim()
305 self._time_limits = chart._get_time_lim()
309 class WidgetPrinter(ChartPrinter):
310 def __init__(self, chart, **kwargs):
311 ChartPrinter.__init__(self, chart, **kwargs)
312 self._ylimits = kwargs.get("ylimits", (None, None))
313 unit = self.unit
314 self.unit = "point"
315 self.set_xlimits(*kwargs.get("xlimits", (None, None)))
316 self.unit = unit
319 def flipy(self):
320 return False
323 def set_height(self, value):
324 self.check_valid()
325 value = self.to_point(value)
326 chart = self._chart_instance
327 ymin, ymax = chart._get_ylim()
328 bottom = chart._right_margin.get()
329 top = chart._top_margin.get()
330 value -= (bottom + top)
331 self._ylimits = (ymax - value, ymax)
332 self._set_limits()
335 def set_ylimits(self, ymin=None, ymax=None):
336 ymin = ymin and -self.to_point(ymin)
337 ymax = ymax and -self.to_point(ymax)
338 self._ylimits = (ymax, ymin)
339 self._set_limits()
342 def get_height(self):
343 self.check_valid()
344 chart = self._chart_instance
345 margin = chart._top_margin.get() + chart._bottom_margin.get()
346 return self.from_point(chart._view_lim.height() + margin)
348 height = property(get_height, set_height)
351 def get_ylimits(self):
352 self.check_valid()
353 ymin, ymax = self._chart_instance._get_ylim()
354 return self.from_point(-ymax), self.from_point(-ymin)
357 def set_ylimits(self, ymin=None, ymax=None):
358 ymin = ymin and -self.to_point(ymin)
359 ymax = ymax and -self.to_point(ymax)
360 self._ylimits = (ymax, ymin)
361 self._set_limits()
364 def refresh(self):
365 ChartPrinter.refresh(self)
366 self.autoscale(False)
367 self._set_limits()
368 self._correct_size()
371 def get_margins(self):
372 self.check_valid()
373 chart = self._chart_instance
374 left = chart._left_margin.get()
375 right = chart._right_margin.get()
376 top = chart._top_margin.get()
377 bottom = chart._bottom_margin.get()
378 return map(self.from_point, (left, top, right, bottom))
381 def _set_frame(self):
382 chart = self._chart_instance
383 patch = chart._axes_patch
384 chart._set_frame_on(True)
385 patch.set_fill(False)
386 self._figure.set_facecolor(patch.get_facecolor())
387 patch.set_linewidth(self.linewidth)
388 patch.set_edgecolor(self.edgecolor)
391 def _set_limits(self):
392 if not self.valid: return False
393 chart = self._chart_instance
394 chart._set_ylim(ymin=self._ylimits[0], ymax=self._ylimits[1])
395 return True
398 def autoscale(self, set=True):
399 if not self.valid: self.refresh()
400 chart = self._chart_instance
401 chart._set_autoscale_on(True)
402 chart._set_auto_scale_y(False)
403 chart._autoscale_view(False)
404 chart._set_ylim(chart._data_lim.intervaly().get_bounds())
405 if set: self._ylimits = chart._get_ylim()
408 def _correct_size(self):
409 raise RuntimeError("abstract")
412 class TimeWidgetPrinter(WidgetPrinter):
413 _width = 1152
415 def __init__(self, chart, **kwargs):
416 WidgetPrinter.__init__(self, chart, **kwargs)
417 size = kwargs.get("size")
418 if size:
419 self._width = size[0] * 72
422 def get_width(self):
423 return self.from_point(self._width)
426 def set_width(self, width):
427 self._width = self.to_point(width)
428 height = self._figure.get_figheight()
429 self._figure.set_figsize_inches(self._width/72.0, height)
431 width = property(get_width, set_width)
434 def set_time_limits(self, min=None, max=None):
435 self._time_limits = (min, max)
436 self._set_limits()
439 def get_time_limits(self):
440 self.check_valid()
441 return self._chart_instance._get_time_lim()
444 def set_xlimits(self, min=None, max=None):
445 self._time_limits = (min and int(min), max and int(max))
446 self._set_limits()
449 def get_xlimits(self):
450 self.check_valid()
451 return self._chart_instance._get_xlim()
454 def _set_limits(self):
455 if WidgetPrinter._set_limits(self):
456 chart = self._chart_instance
457 chart._set_time_lim(*self._time_limits)
458 self._correct_size()
461 def _correct_size(self):
462 if not self.valid: return
463 chart = self._chart_instance
464 margin = chart._top_margin.get() + chart._bottom_margin.get()
465 height = chart._view_lim.height() + margin
466 self._figure.set_figsize_inches(self._width/72.0, height/72.0)
467 self._chart_instance._check_limits(False)
470 def autoscale(self, set=True):
471 WidgetPrinter.autoscale(self, set)
472 if set:
473 chart = self._chart_instance
474 self._time_limits = chart._get_time_lim()
475 self._correct_size()
478 class PointPrinter(WidgetPrinter):
479 def __init__(self, chart, **kwargs):
480 WidgetPrinter.__init__(self, chart, **kwargs)
483 def set_width(self, value):
484 self.check_valid()
485 chart = self._chart_instance
486 xmin, xmax = self.get_xlimits()
487 left = chart._left_margin.get()
488 right = chart._right_margin.get()
489 value -= (left + right)
490 self.set_xlimits(xmin, xmin + value)
493 def get_width(self):
494 self.check_valid()
495 chart = self._chart_instance
496 margin = chart._left_margin.get() + chart._right_margin.get()
497 return self.from_point(chart._view_lim.width() + margin)
499 width = property(get_width, set_width)
502 def get_xlimits(self):
503 self.check_valid()
504 xmin, xmax = self._chart_instance._get_xlim()
505 return self.from_point(xmin), self.from_point(xmax)
508 def set_xlimits(self, xmin=None, xmax=None):
509 xmin = xmin and self.to_point(xmin)
510 xmax = xmax and self.to_point(xmax)
511 self._xlimits = (xmin, xmax)
512 self._set_limits()
515 def _set_limits(self):
516 if WidgetPrinter._set_limits(self):
517 chart = self._chart_instance
518 chart._set_xlim(xmin=self._xlimits[0], xmax=self._xlimits[1])
519 self._correct_size()
522 def _correct_size(self):
523 chart = self._chart_instance
524 hmargin = chart._left_margin.get() + chart._right_margin.get()
525 vmargin = chart._top_margin.get() + chart._bottom_margin.get()
526 width = chart._view_lim.width() + hmargin
527 height = chart._view_lim.height() + vmargin
528 self._figure.set_figsize_inches(width/72.0, height/72.0)
529 self._chart_instance._check_limits(False)
532 def autoscale(self, set=True):
533 chart = self._chart_instance
534 chart._set_xlim(chart._data_lim.intervalx().get_bounds())
535 chart._set_ylim(chart._data_lim.intervaly().get_bounds())
536 if set:
537 self._xlimits = chart._get_xlim()
538 self._ylimits = chart._get_ylim()
539 self._set_limits()