~ubuntu-branches/ubuntu/utopic/python-chaco/utopic

« back to all changes in this revision

Viewing changes to enthought/chaco/plot_label.py

  • Committer: Bazaar Package Importer
  • Author(s): Varun Hiremath
  • Date: 2011-04-06 19:03:54 UTC
  • mfrom: (7.2.2 sid)
  • Revision ID: james.westby@ubuntu.com-20110406190354-rwd55l2ezjecfo41
Tags: 3.4.0-2
d/rules: fix pyshared directory path (Closes: #621116)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
""" Defines the PlotLabel class.
2
 
"""
3
 
from enthought.kiva import font_metrics_provider
4
 
from enthought.traits.api import Delegate, Enum, Instance, Str, Trait
5
 
 
6
 
from abstract_overlay import AbstractOverlay
7
 
from label import Label
8
 
 
9
 
 
10
 
class PlotLabel(AbstractOverlay):
11
 
    """ A label used by plots. 
12
 
    
13
 
    This class wraps a simple Label instance, and delegates some traits to it.
14
 
    """
15
 
    
16
 
    # The text of the label.
17
 
    text = Delegate("_label")
18
 
    # The color of the label text.
19
 
    color = Delegate("_label", modify=True)
20
 
    # The font for the label text.
21
 
    font = Delegate("_label")
22
 
    # The angle of rotation of the label.
23
 
    angle = Delegate("_label", "rotate_angle")
24
 
    
25
 
    #------------------------------------------------------------------------
26
 
    # Layout-related traits
27
 
    #------------------------------------------------------------------------
28
 
    
29
 
    # Horizontal justification used if the label has more horizontal space
30
 
    # than it needs.
31
 
    hjustify = Enum("center", "left", "right")
32
 
    
33
 
    # Vertical justification used if the label has more vertical space than it
34
 
    # needs.
35
 
    vjustify = Enum("center", "bottom", "top")
36
 
    
37
 
    # The position of this label relative to the object it is overlaying.
38
 
    # Can be "top", "left", "right", "bottom", and optionally can be preceeded
39
 
    # by the words "inside" or "outside", separated by a space.  If "inside"
40
 
    # and "outside" are not provided, then defaults to "outside".
41
 
    # Examples:
42
 
    #     inside top
43
 
    #     outside right
44
 
    overlay_position = Trait("outside top", Str, None)
45
 
    
46
 
    # Should this PlotLabel modify the padding on its underlying component
47
 
    # if there is not enough room to lay out the text?
48
 
    # FIXME: This could cause cycles in layout, so not implemented for now
49
 
    #modify_component = Bool(True)
50
 
 
51
 
    # By default, this acts like a component and will render on the main
52
 
    # "plot" layer unless its **component** attribute gets set.
53
 
    draw_layer = "plot"
54
 
 
55
 
    #------------------------------------------------------------------------
56
 
    # Private traits
57
 
    #------------------------------------------------------------------------
58
 
    
59
 
    # The label has a fixed height and can be resized horizontally. (Overrides
60
 
    # PlotComponent.)
61
 
    resizable = "h"
62
 
 
63
 
    # The Label instance this plot label is wrapping.
64
 
    _label = Instance(Label, args=())
65
 
 
66
 
    
67
 
    def __init__(self, text="", *args, **kw):
68
 
        super(PlotLabel, self).__init__(*args, **kw)
69
 
        self.text = text
70
 
        return
71
 
    
72
 
    def overlay(self, component, gc, view_bounds=None, mode="normal"):
73
 
        """ Draws this label overlaid on another component.
74
 
        
75
 
        Overrides AbstractOverlay.
76
 
        """
77
 
        self._draw_overlay(gc, view_bounds, mode)
78
 
        return
79
 
 
80
 
    def get_preferred_size(self):
81
 
        """ Returns the label's preferred size.
82
 
        
83
 
        Overrides PlotComponent.
84
 
        """
85
 
        dummy_gc = font_metrics_provider()
86
 
        size = self._label.get_bounding_box(dummy_gc)
87
 
        return size
88
 
    
89
 
    def do_layout(self):
90
 
        """ Tells this component to do layout.
91
 
        
92
 
        Overrides PlotComponent.
93
 
        """
94
 
        if self.component is not None:
95
 
            self._layout_as_overlay()
96
 
        else:
97
 
            self._layout_as_component()
98
 
        return
99
 
    
100
 
    def _draw_overlay(self, gc, view_bounds=None, mode="normal"):
101
 
        """ Draws the overlay layer of a component.
102
 
        
103
 
        Overrides PlotComponent.
104
 
        """
105
 
        try:
106
 
            # Perform justification and compute the correct offsets for
107
 
            # the label position
108
 
            width, height = self._label.get_bounding_box(gc)
109
 
            if self.hjustify == "left":
110
 
                x_offset = 0
111
 
            elif self.hjustify == "right":
112
 
                x_offset = self.width - width
113
 
            elif self.hjustify == "center":
114
 
                x_offset = int((self.width - width) / 2)
115
 
            
116
 
            if self.vjustify == "bottom":
117
 
                y_offset = 0
118
 
            elif self.vjustify == "top":
119
 
                y_offset = self.height - height
120
 
            elif self.vjustify == "center":
121
 
                y_offset = int((self.height - height) / 2)
122
 
            
123
 
            gc.save_state()
124
 
            
125
 
            # XXX: Uncomment this after we fix kiva GL backend's clip stack
126
 
            #gc.clip_to_rect(self.x, self.y, self.width, self.height)
127
 
 
128
 
            # We have to translate to our position because the label
129
 
            # tries to draw at (0,0).
130
 
            gc.translate_ctm(self.x + x_offset, self.y + y_offset)
131
 
            self._label.draw(gc)
132
 
        finally:
133
 
            gc.restore_state()
134
 
        return
135
 
    
136
 
    def _draw_plot(self, gc, view_bounds=None, mode="normal"):
137
 
        if self.component is None:
138
 
            # We are not overlaying anything else, so we should render
139
 
            # on this layer
140
 
            self._draw_overlay(gc, view_bounds, mode)
141
 
 
142
 
    def _layout_as_component(self, size=None, force=False):
143
 
        pass
144
 
    
145
 
    def _layout_as_overlay(self, size=None, force=False):
146
 
        """ Lays out the label as an overlay on another component.
147
 
        """
148
 
        if self.component is not None:
149
 
            orientation = self.overlay_position
150
 
            outside = True
151
 
            if "inside" in orientation:
152
 
                tmp = orientation.split()
153
 
                tmp.remove("inside")
154
 
                orientation = tmp[0]
155
 
                outside = False
156
 
            elif "outside" in orientation:
157
 
                tmp = orientation.split()
158
 
                tmp.remove("outside")
159
 
                orientation = tmp[0]
160
 
 
161
 
            if orientation in ("left", "right"):
162
 
                self.y = self.component.y
163
 
                self.height = self.component.height
164
 
                if not outside:
165
 
                    gc = font_metrics_provider()
166
 
                    self.width = self._label.get_bounding_box(gc)[0]
167
 
                if orientation == "left":
168
 
                    if outside:
169
 
                        self.x = self.component.outer_x
170
 
                        self.width = self.component.padding_left
171
 
                    else:
172
 
                        self.outer_x = self.component.x
173
 
                elif orientation == "right":
174
 
                    if outside:
175
 
                        self.x = self.component.x2 + 1
176
 
                        self.width = self.component.padding_right
177
 
                    else:
178
 
                        self.x = self.component.x2 - self.outer_width
179
 
            elif orientation in ("bottom", "top"):
180
 
                self.x = self.component.x
181
 
                self.width = self.component.width
182
 
                if not outside:
183
 
                    gc = font_metrics_provider()
184
 
                    self.height = self._label.get_bounding_box(gc)[1]
185
 
                if orientation == "bottom":
186
 
                    if outside:
187
 
                        self.y = self.component.outer_y
188
 
                        self.height = self.component.padding_bottom
189
 
                    else:
190
 
                        self.outer_y = self.component.y
191
 
                elif orientation == "top":
192
 
                    if outside:
193
 
                        self.y = self.component.y2 + 1
194
 
                        self.height = self.component.padding_top
195
 
                    else:
196
 
                        self.y = self.component.y2 - self.outer_height
197
 
            else:
198
 
                # Leave the position alone
199
 
                pass
200
 
        return
201
 
 
202
 
    def _text_changed(self, old, new):
203
 
        self._label.text = new
204
 
        self.do_layout()
205
 
        return
206
 
 
207
 
    def _font_changed(self, old, new):
208
 
        self._label.font = new
209
 
        self.do_layout()
210
 
        return
211
 
 
212
 
    def _angle_changed(self, old, new):
213
 
        self._label.rotate_angle = new
214
 
        self.do_layout()
215
 
        return
216
 
 
217
 
    def _overlay_position_changed(self):
218
 
        self.do_layout()
219
 
 
220
 
    def _component_changed(self, old, new):
221
 
        if new:
222
 
            self.draw_layer = "overlay"
223
 
        else:
224
 
            self.draw_layer = "plot"
225
 
        return
226
 
 
227
 
 
228
 
 
229
 
# EOF
 
1
""" Defines the PlotLabel class.
 
2
"""
 
3
 
 
4
from __future__ import with_statement
 
5
 
 
6
from enthought.enable.font_metrics_provider import font_metrics_provider
 
7
from enthought.traits.api import DelegatesTo, Enum, Instance, Str, Trait
 
8
 
 
9
from abstract_overlay import AbstractOverlay
 
10
from label import Label
 
11
 
 
12
 
 
13
LabelDelegate = DelegatesTo("_label")
 
14
 
 
15
class PlotLabel(AbstractOverlay):
 
16
    """ A label used by plots. 
 
17
    
 
18
    This class wraps a simple Label instance, and delegates some traits to it.
 
19
    """
 
20
    
 
21
    # The text of the label.
 
22
    text = LabelDelegate
 
23
    # The color of the label text.
 
24
    color = DelegatesTo("_label")
 
25
    # The font for the label text.
 
26
    font = LabelDelegate
 
27
    # The angle of rotation of the label.
 
28
    angle = DelegatesTo("_label", "rotate_angle")
 
29
 
 
30
    bgcolor = LabelDelegate
 
31
    border_width = LabelDelegate
 
32
    border_color = LabelDelegate
 
33
    border_visible = LabelDelegate
 
34
    margin = LabelDelegate
 
35
    line_spacing = LabelDelegate
 
36
 
 
37
    #------------------------------------------------------------------------
 
38
    # Layout-related traits
 
39
    #------------------------------------------------------------------------
 
40
    
 
41
    # Horizontal justification used if the label has more horizontal space
 
42
    # than it needs.
 
43
    hjustify = Enum("center", "left", "right")
 
44
    
 
45
    # Vertical justification used if the label has more vertical space than it
 
46
    # needs.
 
47
    vjustify = Enum("center", "bottom", "top")
 
48
    
 
49
    # The position of this label relative to the object it is overlaying.
 
50
    # Can be "top", "left", "right", "bottom", and optionally can be preceeded
 
51
    # by the words "inside" or "outside", separated by a space.  If "inside"
 
52
    # and "outside" are not provided, then defaults to "outside".
 
53
    # Examples:
 
54
    #     inside top
 
55
    #     outside right
 
56
    overlay_position = Trait("outside top", Str, None)
 
57
    
 
58
    # Should this PlotLabel modify the padding on its underlying component
 
59
    # if there is not enough room to lay out the text?
 
60
    # FIXME: This could cause cycles in layout, so not implemented for now
 
61
    #modify_component = Bool(True)
 
62
 
 
63
    # By default, this acts like a component and will render on the main
 
64
    # "plot" layer unless its **component** attribute gets set.
 
65
    draw_layer = "plot"
 
66
 
 
67
    #------------------------------------------------------------------------
 
68
    # Private traits
 
69
    #------------------------------------------------------------------------
 
70
    
 
71
    # The label has a fixed height and can be resized horizontally. (Overrides
 
72
    # PlotComponent.)
 
73
    resizable = "h"
 
74
 
 
75
    # The Label instance this plot label is wrapping.
 
76
    _label = Instance(Label, args=())
 
77
 
 
78
    
 
79
    def __init__(self, text="", *args, **kw):
 
80
        super(PlotLabel, self).__init__(*args, **kw)
 
81
        self.text = text
 
82
        return
 
83
    
 
84
    def overlay(self, component, gc, view_bounds=None, mode="normal"):
 
85
        """ Draws this label overlaid on another component.
 
86
        
 
87
        Overrides AbstractOverlay.
 
88
        """
 
89
        self._draw_overlay(gc, view_bounds, mode)
 
90
        return
 
91
 
 
92
    def get_preferred_size(self):
 
93
        """ Returns the label's preferred size.
 
94
        
 
95
        Overrides PlotComponent.
 
96
        """
 
97
        dummy_gc = font_metrics_provider()
 
98
        size = self._label.get_bounding_box(dummy_gc)
 
99
        return size
 
100
    
 
101
    def do_layout(self):
 
102
        """ Tells this component to do layout.
 
103
        
 
104
        Overrides PlotComponent.
 
105
        """
 
106
        if self.component is not None:
 
107
            self._layout_as_overlay()
 
108
        else:
 
109
            self._layout_as_component()
 
110
        return
 
111
    
 
112
    def _draw_overlay(self, gc, view_bounds=None, mode="normal"):
 
113
        """ Draws the overlay layer of a component.
 
114
        
 
115
        Overrides PlotComponent.
 
116
        """
 
117
        # Perform justification and compute the correct offsets for
 
118
        # the label position
 
119
        width, height = self._label.get_bounding_box(gc)
 
120
        if self.hjustify == "left":
 
121
            x_offset = 0
 
122
        elif self.hjustify == "right":
 
123
            x_offset = self.width - width
 
124
        elif self.hjustify == "center":
 
125
            x_offset = int((self.width - width) / 2)
 
126
        
 
127
        if self.vjustify == "bottom":
 
128
            y_offset = 0
 
129
        elif self.vjustify == "top":
 
130
            y_offset = self.height - height
 
131
        elif self.vjustify == "center":
 
132
            y_offset = int((self.height - height) / 2)
 
133
 
 
134
        with gc:
 
135
            # XXX: Uncomment this after we fix kiva GL backend's clip stack
 
136
            #gc.clip_to_rect(self.x, self.y, self.width, self.height)
 
137
 
 
138
            # We have to translate to our position because the label
 
139
            # tries to draw at (0,0).
 
140
            gc.translate_ctm(self.x + x_offset, self.y + y_offset)
 
141
            self._label.draw(gc)
 
142
 
 
143
        return
 
144
    
 
145
    def _draw_plot(self, gc, view_bounds=None, mode="normal"):
 
146
        if self.component is None:
 
147
            # We are not overlaying anything else, so we should render
 
148
            # on this layer
 
149
            self._draw_overlay(gc, view_bounds, mode)
 
150
 
 
151
    def _layout_as_component(self, size=None, force=False):
 
152
        pass
 
153
    
 
154
    def _layout_as_overlay(self, size=None, force=False):
 
155
        """ Lays out the label as an overlay on another component.
 
156
        """
 
157
        if self.component is not None:
 
158
            orientation = self.overlay_position
 
159
            outside = True
 
160
            if "inside" in orientation:
 
161
                tmp = orientation.split()
 
162
                tmp.remove("inside")
 
163
                orientation = tmp[0]
 
164
                outside = False
 
165
            elif "outside" in orientation:
 
166
                tmp = orientation.split()
 
167
                tmp.remove("outside")
 
168
                orientation = tmp[0]
 
169
 
 
170
            if orientation in ("left", "right"):
 
171
                self.y = self.component.y
 
172
                self.height = self.component.height
 
173
                if not outside:
 
174
                    gc = font_metrics_provider()
 
175
                    self.width = self._label.get_bounding_box(gc)[0]
 
176
                if orientation == "left":
 
177
                    if outside:
 
178
                        self.x = self.component.outer_x
 
179
                        self.width = self.component.padding_left
 
180
                    else:
 
181
                        self.outer_x = self.component.x
 
182
                elif orientation == "right":
 
183
                    if outside:
 
184
                        self.x = self.component.x2 + 1
 
185
                        self.width = self.component.padding_right
 
186
                    else:
 
187
                        self.x = self.component.x2 - self.outer_width
 
188
            elif orientation in ("bottom", "top"):
 
189
                self.x = self.component.x
 
190
                self.width = self.component.width
 
191
                if not outside:
 
192
                    gc = font_metrics_provider()
 
193
                    self.height = self._label.get_bounding_box(gc)[1]
 
194
                if orientation == "bottom":
 
195
                    if outside:
 
196
                        self.y = self.component.outer_y
 
197
                        self.height = self.component.padding_bottom
 
198
                    else:
 
199
                        self.outer_y = self.component.y
 
200
                elif orientation == "top":
 
201
                    if outside:
 
202
                        self.y = self.component.y2 + 1
 
203
                        self.height = self.component.padding_top
 
204
                    else:
 
205
                        self.y = self.component.y2 - self.outer_height
 
206
            else:
 
207
                # Leave the position alone
 
208
                pass
 
209
        return
 
210
 
 
211
    def _text_changed(self, old, new):
 
212
        self._label.text = new
 
213
        self.do_layout()
 
214
        return
 
215
 
 
216
    def _font_changed(self, old, new):
 
217
        self._label.font = new
 
218
        self.do_layout()
 
219
        return
 
220
 
 
221
    def _angle_changed(self, old, new):
 
222
        self._label.rotate_angle = new
 
223
        self.do_layout()
 
224
        return
 
225
 
 
226
    def _overlay_position_changed(self):
 
227
        self.do_layout()
 
228
 
 
229
    def _component_changed(self, old, new):
 
230
        if new:
 
231
            self.draw_layer = "overlay"
 
232
        else:
 
233
            self.draw_layer = "plot"
 
234
        return
 
235
 
 
236
 
 
237
 
 
238
# EOF