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

« back to all changes in this revision

Viewing changes to chaco/label.py

  • Committer: Package Import Robot
  • Author(s): Andrew Starr-Bochicchio
  • Date: 2014-06-01 17:04:08 UTC
  • mfrom: (7.2.5 sid)
  • Revision ID: package-import@ubuntu.com-20140601170408-m86xvdjd83a4qon0
Tags: 4.4.1-1ubuntu1
* Merge from Debian unstable. Remaining Ubuntu changes:
 - Let the binary-predeb target work on the usr/lib/python* directory
   as we don't have usr/share/pyshared anymore.

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
from enable.api import black_color_trait, transparent_color_trait
12
12
from kiva.constants import FILL
13
13
from kiva.trait_defs.kiva_font_trait import KivaFont
14
 
from traits.api import Any, Bool, Enum, Float, HasTraits, Int, \
15
 
                                 List, Str, on_trait_change
 
14
from traits.api import (Any, Bool, Float, HasTraits, Int, List, Str,
 
15
                        on_trait_change)
16
16
 
17
17
 
18
18
class Label(HasTraits):
63
63
    # Number of pixels of spacing between lines of text.
64
64
    line_spacing = Int(5)
65
65
 
 
66
    # Number of pixels to limit the width of the label to. Lines which are
 
67
    # too long will be broken to fit on word boundaries. Line width is
 
68
    # calculated without considering the value of `margin`.
 
69
    # A `max_width` of 0.0 means that lines will not be broken.
 
70
    max_width = Float(0.0)
66
71
 
67
72
    #------------------------------------------------------------------------
68
73
    # Private traits
70
75
 
71
76
    _bounding_box = List()
72
77
    _position_cache_valid = Bool(False)
 
78
    _text_needs_fitting = Bool(False)
73
79
    _line_xpos = Any()
74
80
    _line_ypos = Any()
75
81
    _rot_matrix = Any()
76
82
 
77
83
    def __init__(self, **traits):
78
 
        HasTraits.__init__(self, **traits)
79
 
        self._bounding_box = [0,0]
80
 
        return
81
 
 
82
 
    def _calc_line_positions(self, gc):
83
 
        if not self._position_cache_valid:
84
 
            with gc:
85
 
                gc.set_font(self.font)
86
 
                # The bottommost line starts at postion (0,0).
87
 
                x_pos = []
88
 
                y_pos = []
89
 
                self._bounding_box = [0,0]
90
 
                margin = self.margin
91
 
                prev_y_pos = margin
92
 
                prev_y_height = -self.line_spacing
93
 
                max_width = 0
94
 
                for line in self.text.split("\n")[::-1]:
95
 
                    if line != "":
96
 
                        (width, height, descent, leading) = gc.get_full_text_extent(line)
97
 
                        ascent = height - abs(descent)
98
 
                        if width > max_width:
99
 
                            max_width = width
100
 
                        new_y_pos = prev_y_pos + prev_y_height + self.line_spacing
101
 
                    else:
102
 
                        # For blank lines, we use the height of the previous line, if there
103
 
                        # is one.  The width is 0.
104
 
                        leading = 0
105
 
                        if prev_y_height != -self.line_spacing:
106
 
                            new_y_pos = prev_y_pos + prev_y_height + self.line_spacing
107
 
                            ascent = prev_y_height
108
 
                        else:
109
 
                            new_y_pos = prev_y_pos
110
 
                            ascent = 0
111
 
                    x_pos.append(-leading + margin)
112
 
                    y_pos.append(new_y_pos)
113
 
                    prev_y_pos = new_y_pos
114
 
                    prev_y_height = ascent
115
 
 
116
 
            self._line_xpos = x_pos[::-1]
117
 
            self._line_ypos = y_pos[::-1]
118
 
            border_width = self.border_width if self.border_visible else 0
119
 
            self._bounding_box[0] = max_width + 2*margin + 2*border_width
120
 
            self._bounding_box[1] = prev_y_pos + prev_y_height + margin + 2*border_width
121
 
            self._position_cache_valid = True
 
84
        super(Label, self).__init__(**traits)
 
85
        self._bounding_box = [0, 0]
122
86
        return
123
87
 
124
88
    def get_width_height(self, gc):
125
89
        """ Returns the width and height of the label, in the rotated frame of
126
90
        reference.
127
91
        """
 
92
        self._fit_text_to_max_width(gc)
128
93
        self._calc_line_positions(gc)
129
94
        width, height = self._bounding_box
130
95
        return width, height
143
108
                    abs(height*sin(angle))+abs(width*cos(angle)))
144
109
 
145
110
    def get_bounding_poly(self, gc):
146
 
        """
147
 
        Returns a list [(x0,y0), (x1,y1),...] of tuples representing a polygon
148
 
        that bounds the label.
 
111
        """ Returns a list [(x0,y0), (x1,y1),...] of tuples representing a
 
112
        polygon that bounds the label.
149
113
        """
150
114
        width, height = self.get_width_height(gc)
151
115
        offset = array(self.get_bounding_box(gc))/2.
159
123
        ]
160
124
        # rotate about centre, and offset to bounding box coords
161
125
        points = [dot(self.get_rotation_matrix(), point).transpose()[0]+offset
162
 
            for point in base_points]
 
126
                  for point in base_points]
163
127
        return points
164
128
 
165
129
    def get_rotation_matrix(self):
166
130
        return array([[cos(self.rotate_angle), -sin(self.rotate_angle)],
167
 
            [sin(self.rotate_angle), cos(self.rotate_angle)]])
 
131
                     [sin(self.rotate_angle), cos(self.rotate_angle)]])
168
132
 
169
133
    def draw(self, gc):
170
134
        """ Draws the label.
