1
""" This module is a cut-n-paste job of axis.py. This is meant to be a
2
a temporary solution until we merge the Scales functionality into the
3
mainline of Chaco development.
6
from numpy import array, around, absolute, cos, dot, float64, inf, pi, \
9
# Enthought Library imports
10
from enthought.enable.api import ColorTrait, LineStyle
11
from enthought.kiva.traits.kiva_font_trait import KivaFont
12
from enthought.traits.api import Any, Bool, Float, Int, Str, Trait, Unicode, \
13
Event, List, Array, Instance, Enum, Callable
15
# Local relative imports
16
from ticks import AbstractTickGenerator, DefaultTickGenerator
17
from abstract_mapper import AbstractMapper
18
from abstract_overlay import AbstractOverlay
19
from label import Label
20
from log_mapper import LogMapper
22
def DEFAULT_TICK_FORMATTER(val):
23
return ("%f"%val).rstrip("0").rstrip(".")
25
class PlotAxis(AbstractOverlay):
26
""" This class is not currently used. See axis.PlotAxis.
28
The PlotAxis is a visual component that can be rendered on its own as
29
a standalone component or attached as an overlay to another component.
30
(To attach it as an overlay, set its .component attribute.)
32
When it is attached as an overlay, it draws into the padding around
36
# The mapper that drives this axis
37
mapper = Instance(AbstractMapper)
39
# The text of the title
40
title = Trait('', Str, Unicode) #May want to add PlotLabel option
42
# The font in which to render the title
43
title_font = KivaFont('modern 12')
45
# The color in which to render the title
46
title_color = ColorTrait("black")
49
# TODO: Implement this
52
# The thickness (in pixels) of each tick
53
tick_weight = Float(1.0)
55
# The color of the ticks
56
tick_color = ColorTrait("black")
58
# The font in which to render the tick labels
59
tick_label_font = KivaFont('modern 10')
61
# The color of the tick labels
62
tick_label_color = ColorTrait("black")
64
# A callable that is passed the numerical value of each tick label and
65
# which should return a string.
66
tick_label_formatter = Callable(DEFAULT_TICK_FORMATTER)
68
# The number of pixels by which the ticks go "into" the plot area
71
# The number of pixels by which the ticks extend into the label area
74
# Are ticks visible at all?
75
tick_visible = Bool(True)
77
# What is the dataspace interval between ticks?
78
tick_interval = Trait('auto', 'auto', Float)
80
# A callable that implements the AbstractTickGenerator Interface
81
tick_generator = Instance(AbstractTickGenerator)
83
# The location of the axis relative to the plot. This determines where
84
# the axis title is located relative to the axis line.
85
orientation = Enum("top", "bottom", "left", "right")
87
# Is the axis line visible?
88
axis_line_visible = Bool(True)
90
# The color of the axis line
91
axis_line_color = ColorTrait("black")
93
# The line thickness (in pixels) of the axis line
94
axis_line_weight = Float(1.0)
96
# The dash style of the axis line
97
axis_line_style = LineStyle('solid')
99
# A special version of the axis line that is more useful for geophysical
101
# TODO: MOVE THIS OUT OF HERE!
102
small_haxis_style = Bool(False)
104
# Should we do extra work to ensure that the end labels of the axis fall
105
# within the axis's bounding area?
106
ensure_labels_bounded = Bool(False)
108
# Should we prevent the ticks from being rendered outside our bounds?
109
# This is off by default because the standard axis *will* render ticks
110
# that encroach on the plot area.
111
ensure_ticks_bounded = Bool(False)
113
# Fired when our range's bounds change
116
#------------------------------------------------------------------------
117
# Override default values of inherited traits
118
#------------------------------------------------------------------------
120
# Axes will generally want the color of the container to show through
121
bgcolor = ColorTrait("transparent")
123
# Typically, axes are resizable in both dimensions.
126
#------------------------------------------------------------------------
128
#------------------------------------------------------------------------
130
# Cached position calculations
131
_tick_list = List # These are caches of their respective positions
132
_tick_positions = Any #List
133
_tick_label_list = Any
134
_tick_label_positions = Any
135
_tick_label_bounding_boxes = List
136
_major_axis_size = Float
137
_minor_axis_size = Float
139
_title_orientation = Array
141
_origin_point = Array
142
_inside_vector = Array
144
_axis_pixel_vector = Array
145
_end_axis_point = Array
148
ticklabel_cache = List
149
_cache_valid = Bool(False)
152
#------------------------------------------------------------------------
154
#------------------------------------------------------------------------
156
def __init__(self, component=None, **kwargs):
157
# TODO: change this back to a factory in the instance trait some day
158
self.tick_generator = DefaultTickGenerator()
159
# Override init so that our component gets set last. We want the
160
# _component_changed() event handler to get run last.
161
super(PlotAxis, self).__init__(**kwargs)
162
if component is not None:
163
self.component = component
165
def invalidate(self):
167
Invalidates the pre-computed layout and scaling data.
170
self.invalidate_draw()
173
def traits_view(self):
174
""" Returns a View instance for use with Traits UI. This method is
175
called automatically be the Traits framework when .edit_traits() is
178
from axis_view import AxisView
181
#------------------------------------------------------------------------
182
# PlotComponent and AbstractOverlay interface
183
#------------------------------------------------------------------------
185
def do_layout(self, *args, **kw):
186
if self.use_draw_order and self.component is not None:
187
self._layout_as_overlay(*args, **kw)
189
super(PlotAxis, self).do_layout(*args, **kw)
192
def overlay(self, component, gc, view_bounds=None, mode='normal'):
195
self._draw_component(gc, view_bounds, mode, component)
198
def _draw_overlay(self, gc, view_bounds=None, mode='normal'):
199
self._draw_component(gc, view_bounds, mode)
202
def _draw_component(self, gc, view_bounds=None, mode='normal', component=None):
209
# slight optimization: if we set the font correctly on the
210
# base gc before handing it in to our title and tick labels,
211
# their set_font() won't have to do any work.
212
gc.set_font(self.tick_label_font)
214
if not self._cache_valid:
215
if component is not None:
216
self._calculate_geometry(component)
218
self._old_calculate_geometry()
219
self._compute_tick_positions(gc, component)
220
# self._compute_labels(gc)
222
if self.axis_line_visible:
223
self._draw_axis_line(gc, self._origin_point, self._end_axis_point)
228
self._draw_labels(gc)
232
self._cache_valid = True
236
#------------------------------------------------------------------------
237
# Private draw routines
238
#------------------------------------------------------------------------
240
def _layout_as_overlay(self, size=None, force=False):
241
if self.component is not None:
242
if self.orientation in ("left", "right"):
243
self.y = self.component.y
244
self.height = self.component.height
245
if self.orientation == "left":
246
self.width = self.component.padding_left
247
self.x = self.component.outer_x
248
elif self.orientation == "right":
249
self.width = self.component.padding_right
250
self.x = self.component.x2 + 1
252
self.x = self.component.x
253
self.width = self.component.width
254
if self.orientation == "bottom":
255
self.height = self.component.padding_bottom
256
self.y = self.component.outer_y
257
elif self.orientation == "top":
258
self.height = self.component.padding_top
259
self.y = self.component.y2 + 1
262
def _draw_axis_line(self, gc, startpoint, endpoint):
266
gc.set_line_width(self.axis_line_weight)
267
gc.set_stroke_color(self.axis_line_color_)
268
gc.set_line_dash(self.axis_line_style_)
269
gc.move_to(*around(startpoint))
270
gc.line_to(*around(endpoint))
276
def _draw_title_old(self, gc, label=None, v_offset=20):
277
#put in rotation code for right side
280
title_label = Label(text=self.title,
281
font=self.title_font,
282
color=self.title_color,
283
rotate_angle=self.title_angle)
286
tl_bounds = array(title_label.get_width_height(gc), float64)
288
if self.title_angle == 0:
289
text_center_to_corner = -tl_bounds/2.0
291
v_offset *= 2.3 # Hack until we have some better geometry code
292
corner_vec = transpose(-tl_bounds/2.0)
293
rotmatrix = self._rotmatrix(-self.title_angle*pi/180.0)
294
text_center_to_corner = transpose(dot(rotmatrix, corner_vec))[0]
296
# would be good to count tick height, but more complicated.
297
offset = (self._origin_point+self._end_axis_point)/2
298
center_dist = self._center_dist(-self._inside_vector, tl_bounds[0], tl_bounds[1], rotation=self.title_angle)
299
offset -= self._inside_vector * (center_dist + v_offset)
300
offset += text_center_to_corner
302
if self.title_angle == 90.0:
303
# Horrible hack to adjust for the fact that the generic math above isn't
304
# actually putting the label in the right place...
305
offset[1] = offset[1] - tl_bounds[0]/2.0
307
gc.translate_ctm(*offset)
309
gc.translate_ctm(*(-offset))
312
def _draw_title(self, gc, label=None, v_offset=20):
313
""" Draws the title for the axis.
315
#put in rotation code for right side
318
title_label = Label(text=self.title,
319
font=self.title_font,
320
color=self.title_color,
321
rotate_angle=self.title_angle)
324
tl_bounds = array(title_label.get_width_height(gc), float64)
326
if self.title_angle == 0:
327
text_center_to_corner = -tl_bounds/2.0
328
if not self.ticklabel_cache:
331
v_offset = max([l._bounding_box[1] for l in self.ticklabel_cache]) * 1.3
332
offset = (self._origin_point+self._end_axis_point)/2
333
center_dist = self._center_dist(-self._inside_vector, tl_bounds[0], tl_bounds[1], rotation=self.title_angle)
334
offset -= self._inside_vector * (center_dist + v_offset)
335
offset += text_center_to_corner
337
elif self.title_angle == 90:
338
# Center the text vertically
339
if not self.ticklabel_cache:
342
v_offset = (self._end_axis_point[1] - self._origin_point[1] - tl_bounds[0])/2.0
343
h_offset = self.tick_out + tl_bounds[1] + 8
344
if len(self.ticklabel_cache) > 0:
345
h_offset += max([l._bounding_box[0] for l in self.ticklabel_cache])
346
offset = array([self._origin_point[0] - h_offset, self._origin_point[1] + v_offset])
349
if not self.ticklabel_cache:
352
v_offset = max([l._bounding_box[0] for l in self.ticklabel_cache])
353
corner_vec = transpose(-tl_bounds/2.0)
354
rotmatrix = self._rotmatrix(-self.title_angle*pi/180.0)
355
text_center_to_corner = transpose(dot(rotmatrix, corner_vec))[0]
356
offset = (self._origin_point+self._end_axis_point)/2
357
center_dist = self._center_dist(-self._inside_vector, tl_bounds[0], tl_bounds[1], rotation=self.title_angle)
358
offset -= self._inside_vector * (center_dist + v_offset)
359
offset += text_center_to_corner
361
gc.translate_ctm(*offset)
363
gc.translate_ctm(*(-offset))
367
def _draw_ticks(self, gc):
368
if not self.tick_visible:
370
gc.set_stroke_color(self.tick_color_)
371
gc.set_line_width(self.tick_weight)
372
gc.set_antialias(False)
374
tick_in_vector = self._inside_vector*self.tick_in
375
tick_out_vector = self._inside_vector*self.tick_out
376
for tick_pos in self._tick_positions:
377
gc.move_to(*(tick_pos + tick_in_vector))
378
gc.line_to(*(tick_pos - tick_out_vector))
382
def _draw_labels(self, gc):
383
for i in range(len(self._tick_label_positions)):
384
#We want a more sophisticated scheme than just 2 decimals all the time
385
ticklabel = self.ticklabel_cache[i]
386
tl_bounds = self._tick_label_bounding_boxes[i]
388
#base_position puts the tick label at a point where the vector
389
#extending from the tick mark inside 8 units
390
#just touches the rectangular bounding box of the tick label.
391
#Note: This is not necessarily optimal for non
392
#horizontal/vertical axes. More work could be done on this.
394
base_position = (self._center_dist(-self._inside_vector, *tl_bounds)+8) \
395
* -self._inside_vector \
396
- tl_bounds/2.0 + self._tick_label_positions[i]
398
if self.ensure_labels_bounded:
402
elif i == len(self._tick_label_positions)-1:
404
push_pixel_vector = self._axis_pixel_vector * pushdir
405
tlpos = around((self._center_dist(push_pixel_vector,*tl_bounds)+4) \
406
* push_pixel_vector + base_position)
409
tlpos = around(base_position)
411
gc.translate_ctm(*tlpos)
413
gc.translate_ctm(*(-tlpos))
417
#------------------------------------------------------------------------
418
# Private methods for computing positions and layout
419
#------------------------------------------------------------------------
421
def _reset_cache(self):
422
self._tick_positions = []
423
self._tick_label_list = []
424
self._tick_label_positions = []
427
def _compute_tick_positions(self, gc, overlay_component=None):
428
if (self.mapper is None):
430
self._cache_valid = True
433
datalow = self.mapper.range.low
434
datahigh = self.mapper.range.high
435
screenhigh = self.mapper.high_pos
436
screenlow = self.mapper.low_pos
438
if overlay_component is not None and hasattr(overlay_component, "x_direction") and\
439
hasattr(overlay_component, "y_direction"):
440
if self.orientation in ("top", "bottom"):
441
direction = overlay_component.x_direction
442
elif self.orientation in ("left", "right"):
443
direction = overlay_component.y_direction
446
if screenlow>screenhigh:
447
direction = 'flipped'
448
if direction == "flipped":
449
screenlow, screenhigh = screenhigh, screenlow
452
if (datalow == datahigh) or (screenlow == screenhigh) or \
453
(datalow in [inf, -inf]) or (datahigh in [inf, -inf]):
455
self._cache_valid = True
458
if datalow > datahigh:
459
raise RuntimeError, "DataRange low is greater than high; unable to compute axis ticks."
461
if not self.tick_generator:
464
self.tick_generator.font = self.tick_label_font
466
if hasattr(self.tick_generator, "get_ticks_and_labels"):
467
tmp = self.tick_generator.get_ticks_and_labels(datalow, datahigh,
468
screenlow, screenhigh)
474
mapped_tick_positions = (array(self.mapper.map_screen(ticks))-screenlow) / \
475
(screenhigh-screenlow)
476
self._tick_positions = around(array([self._axis_vector*tickpos + self._origin_point \
477
for tickpos in mapped_tick_positions]))
478
self._tick_label_list = ticks
479
self._tick_label_positions = self._tick_positions
480
self.ticklabel_cache = [Label(text=lab, font=self.tick_label_font, color=self.tick_label_color) \
482
self._tick_label_bounding_boxes = [array(ticklabel.get_bounding_box(gc), float64) for ticklabel in self.ticklabel_cache]
485
if isinstance(self.mapper, LogMapper):
490
tick_list = array(self.tick_generator.get_ticks(datalow, datahigh,
494
scale=scale), float64)
496
mapped_tick_positions = (array(self.mapper.map_screen(tick_list))-screenlow) / \
497
(screenhigh-screenlow)
498
self._tick_positions = around(array([self._axis_vector*tickpos + self._origin_point \
499
for tickpos in mapped_tick_positions]))
501
if self.small_haxis_style:
502
# If we're a small axis, we want the endpoints to be the labels regardless of
503
# where the ticks are, as the labels represent the bounds, not where the tick
505
self._tick_label_list = array([datalow, datahigh])
506
mapped_label_positions = (array(self.mapper.map_screen(self._tick_label_list))-screenlow) / \
507
(screenhigh-screenlow)
508
self._tick_label_positions = [self._axis_vector*tickpos + self._origin_point \
509
for tickpos in mapped_label_positions]
511
self._tick_label_list = tick_list
512
self._tick_label_positions = self._tick_positions
513
self._compute_labels(gc)
516
def _compute_labels(self, gc):
517
"""Computes the positions and generates the label objects. Waits
518
for the cache to become invalid.
520
self.ticklabel_cache = []
521
formatter = self.tick_label_formatter
522
# if formatter is not None:
523
# labels = formatter.labels(self._tick_label_positions)
524
for i in range(len(self._tick_label_positions)):
525
val = self._tick_label_list[i]
526
if formatter is not None:
527
tickstring = formatter(val)
529
tickstring = str(val)
530
ticklabel = Label(text=tickstring,
531
font=self.tick_label_font,
532
color=self.tick_label_color)
533
self.ticklabel_cache.append(ticklabel)
535
self._tick_label_bounding_boxes = [array(ticklabel.get_bounding_box(gc), float64) for ticklabel in self.ticklabel_cache]
539
def _calculate_geometry(self, overlay_component=None):
540
if overlay_component is not None:
541
if self.orientation == "top":
542
new_origin = [overlay_component.x, overlay_component.y2]
543
inside_vec = [0.0, -1.0]
544
elif self.orientation == "bottom":
545
new_origin = [overlay_component.x, overlay_component.y]
546
inside_vec = [0.0, 1.0]
547
elif self.orientation == "left":
548
new_origin = [overlay_component.x, overlay_component.y]
549
inside_vec = [1.0, 0.0]
550
else: # self.orientation == "right":
551
new_origin = [overlay_component.x2, overlay_component.y]
552
inside_vec = [-1.0, 0.0]
553
self._origin_point = array(new_origin)
554
self._inside_vector = array(inside_vec)
556
overlay_component = self
557
new_origin = array(self.position)
559
origin = getattr(overlay_component, "origin", 'bottom left')
560
if self.orientation in ('top', 'bottom'):
561
self._major_axis_size = overlay_component.bounds[0]
562
self._minor_axis_size = overlay_component.bounds[1]
563
self._major_axis = array([1., 0.])
564
self._title_orientation = array([0.,1.])
565
if "right" in origin:
569
#this could be calculated...
570
self.title_angle = 0.0
571
elif self.orientation in ('left', 'right'):
572
self._major_axis_size = overlay_component.bounds[1]
573
self._minor_axis_size = overlay_component.bounds[0]
574
self._major_axis = array([0., 1.])
575
self._title_orientation = array([-1., 0])
576
origin = getattr(overlay_component, "origin", 'bottom left')
581
if self.orientation == 'left':
582
self.title_angle = 90.0
584
self.title_angle = 270.0
586
if self.ensure_ticks_bounded:
587
self._origin_point -= self._inside_vector*self.tick_in
589
screenhigh = self.mapper.high_pos
590
screenlow = self.mapper.low_pos
591
# TODO: should this be here, or not?
593
screenlow, screenhigh = screenhigh, screenlow
595
self._end_axis_point = (screenhigh-screenlow)*self._major_axis + self._origin_point
596
self._axis_vector = self._end_axis_point - self._origin_point
597
# This is the vector that represents one unit of data space in terms of screen space.
598
self._axis_pixel_vector = self._axis_vector/sqrt(dot(self._axis_vector,self._axis_vector))
602
def _old_calculate_geometry(self):
603
if hasattr(self, 'mapper') and self.mapper is not None:
604
screenhigh = self.mapper.high_pos
605
screenlow = self.mapper.low_pos
607
# fixme: this should take into account axis orientation
611
if self.orientation in ('top', 'bottom'):
612
self._major_axis_size = self.bounds[0]
613
self._minor_axis_size = self.bounds[1]
614
self._major_axis = array([1., 0.])
615
self._title_orientation = array([0.,1.])
616
#this could be calculated...
617
self.title_angle = 0.0
618
if self.orientation == 'top':
619
self._origin_point = array(self.position) + self._major_axis * screenlow
620
self._inside_vector = array([0.,-1.])
621
else: #self.oriention == 'bottom'
622
self._origin_point = array(self.position) + array([0., self.bounds[1]]) + self._major_axis*screenlow
623
self._inside_vector = array([0., 1.])
624
elif self.orientation in ('left', 'right'):
625
self._major_axis_size = self.bounds[1]
626
self._minor_axis_size = self.bounds[0]
627
self._major_axis = array([0., 1.])
628
self._title_orientation = array([-1., 0])
629
self.title_angle = 90.0
630
if self.orientation == 'left':
631
self._origin_point = array(self.position) + array([self.bounds[0], 0.]) + self._major_axis*screenlow
632
self._inside_vector = array([1., 0.])
633
else: #self.orientation == 'right'
634
self._origin_point = array(self.position) + self._major_axis*screenlow
635
self._inside_vector = array([-1., 0.])
637
# if self.mapper.high_pos<self.mapper.low_pos:
638
# self._origin_point = self._origin_point + self._axis_
640
if self.ensure_ticks_bounded:
641
self._origin_point -= self._inside_vector*self.tick_in
643
self._end_axis_point = (screenhigh-screenlow)*self._major_axis + self._origin_point
644
self._axis_vector = self._end_axis_point - self._origin_point
645
# This is the vector that represents one unit of data space in terms of screen space.
646
self._axis_pixel_vector = self._axis_vector/sqrt(dot(self._axis_vector,self._axis_vector))
650
#------------------------------------------------------------------------
651
# Private helper methods
652
#------------------------------------------------------------------------
654
def _rotmatrix(self, theta):
655
"""Returns a 2x2 rotation matrix for angle theta
657
return array([[cos(theta), sin(theta)], [-sin(theta), cos(theta)]], float64)
659
def _center_dist(self, vect, width, height, rotation=0.0):
660
"""Given a width and height of a rectangle, find the distance in units of the vector,
661
in the direction of the vector, from the center of the rectangle, to wherever the vector
662
leaves the rectangle. This is useful for determining where to place text so it doesn't
663
run into other things.
665
rotvec = transpose(dot(self._rotmatrix(rotation*pi/180.0), transpose(array([vect], float64))))[0]
666
absvec = absolute(rotvec)
668
heightdist = (float(height)/2)/float(absvec[1])
672
widthdist = (float(width)/2)/float(absvec[0])
676
return min(heightdist, widthdist)
679
#------------------------------------------------------------------------
681
#------------------------------------------------------------------------
683
def _bounds_changed(self, old, new):
684
super(PlotAxis, self)._bounds_changed(old, new)
685
self._layout_needed = True
688
def _bounds_items_changed(self, event):
689
super(PlotAxis, self)._bounds_items_changed(event)
690
self._layout_needed = True
693
def _mapper_changed(self, old, new):
695
old.on_trait_change(self.mapper_updated, "updated", remove=True)
697
new.on_trait_change(self.mapper_updated, "updated")
700
def mapper_updated(self):
702
Event handler that gets bound to our mapper's .updated event
706
def _position_changed(self, old, new):
707
super(PlotAxis, self)._position_changed(old, new)
708
self._cache_valid = False
710
def _position_items_changed(self, event):
711
super(PlotAxis, self)._position_items_changed(event)
712
self._cache_valid = False
714
def _position_changed_for_component(self):
715
self._cache_valid = False
717
def _position_items_changed_for_component(self):
718
self._cache_valid = False
720
def _bounds_changed_for_component(self):
721
self._cache_valid = False
722
self._layout_needed = True
724
def _bounds_items_changed_for_component(self):
725
self._cache_valid = False
726
self._layout_needed = True
728
def _origin_changed_for_component(self):
731
def _updated_fired(self):
732
"""If our bounds changed, redraw."""
733
self._cache_valid = False
736
def _invalidate(self):
737
self._cache_valid = False
738
self.invalidate_draw()
740
self.component.invalidate_draw()
741
# self.component.request_redraw()
743
# self.request_redraw()
746
def _component_changed(self):
747
if self.mapper is not None:
748
# If there is a mapper set, just leave it be.
751
# Try to pick the most appropriate mapper for our orientation
752
# and what information we can glean from our component.
753
attrmap = { "left": ("ymapper", "y_mapper", "value_mapper"),
754
"bottom": ("xmapper", "x_mapper", "index_mapper"), }
755
attrmap["right"] = attrmap["left"]
756
attrmap["top"] = attrmap["bottom"]
758
component = self.component
759
attr1, attr2, attr3 = attrmap[self.orientation]
760
for attr in attrmap[self.orientation]:
761
if hasattr(component, attr):
762
self.mapper = getattr(component, attr)
766
#------------------------------------------------------------------------
767
# The following event handlers just invalidate our previously computed
768
# Label instances and backbuffer if any of our visual attributes change.
769
# TODO: refactor this stuff and the caching of contained objects (e.g. Label)
770
#------------------------------------------------------------------------
772
def _title_changed(self):
773
self.invalidate_draw()
775
self.component.invalidate_draw()
776
# self.component.request_redraw()
778
# self.request_redraw()
780
def _title_color_changed(self):
781
return self._invalidate()
783
def _title_font_changed(self):
784
return self._invalidate()
786
def _tick_weight_changed(self):
787
return self._invalidate()
789
def _tick_color_changed(self):
790
return self._invalidate()
792
def _tick_font_changed(self):
793
return self._invalidate()
795
def _tick_label_font_changed(self):
796
return self._invalidate()
798
def _tick_label_color_changed(self):
799
return self._invalidate()
801
def _tick_in_changed(self):
802
return self._invalidate()
804
def _tick_out_changed(self):
805
return self._invalidate()
807
def _tick_visible_changed(self):
808
return self._invalidate()
810
def _tick_interval_changed(self):
811
return self._invalidate()
813
def _axis_line_color_changed(self):
814
return self._invalidate()
816
def _axis_line_weight_changed(self):
817
return self._invalidate()
819
def _axis_line_style_changed(self):
820
return self._invalidate()
822
def _orientation_changed(self):
823
return self._invalidate()
825
#------------------------------------------------------------------------
826
# Persistence-related methods
827
#------------------------------------------------------------------------
829
def __getstate__(self):
834
'_tick_label_positions',
835
'_tick_label_bounding_boxes',
839
'_title_orientation',
844
'_axis_pixel_vector',
850
state = super(PlotAxis,self).__getstate__()
851
for key in dont_pickle:
852
if state.has_key(key):
857
def __setstate__(self, state):
858
super(PlotAxis,self).__setstate__(state)
859
self._mapper_changed(None, self.mapper)
861
self._cache_valid = False
865
# EOF ########################################################################