2 #@+node:@file charting/faxes.py
5 #@+node:<< Copyright >>
6 ############################################################################
7 # Copyright (C) 2005, 2006, 2007, 2008 by Reithinger GmbH
10 # This file is part of faces.
12 # faces is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 2 of the License, or
15 # (at your option) any later version.
17 # faces is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the
24 # Free Software Foundation, Inc.,
25 # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 ############################################################################
28 #@-node:<< Copyright >>
31 A special axes for faces charts
35 import matplotlib
.axes
as axes
36 import matplotlib
.artist
as artist
37 import matplotlib
.transforms
as mtrans
38 import matplotlib
.ticker
as ticker
39 import matplotlib
._image
as mimage
40 import matplotlib
.font_manager
as font
45 import renderer
as prend
53 def _cint(val
): return int(math
.ceil(val
))
56 def cut_canvas(axes
, reset
=False):
58 bbox
= axes
.content_bbox
59 except AttributeError:
63 if not reset
and axes
._last
_viewbounds
!= bbox
.get_bounds():
64 #when the size has changed, don't scale
65 old_bbox
= mtrans
.lbwh_to_bbox(*axes
._last
_viewbounds
)
66 trans
= mtrans
.get_bbox_transform(axes
.viewLim
, old_bbox
)
67 data_box
= mtrans
.inverse_transform_bbox(trans
, bbox
)
68 xmin
= axes
.viewLim
.xmin()
69 ymax
= axes
.viewLim
.ymax()
72 axes
.set_ylim(ymax
- data_box
.height(), ymax
, emit
=True)
75 axes
.set_xlim(xmin
, xmin
+ data_box
.width(), emit
=True)
76 except AttributeError:
79 axes
._last
_viewbounds
= bbox
.get_bounds()
81 #@+node:class _WidgetCollection
85 class _WidgetCollection(artist
.Artist
):
86 #@ << class _WidgetCollection declarations >>
87 #@+node:<< class _WidgetCollection declarations >>
88 # a dummy to avoid a complete rewrite of axes draw
91 #@-node:<< class _WidgetCollection declarations >>
95 def __init__(self
, draw_forward
):
96 artist
.Artist
.__init
__(self
)
97 self
.draw_forward
= draw_forward
100 def draw(self
, renderer
):
101 if self
.get_visible():
102 self
.draw_forward(renderer
)
105 #@-node:class _WidgetCollection
107 #time_it(self.draw_forward, renderer)
111 def _get_margin(name
, kwargs
):
112 margin
= kwargs
.get(name
+ "_margin")
113 if margin
is not None:
114 del kwargs
[name
+ "_margin"]
116 margin
= mtrans
.Value(0)
120 #@+node:class MarginAxes
121 class MarginAxes(axes
.Axes
):
123 An axes with a title bar inside the axes
127 def __init__(self
, *args
, **kwargs
):
128 self
.top_margin
= _get_margin("top", kwargs
)
129 self
.bottom_margin
= _get_margin("bottom", kwargs
)
130 self
.left_margin
= _get_margin("left", kwargs
)
131 self
.right_margin
= _get_margin("right", kwargs
)
132 axes
.Axes
.__init
__(self
, *args
, **kwargs
)
134 #@+node:build_margin_transform
135 def build_margin_transform(self
, left
=True, bottom
=True,
136 right
=True, top
=True):
139 get_bbox_transform
= mtrans
.get_bbox_transform
142 left_margin
= self
.left_margin
* self
.fig_point_to_pixel
143 left
= self
.bbox
.ll().x() + left_margin
145 left
= self
.bbox
.ll().x()
148 right_margin
= self
.right_margin
* self
.fig_point_to_pixel
149 right
= self
.bbox
.ur().x() - right_margin
151 right
= self
.bbox
.ur().x()
154 top_margin
= self
.top_margin
* self
.fig_point_to_pixel
155 top
= self
.bbox
.ur().y() - top_margin
157 top
= self
.bbox
.ur().y()
160 bottom_margin
= self
.bottom_margin
* self
.fig_point_to_pixel
161 bottom
= self
.bbox
.ll().y() + bottom_margin
163 bottom
= self
.bbox
.ll().y()
165 bbox
= Bbox(Point(left
, bottom
), Point(right
, top
))
167 transform
= get_bbox_transform(self
.viewLim
, bbox
)
168 transform
.set_funcx(self
.transData
.get_funcx())
169 transform
.set_funcy(self
.transData
.get_funcy())
171 #@-node:build_margin_transform
172 #@+node:_set_lim_and_transforms
173 def _set_lim_and_transforms(self
):
174 axes
.Axes
._set
_lim
_and
_transforms
(self
)
175 self
.fig_point_to_pixel
= self
.get_figure().dpi
/ mtrans
.Value(72)
177 self
.org_transData
= self
.transData
178 self
.org_transAxes
= self
.transAxes
180 self
.transData
= self
.build_margin_transform()
181 self
.content_bbox
= self
.transData
.get_bbox2()
182 #self.content_bbox is self.bbox reduced by the margins
184 self
.transAxes
= mtrans
.get_bbox_transform(mtrans
.unit_bbox(),
186 #@-node:_set_lim_and_transforms
188 def in_axes(self
, xwin
, ywin
):
189 return self
.content_bbox
.contains(xwin
, ywin
)
194 self
.axesPatch
.set_transform(self
.org_transAxes
)
197 def draw(self
, renderer
, inframe
=False):
198 axes
.Axes
.draw(self
, renderer
, inframe
)
199 if self
.axison
and self
._frameon
:
200 fill
= self
.axesPatch
.get_fill()
201 self
.axesPatch
.set_fill(False)
202 self
.axesPatch
.draw(renderer
)
203 self
.axesPatch
.set_fill(fill
)
206 #@-node:class MarginAxes
207 #@+node:class WidgetAxes
208 class WidgetAxes(MarginAxes
):
210 An axes which is optimized to display widgets.
211 If widgets are not inside the current view they will not
216 def __init__(self
, *args
, **kwargs
):
217 self
._first
_draw
= True
220 self
._visible
_widgets
= []
221 self
.widget_artist
= _WidgetCollection(self
._draw
_widgets
)
222 MarginAxes
.__init
__(self
, *args
, **kwargs
)
228 self
.dataLim
.intervalx().set_bounds(sys
.maxint
, -sys
.maxint
)
229 self
.dataLim
.intervaly().set_bounds(sys
.maxint
, -sys
.maxint
)
230 self
._fobj
_map
.clear()
232 self
.marker
= patches
.Rectangle((0,0), 0, 0)
233 self
.marker
.widget
= None
234 self
.marker
.set_visible(False)
235 self
.add_artist(self
.marker
)
236 self
.marker
.set_clip_box(self
.content_bbox
)
237 self
.xaxis
.set_major_locator(ticker
.NullLocator())
238 self
.xaxis
.set_minor_locator(ticker
.NullLocator())
239 self
.yaxis
.set_major_locator(ticker
.NullLocator())
240 self
.yaxis
.set_minor_locator(ticker
.NullLocator())
242 self
.add_collection(self
.widget_artist
)
245 def check_limits(self
, cut
=True):
247 Changes the viewLimits to reasonable values
249 if cut
: cut_canvas(self
)
252 def reset_limits(self
, cut
=True):
254 Sets the data width and data size to the default values.
255 e.g. reset the y axis to display the fonts in the original size
257 self
.check_limits(cut
)
258 vmin
, vmax
= self
.get_ylim()
259 height
= self
.content_bbox
.height() / self
.fig_point_to_pixel
.get()
260 self
.set_ylim(vmax
- height
, vmax
, emit
=True)
262 #@+node:_get_renderer
263 def _get_renderer(self
):
264 if not self
._cachedRenderer
:
265 Renderer
= prend
.PatchedRendererAgg
266 self
._cachedRenderer
= Renderer(10, 10, self
.get_figure().dpi
)
268 return self
._cachedRenderer
269 #@-node:_get_renderer
271 def add_widget(self
, widget
):
273 idendity
= widget
.fobj
._idendity
_()
274 self
._fobj
_map
.setdefault(idendity
, []).append(widget
)
276 self
.widgets
.append(widget
)
278 widget
.set_figure(self
.figure
)
279 widget
.set_clip_box(self
.content_bbox
)
280 widget
.set_transform(self
.transData
)
283 horz
, vert
= widget
.prepare_draw(self
._get
_renderer
(),
285 self
.fig_point_to_pixel
)
288 set_bounds
= self
.dataLim
.intervalx().set_bounds
289 set_bounds(min(widget
.bbox
.xmin(), self
.dataLim
.xmin()),
290 max(widget
.bbox
.xmax(), self
.dataLim
.xmax()))
294 set_bounds
= self
.dataLim
.intervaly().set_bounds
295 extra
= tools
.VSEP
.get() * 4
296 set_bounds(min(widget
.bbox
.ymin() - extra
, self
.dataLim
.ymin()), 0)
300 def mark_widget(self
, widget
=None):
301 ow
= self
.marker
.widget
302 self
.marker
.widget
= widget
303 if not widget
: self
.marker
.set_visible(False)
307 def find_widget(self
, fobj
):
309 return self
._fobj
_map
.get(fobj
)
311 idendity
= fobj
._idendity
_()
312 widgets
= self
._fobj
_map
.get(idendity
, ())
314 #first try to find visible widgets
315 identicals
= filter(lambda w
: w
.fobj
is fobj
, widgets
)
317 if w
in self
._visible
_widgets
: return w
319 if identicals
: return identicals
[0]
322 if w
in self
._visible
_widgets
: return w
324 return widgets
and widgets
[0] or None
327 def widget_at(self
, x
, y
):
329 found
= filter(lambda w
: w
.contains(x
, y
), self
._visible
_widgets
)
330 if found
: return found
[-1]
333 #@+node:set_focused_on
334 def set_focused_on(self
):
335 self
.marker
.update(self
.focused_props
)
336 #@-node:set_focused_on
337 #@+node:set_focused_off
338 def set_focused_off(self
):
339 self
.marker
.update(self
.marker_props
)
340 #@-node:set_focused_off
342 def set_marker(self
, focused_props
, normal_props
):
343 self
.focused_props
= focused_props
344 self
.marker_props
= normal_props
345 self
.marker
.update(normal_props
)
347 #@+node:widget_x_visible
348 def widget_x_visible(self
, widget
):
350 bbox
= widget
.get_bounds(self
._get
_renderer
())
351 xmin
, xmax
= self
.get_xlim()
353 wxmin
, wxmax
= bbox
.intervalx().get_bounds()
354 wwidth
= wxmax
- wxmin
356 vwidth
= min(wwidth
, width
)
358 if wxmax
<= xmin
+ vwidth
:
359 xmin
= wxmax
- vwidth
362 if wxmin
>= xmax
- vwidth
:
363 xmax
= wxmin
+ vwidth
366 self
.set_xlim(xmin
, xmax
)
367 #@-node:widget_x_visible
368 #@+node:widget_y_visible
369 def widget_y_visible(self
, widget
):
370 ymin
, ymax
= self
.get_ylim()
372 wymin
, wymax
= widget
.bbox
.intervaly().get_bounds()
373 if wymax
<= ymin
+ height
/ 2:
374 ymin
= wymax
- height
/ 2
377 if wymin
>= ymax
- height
/ 2:
378 ymax
= wymin
+ height
/ 2
381 self
.set_ylim(ymin
, ymax
)
382 #@-node:widget_y_visible
384 def zoomx(self
, numsteps
):
385 MarginAxes
.zoomx(self
, numsteps
)
386 if self
.marker
.get_visible():
387 self
.widget_x_visible(self
.marker
.widget
)
388 self
.widget_y_visible(self
.marker
.widget
)
391 def zoomy(self
, numsteps
):
392 MarginAxes
.zoomy(self
, numsteps
)
394 if self
.marker
.get_visible():
395 self
.widget_x_visible(self
.marker
.widget
)
396 self
.widget_y_visible(self
.marker
.widget
)
400 def _calc_hsep(self
):
401 trans
= self
.transData
402 vsep
= tools
.VSEP
.get() * self
.point_to_pixel
.get()
403 origin
= trans
.inverse_xy_tup((0, 0))
404 seps
= trans
.inverse_xy_tup((vsep
, vsep
))
405 tools
.HSEP
.set(seps
[0] - origin
[0])
407 #@+node:_draw_widgets
408 def _draw_widgets(self
, renderer
):
409 trans
= self
.transData
410 data_box
= mtrans
.inverse_transform_bbox(trans
, self
.content_bbox
)
413 if self
._speed
_cache
:
414 l
, b
, w
, h
= self
._speed
_bbox
.get_bounds()
415 l
, b
= self
.transData
.xy_tup((l
, b
))
416 renderer
.draw_image(l
, b
, self
._speed
_cache
, self
.content_bbox
)
418 for w
in self
.widgets
:
419 if isinstance(w
, (widgets
.Row
, widgets
.Column
)):
420 w
.draw(renderer
, data_box
)
422 self
._visible
_widgets
= self
.widgets
424 self
._visible
_widgets
= [ w
for w
in self
.widgets
425 if w
.draw(renderer
, data_box
) ]
427 #print "widgets drawn", len(self._visible_widgets)
428 if self
.marker
.widget
:
429 if self
.marker
.widget
.overlaps(data_box
):
430 bbox
= self
.marker
.widget
.bbox
431 self
.marker
.set_bounds(*bbox
.get_bounds())
432 self
.marker
.set_visible(True)
434 self
.marker
.set_visible(False)
435 #@-node:_draw_widgets
436 #@+node:clear_speed_cache
437 def clear_speed_cache(self
):
438 self
._speed
_cache
= None
439 #@-node:clear_speed_cache
442 def speed_up(self
, max_size
):
443 self
._speed
_cache
= None
445 if not self
.widgets
: return
448 renderer
= self
._get
_renderer
()
449 all_data
= self
.dataLim
.deepcopy()
451 xmin
= ymin
= sys
.maxint
452 xmax
= ymax
= -sys
.maxint
454 for w
in self
.widgets
:
455 bounds
= w
.get_bounds(renderer
)
456 xmin
= min(xmin
, bounds
.xmin())
457 xmax
= max(xmax
, bounds
.xmax())
458 ymin
= min(ymin
, bounds
.ymin())
459 ymax
= max(ymax
, bounds
.ymax())
461 all_data
.intervalx().set_bounds(xmin
, xmax
)
462 all_data
.intervaly().set_bounds(ymin
, ymax
)
464 all_view
= mtrans
.transform_bbox(self
.transData
, all_data
)
466 # increase view because of rounding mistakes
467 xmin
, xmax
= all_view
.intervalx().get_bounds()
468 ymin
, ymax
= all_view
.intervaly().get_bounds()
469 all_view
.intervalx().set_bounds(xmin
- 1, xmax
+ 1)
470 all_view
.intervaly().set_bounds(ymin
- 1, ymax
+ 1)
472 if all_view
.width() * all_view
.height() * 4 > max_size
:
475 #adjust all_data to increased all_view
476 all_data
= mtrans
.inverse_transform_bbox(self
.transData
, all_view
)
478 Renderer
= prend
.SpeedupRenderer
479 cache
= Renderer(_cint(all_view
.width()), _cint(all_view
.height()),
480 self
.get_figure().dpi
)
483 render_bbox
= mtrans
.lbwh_to_bbox(0, 0, all_view
.width(), all_view
.height())
484 all_trans
= mtrans
.get_bbox_transform(all_data
, render_bbox
)
486 for w
in self
.widgets
:
487 if isinstance(w
, (widgets
.Row
, widgets
.Column
)): continue
488 w
.set_transform(all_trans
)
489 w
.set_clip_box(render_bbox
)
490 w
.draw(cache
, all_data
)
491 w
.set_transform(self
.transData
)
492 w
.set_clip_box(self
.content_bbox
)
494 self
._speed
_cache
= mimage
.frombuffer(cache
.buffer_rgba(0, 0),
495 cache
.width
, cache
.height
, 1)
496 if self
._speed
_cache
:
497 self
._speed
_cache
.flipud_out()
498 self
._speed
_bbox
= all_data
501 def draw(self
, renderer
, inframe
=False):
503 self
._first
_draw
= False
504 widgets
= map(lambda w
: (w
[1].zorder
, w
[0], w
[1]),
505 enumerate(self
.widgets
))
507 self
.widgets
= map(lambda ziw
: ziw
[2], widgets
)
509 MarginAxes
.draw(self
, renderer
, inframe
)
511 #@+node:_set_lim_and_transforms
512 def _set_lim_and_transforms(self
):
516 MarginAxes
._set
_lim
_and
_transforms
(self
)
517 dtop
= self
.viewLim
.ur().y()
518 dbottom
= self
.viewLim
.ll().y()
520 vtop
= self
.content_bbox
.ur().y()
521 vbottom
= self
.content_bbox
.ll().y()
522 self
.point_to_pixel
= (vtop
- vbottom
) / (dtop
- dbottom
)
523 cut_canvas(self
, True)
524 #@-node:_set_lim_and_transforms
526 #@-node:class WidgetAxes
527 #@+node:class PointAxes
528 class PointAxes(WidgetAxes
):
530 An axes which scales x, y proportional to points
534 def __init__(self
, *args
, **kwargs
):
535 WidgetAxes
.__init
__(self
, *args
, **kwargs
)
536 self
.zoomx
= self
.zoomy
541 self
.dataLim
.intervalx().set_bounds(0, 0)
542 self
.dataLim
.intervaly().set_bounds(0, 0)
546 def check_limits(self
, cut
=True):
547 WidgetAxes
.check_limits(self
, cut
)
548 size
= (self
.viewLim
.width(), self
.viewLim
.height())
549 if size
!= self
.__last
_size
:
550 prop
= self
.content_bbox
.width() / self
.content_bbox
.height()
551 pwidth
= size
[1] * prop
552 if pwidth
!= size
[0]:
553 #we have to correct x
554 size
= (pwidth
, size
[1])
555 xmin
= self
.viewLim
.xmin()
556 self
.set_xlim(xmin
, xmin
+ pwidth
)
558 self
.__last
_size
= size
560 #@+node:autoscale_view
561 def autoscale_view(self
, cut
=True):
562 if not self
._autoscaleon
: return
563 self
.check_limits(cut
)
565 width
= self
.dataLim
.width()
566 height
= self
.dataLim
.height()
568 prop
= self
.content_bbox
.width() / self
.content_bbox
.height()
569 pwidth
= height
* prop
571 xmin
= self
.dataLim
.xmin()
572 ymax
= self
.dataLim
.ymax()
575 self
.set_xlim(xmin
, xmin
+ pwidth
)
576 self
.set_ylim(ymax
- height
, ymax
)
578 self
.set_xlim(xmin
, xmin
+ width
)
579 self
.set_ylim(ymax
- width
/ prop
, ymax
)
581 self
.__last
_size
= (self
.viewLim
.width(), self
.viewLim
.height())
582 #@-node:autoscale_view
584 #@-node:class PointAxes
585 #@+node:class TimeAxes
586 class TimeAxes(object):
587 #@ << class TimeAxes declarations >>
588 #@+node:<< class TimeAxes declarations >>
593 #@-node:<< class TimeAxes declarations >>
596 #@+node:set_time_axis
597 def set_time_axis(self
, time_axis
):
599 self
.collections
.remove(self
.time_axis
)
603 self
.time_axis
= time_axis
604 self
.add_collection(self
.time_axis
)
605 axis_transform
= self
.build_margin_transform(top
=False)
606 self
.time_axis
.set_transform(axis_transform
)
607 self
.time_axis
.set_clip_box(axis_transform
.get_bbox2())
608 self
.update_time_axis()
609 #@-node:set_time_axis
610 #@+node:xaxis_timescale
611 def xaxis_timescale(self
, time_scale
):
612 self
.time_scale
= time_scale
613 self
.xaxis
.set_major_locator(ticker
.NullLocator())
614 self
.xaxis
.set_minor_locator(ticker
.NullLocator())
615 #@-node:xaxis_timescale
617 def set_time_lim(self
, xmin
=None, xmax
=None, emit
=False):
618 xmin
= xmin
and self
.time_scale
.to_num(xmin
)
619 xmax
= xmax
and self
.time_scale
.to_num(xmax
)
620 self
.set_xlim(xmin
=xmin
, xmax
=xmax
, emit
=emit
)
623 def get_time_lim(self
):
624 xmin
, xmax
= self
.get_xlim()
625 xmin
= self
.time_scale
.to_num(int(xmin
))
626 xmax
= self
.time_scale
.to_num(int(xmax
))
627 return xmin
.to_datetime(), xmax
.to_datetime()
630 def format_coord(self
, x
, y
):
631 'return a format string formatting the x, y coord'
634 xs
= self
.time_scale
.to_num(int(x
)).strftime()
636 xs
= self
.format_xdata(x
)
638 ys
= self
.format_ydata(y
)
639 return 'x=%s, y=%s'%(xs
,ys
)
641 #@+node:update_time_axis
642 def update_time_axis(self
):
643 ah
= self
.time_axis \
644 and self
.time_axis
.get_visible() \
645 and self
.time_axis
.calc_height() or 0
647 self
.top_margin
.set(ah
)
648 #@-node:update_time_axis
655 #@-node:class TimeAxes
656 #@+node:class TimePlotAxes
657 class TimePlotAxes(TimeAxes
, MarginAxes
):
658 #@ << class TimePlotAxes declarations >>
659 #@+node:<< class TimePlotAxes declarations >>
662 #@-node:<< class TimePlotAxes declarations >>
666 def __init__(self
, *args
, **kwargs
):
667 MarginAxes
.__init
__(self
, *args
, **kwargs
)
670 def draw(self
, renderer
, inframe
=False):
672 self
.first_draw
= False
673 for a
in self
.lines
: a
.set_clip_box(self
.content_bbox
)
674 for a
in self
.texts
: a
.set_clip_box(self
.content_bbox
)
675 for a
in self
.patches
: a
.set_clip_box(self
.content_bbox
)
676 for a
in self
.artists
: a
.set_clip_box(self
.content_bbox
)
678 MarginAxes
.draw(self
, renderer
, inframe
)
679 cut_canvas(self
, True)
682 #@-node:class TimePlotAxes
683 #@+node:class TimeWidgetAxes
684 class TimeWidgetAxes(TimeAxes
, WidgetAxes
):
686 An axes wich displays widgets horizontal in time. (e.g. GanttCharts)
688 #@ << class TimeWidgetAxes declarations >>
689 #@+node:<< class TimeWidgetAxes declarations >>
692 #@-node:<< class TimeWidgetAxes declarations >>
696 def __init__(self
, *args
, **kwargs
):
697 WidgetAxes
.__init
__(self
, *args
, **kwargs
)
699 #@+node:set_auto_scale_y
700 def set_auto_scale_y(self
, do_scale
=True):
701 self
.auto_scale_y
= do_scale
702 #@-node:set_auto_scale_y
704 def check_limits(self
, cut
=True):
705 WidgetAxes
.check_limits(self
, cut
)
707 vmin
, vmax
= self
.viewLim
.intervaly().get_bounds()
709 self
.viewLim
.intervaly().set_bounds((vmin
- vmax
), 0)
711 #@+node:autoscale_view
712 def autoscale_view(self
, cut
=True):
713 if not self
._autoscaleon
: return
714 self
.check_limits(cut
)
716 all_data
= self
.dataLim
.deepcopy()
717 reduced
= self
.content_bbox
.deepcopy()
718 renderer
= self
._get
_renderer
()
720 if self
.auto_scale_y
:
721 ymin
, ymax
= self
.dataLim
.intervaly().get_bounds()
722 self
.set_ylim(ymin
, ymax
)
726 xmin
, xmax
= self
.dataLim
.intervalx().get_bounds()
727 self
.set_xlim(xmin
, xmax
, emit
=False)
730 #Notice: it is not correct to just get the bounds
731 #in the actual data coords an set the view limits
732 #This is because text on the left or right bound
733 #has always the same pixel width. This means the text witdh
734 #in data coord changes when the data coord scale changes.
736 #find out the bounds in actual data coords
737 for w
in self
.widgets
:
738 bounds
= w
.get_bounds(renderer
)
739 xmin
= min(xmin
, bounds
.xmin())
740 xmax
= max(xmax
, bounds
.xmax())
742 all_data
.intervalx().set_bounds(xmin
, xmax
)
744 #the complete bound in pixel coords
745 all_view
= mtrans
.transform_bbox(self
.transData
, all_data
)
746 add_space
= 0.08 * self
.get_figure().get_dpi() #2mm margin left an right
748 left_offset
= self
.content_bbox
.xmin() - all_view
.xmin() + add_space
749 right_offset
= all_view
.xmax() - self
.content_bbox
.xmax() + add_space
750 width
= self
.content_bbox
.width()
752 if left_offset
> width
/ 4: left_offset
= width
/ 4
753 if right_offset
> width
/ 4: right_offset
= width
/ 4
755 # scale down the pixel bounds
756 xmin1
= self
.content_bbox
.xmin() + left_offset
757 xmax1
= self
.content_bbox
.xmax() - right_offset
758 reduced
.intervalx().set_bounds(xmin1
, xmax1
)
760 # create a new transformation from scaled down pixel coords to limits
761 trans
= mtrans
.get_bbox_transform(reduced
, self
.viewLim
)
762 data_box
= mtrans
.transform_bbox(trans
, self
.content_bbox
)
763 self
.set_xlim(data_box
.xmin(), data_box
.xmax(), emit
=True)
764 cut_canvas(self
, True)
765 #@-node:autoscale_view
767 def widget_at(self
, x
, y
):
768 return WidgetAxes
.widget_at(self
, x
, y
)
771 #@-node:class TimeWidgetAxes
773 #@-node:@file charting/faxes.py