1
""" Defines the PlotLabel class.
3
from enthought.kiva import font_metrics_provider
4
from enthought.traits.api import Delegate, Enum, Instance, Str, Trait
6
from abstract_overlay import AbstractOverlay
7
from label import Label
10
class PlotLabel(AbstractOverlay):
11
""" A label used by plots.
13
This class wraps a simple Label instance, and delegates some traits to it.
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")
25
#------------------------------------------------------------------------
26
# Layout-related traits
27
#------------------------------------------------------------------------
29
# Horizontal justification used if the label has more horizontal space
31
hjustify = Enum("center", "left", "right")
33
# Vertical justification used if the label has more vertical space than it
35
vjustify = Enum("center", "bottom", "top")
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".
44
overlay_position = Trait("outside top", Str, None)
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)
51
# By default, this acts like a component and will render on the main
52
# "plot" layer unless its **component** attribute gets set.
55
#------------------------------------------------------------------------
57
#------------------------------------------------------------------------
59
# The label has a fixed height and can be resized horizontally. (Overrides
63
# The Label instance this plot label is wrapping.
64
_label = Instance(Label, args=())
67
def __init__(self, text="", *args, **kw):
68
super(PlotLabel, self).__init__(*args, **kw)
72
def overlay(self, component, gc, view_bounds=None, mode="normal"):
73
""" Draws this label overlaid on another component.
75
Overrides AbstractOverlay.
77
self._draw_overlay(gc, view_bounds, mode)
80
def get_preferred_size(self):
81
""" Returns the label's preferred size.
83
Overrides PlotComponent.
85
dummy_gc = font_metrics_provider()
86
size = self._label.get_bounding_box(dummy_gc)
90
""" Tells this component to do layout.
92
Overrides PlotComponent.
94
if self.component is not None:
95
self._layout_as_overlay()
97
self._layout_as_component()
100
def _draw_overlay(self, gc, view_bounds=None, mode="normal"):
101
""" Draws the overlay layer of a component.
103
Overrides PlotComponent.
106
# Perform justification and compute the correct offsets for
108
width, height = self._label.get_bounding_box(gc)
109
if self.hjustify == "left":
111
elif self.hjustify == "right":
112
x_offset = self.width - width
113
elif self.hjustify == "center":
114
x_offset = int((self.width - width) / 2)
116
if self.vjustify == "bottom":
118
elif self.vjustify == "top":
119
y_offset = self.height - height
120
elif self.vjustify == "center":
121
y_offset = int((self.height - height) / 2)
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)
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)
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
140
self._draw_overlay(gc, view_bounds, mode)
142
def _layout_as_component(self, size=None, force=False):
145
def _layout_as_overlay(self, size=None, force=False):
146
""" Lays out the label as an overlay on another component.
148
if self.component is not None:
149
orientation = self.overlay_position
151
if "inside" in orientation:
152
tmp = orientation.split()
156
elif "outside" in orientation:
157
tmp = orientation.split()
158
tmp.remove("outside")
161
if orientation in ("left", "right"):
162
self.y = self.component.y
163
self.height = self.component.height
165
gc = font_metrics_provider()
166
self.width = self._label.get_bounding_box(gc)[0]
167
if orientation == "left":
169
self.x = self.component.outer_x
170
self.width = self.component.padding_left
172
self.outer_x = self.component.x
173
elif orientation == "right":
175
self.x = self.component.x2 + 1
176
self.width = self.component.padding_right
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
183
gc = font_metrics_provider()
184
self.height = self._label.get_bounding_box(gc)[1]
185
if orientation == "bottom":
187
self.y = self.component.outer_y
188
self.height = self.component.padding_bottom
190
self.outer_y = self.component.y
191
elif orientation == "top":
193
self.y = self.component.y2 + 1
194
self.height = self.component.padding_top
196
self.y = self.component.y2 - self.outer_height
198
# Leave the position alone
202
def _text_changed(self, old, new):
203
self._label.text = new
207
def _font_changed(self, old, new):
208
self._label.font = new
212
def _angle_changed(self, old, new):
213
self._label.rotate_angle = new
217
def _overlay_position_changed(self):
220
def _component_changed(self, old, new):
222
self.draw_layer = "overlay"
224
self.draw_layer = "plot"
1
""" Defines the PlotLabel class.
4
from __future__ import with_statement
6
from enthought.enable.font_metrics_provider import font_metrics_provider
7
from enthought.traits.api import DelegatesTo, Enum, Instance, Str, Trait
9
from abstract_overlay import AbstractOverlay
10
from label import Label
13
LabelDelegate = DelegatesTo("_label")
15
class PlotLabel(AbstractOverlay):
16
""" A label used by plots.
18
This class wraps a simple Label instance, and delegates some traits to it.
21
# The text of the label.
23
# The color of the label text.
24
color = DelegatesTo("_label")
25
# The font for the label text.
27
# The angle of rotation of the label.
28
angle = DelegatesTo("_label", "rotate_angle")
30
bgcolor = LabelDelegate
31
border_width = LabelDelegate
32
border_color = LabelDelegate
33
border_visible = LabelDelegate
34
margin = LabelDelegate
35
line_spacing = LabelDelegate
37
#------------------------------------------------------------------------
38
# Layout-related traits
39
#------------------------------------------------------------------------
41
# Horizontal justification used if the label has more horizontal space
43
hjustify = Enum("center", "left", "right")
45
# Vertical justification used if the label has more vertical space than it
47
vjustify = Enum("center", "bottom", "top")
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".
56
overlay_position = Trait("outside top", Str, None)
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)
63
# By default, this acts like a component and will render on the main
64
# "plot" layer unless its **component** attribute gets set.
67
#------------------------------------------------------------------------
69
#------------------------------------------------------------------------
71
# The label has a fixed height and can be resized horizontally. (Overrides
75
# The Label instance this plot label is wrapping.
76
_label = Instance(Label, args=())
79
def __init__(self, text="", *args, **kw):
80
super(PlotLabel, self).__init__(*args, **kw)
84
def overlay(self, component, gc, view_bounds=None, mode="normal"):
85
""" Draws this label overlaid on another component.
87
Overrides AbstractOverlay.
89
self._draw_overlay(gc, view_bounds, mode)
92
def get_preferred_size(self):
93
""" Returns the label's preferred size.
95
Overrides PlotComponent.
97
dummy_gc = font_metrics_provider()
98
size = self._label.get_bounding_box(dummy_gc)
102
""" Tells this component to do layout.
104
Overrides PlotComponent.
106
if self.component is not None:
107
self._layout_as_overlay()
109
self._layout_as_component()
112
def _draw_overlay(self, gc, view_bounds=None, mode="normal"):
113
""" Draws the overlay layer of a component.
115
Overrides PlotComponent.
117
# Perform justification and compute the correct offsets for
119
width, height = self._label.get_bounding_box(gc)
120
if self.hjustify == "left":
122
elif self.hjustify == "right":
123
x_offset = self.width - width
124
elif self.hjustify == "center":
125
x_offset = int((self.width - width) / 2)
127
if self.vjustify == "bottom":
129
elif self.vjustify == "top":
130
y_offset = self.height - height
131
elif self.vjustify == "center":
132
y_offset = int((self.height - height) / 2)
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)
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)
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
149
self._draw_overlay(gc, view_bounds, mode)
151
def _layout_as_component(self, size=None, force=False):
154
def _layout_as_overlay(self, size=None, force=False):
155
""" Lays out the label as an overlay on another component.
157
if self.component is not None:
158
orientation = self.overlay_position
160
if "inside" in orientation:
161
tmp = orientation.split()
165
elif "outside" in orientation:
166
tmp = orientation.split()
167
tmp.remove("outside")
170
if orientation in ("left", "right"):
171
self.y = self.component.y
172
self.height = self.component.height
174
gc = font_metrics_provider()
175
self.width = self._label.get_bounding_box(gc)[0]
176
if orientation == "left":
178
self.x = self.component.outer_x
179
self.width = self.component.padding_left
181
self.outer_x = self.component.x
182
elif orientation == "right":
184
self.x = self.component.x2 + 1
185
self.width = self.component.padding_right
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
192
gc = font_metrics_provider()
193
self.height = self._label.get_bounding_box(gc)[1]
194
if orientation == "bottom":
196
self.y = self.component.outer_y
197
self.height = self.component.padding_bottom
199
self.outer_y = self.component.y
200
elif orientation == "top":
202
self.y = self.component.y2 + 1
203
self.height = self.component.padding_top
205
self.y = self.component.y2 - self.outer_height
207
# Leave the position alone
211
def _text_changed(self, old, new):
212
self._label.text = new
216
def _font_changed(self, old, new):
217
self._label.font = new
221
def _angle_changed(self, old, new):
222
self._label.rotate_angle = new
226
def _overlay_position_changed(self):
229
def _component_changed(self, old, new):
231
self.draw_layer = "overlay"
233
self.draw_layer = "plot"