173
137
        correct position such that the origin is at the lower left-hand corner
174
138
        of this text label's box.
175
139
        """
 
140
        # Make sure `max_width` is respected
 
141
        self._fit_text_to_max_width(gc)
 
142
 
176
143
        # For this version we're not supporting rotated text.
177
144
        self._calc_line_positions(gc)
178
145
 
193
160
                gc.set_stroke_color(self.border_color_)
194
161
                gc.set_line_width(self.border_width)
195
162
                border_offset = (self.border_width-1)/2.0
196
 
                gc.rect(border_offset, border_offset, width-2*border_offset, height-2*border_offset)
 
163
                gc.rect(border_offset, border_offset,
 
164
                        width-2*border_offset, height-2*border_offset)
197
165
                gc.stroke_path()
198
166
 
199
167
            gc.set_fill_color(self.color_)
200
168
            gc.set_stroke_color(self.color_)
201
169
            gc.set_font(self.font)
202
 
            if self.font.size<=8.0:
 
170
            if self.font.size <= 8.0:
203
171
                gc.set_antialias(0)
204
172
            else:
205
173
                gc.set_antialias(1)
217
185
                gc.set_text_position(x_offset, y_offset)
218
186
                gc.show_text(line)
219
187
 
220
 
        return
 
188
    #------------------------------------------------------------------------
 
189
    # Trait handlers
 
190
    #------------------------------------------------------------------------
 
191
 
 
192
    def _text_changed(self):
 
193
        self._text_needs_fitting = (self.max_width > 0.0)
221
194
 
222
195
    @on_trait_change("font,margin,text,rotate_angle")
223
196
    def _invalidate_position_cache(self):
224
197
        self._position_cache_valid = False
225
198
 
 
199
    #------------------------------------------------------------------------
 
200
    # Private methods
 
201
    #------------------------------------------------------------------------
 
202
 
 
203
    def _fit_text_to_max_width(self, gc):
 
204
        """ Break the text into lines whose width is no greater than
 
205
        `max_width`.
 
206
        """
 
207
        if self._text_needs_fitting:
 
208
            lines = []
 
209
 
 
210
            with gc:
 
211
                gc.set_font(self.font)
 
212
                for line in self.text.split('\n'):
 
213
                    if line == "":
 
214
                        lines.append(line)
 
215
                        continue
 
216
 
 
217
                    width = gc.get_full_text_extent(line)[0]
 
218
                    if width > self.max_width:
 
219
                        line_words = []
 
220
                        for word in line.split():
 
221
                            line_words.append(word)
 
222
                            test_line = ' '.join(line_words)
 
223
                            width = gc.get_full_text_extent(test_line)[0]
 
224
                            if width > self.max_width:
 
225
                                if len(line_words) > 1:
 
226
                                    lines.append(' '.join(line_words[:-1]))
 
227
                                    line_words = [word]
 
228
                                else:
 
229
                                    lines.append(word)
 
230
                                    line_words = []
 
231
                        if len(line_words) > 0:
 
232
                            lines.append(' '.join(line_words))
 
233
                    else:
 
234
                        lines.append(line)
 
235
            self.trait_setq(text='\n'.join(lines))
 
236
            self._text_needs_fitting = False
 
237
 
 
238
    def _calc_line_positions(self, gc):
 
239
        if not self._position_cache_valid:
 
240
            with gc:
 
241
                gc.set_font(self.font)
 
242
                # The bottommost line starts at postion (0, 0).
 
243
                x_pos = []
 
244
                y_pos = []
 
245
                self._bounding_box = [0, 0]
 
246
                margin = self.margin
 
247
                prev_y_pos = margin
 
248
                prev_y_height = -self.line_spacing
 
249
                max_width = 0
 
250
                for line in self.text.split("\n")[::-1]:
 
251
                    if line != "":
 
252
                        (width, height, descent, leading) = \
 
253
                            gc.get_full_text_extent(line)
 
254
                        ascent = height - abs(descent)
 
255
                        if width > max_width:
 
256
                            max_width = width
 
257
                        new_y_pos = prev_y_pos + prev_y_height \
 
258
                            + self.line_spacing
 
259
                    else:
 
260
                        # For blank lines, we use the height of the previous
 
261
                        # line, if there is one.  The width is 0.
 
262
                        leading = 0
 
263
                        if prev_y_height != -self.line_spacing:
 
264
                            new_y_pos = prev_y_pos + prev_y_height \
 
265
                                + self.line_spacing
 
266
                            ascent = prev_y_height
 
267
                        else:
 
268
                            new_y_pos = prev_y_pos
 
269
                            ascent = 0
 
270
                    x_pos.append(-leading + margin)
 
271
                    y_pos.append(new_y_pos)
 
272
                    prev_y_pos = new_y_pos
 
273
                    prev_y_height = ascent
 
274
 
 
275
            self._line_xpos = x_pos[::-1]
 
276
            self._line_ypos = y_pos[::-1]
 
277
            border_width = self.border_width if self.border_visible else 0
 
278
            self._bounding_box[0] = max_width + 2*margin + 2*border_width
 
279
            self._bounding_box[1] = prev_y_pos + prev_y_height + margin \
 
280
                + 2*border_width
 
281
            self._position_cache_valid = True
 
282
        return