1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
|
from math import pi as PI
from lvc.widgets import widgetset
from lvc.resources import image_path
def make_surface(image_name, height=None):
path = image_path(image_name + '.png')
image = widgetset.Image(path)
if height is not None:
image = image.resize(image.width, height)
return widgetset.ImageSurface(image)
def font_scale_from_osx_points(points):
"""Create a font scale so that it's points large on OS X.
Assumptions (these should be true for OS X)
- the default font size is 13pt
- the DPI is 72ppi
"""
return points / 13.0
def css_to_color(css_string):
parts = (css_string[1:3], css_string[3:5], css_string[5:7])
return tuple((int(value, 16) / 255.0) for value in parts)
def align(widget, xalign=0, yalign=0, xscale=0, yscale=0,
top_pad=0, bottom_pad=0, left_pad=0, right_pad=0):
"""Create an alignment, then add widget to it and return the alignment.
"""
alignment = widgetset.Alignment(xalign, yalign, xscale, yscale,
top_pad, bottom_pad, left_pad, right_pad)
alignment.add(widget)
return alignment
def align_center(widget, top_pad=0, bottom_pad=0, left_pad=0, right_pad=0):
"""Wrap a widget in an Alignment that will center it horizontally.
"""
return align(widget, 0.5, 0, 0, 1,
top_pad, bottom_pad, left_pad, right_pad)
def align_right(widget, top_pad=0, bottom_pad=0, left_pad=0, right_pad=0):
"""Wrap a widget in an Alignment that will align it left.
"""
return align(widget, 1, 0, 0, 1, top_pad, bottom_pad, left_pad, right_pad)
def align_left(widget, top_pad=0, bottom_pad=0, left_pad=0, right_pad=0):
"""Wrap a widget in an Alignment that will align it right.
"""
return align(widget, 0, 0, 0, 1, top_pad, bottom_pad, left_pad, right_pad)
def align_middle(widget, top_pad=0, bottom_pad=0, left_pad=0, right_pad=0):
"""Wrap a widget in an Alignment that will center it vertically.
"""
return align(widget, 0, 0.5, 1, 0,
top_pad, bottom_pad, left_pad, right_pad)
def align_top(widget, top_pad=0, bottom_pad=0, left_pad=0, right_pad=0):
"""Wrap a widget in an Alignment that will align to the top.
"""
return align(widget, 0, 0, 1, 0, top_pad, bottom_pad, left_pad, right_pad)
def align_bottom(widget, top_pad=0, bottom_pad=0, left_pad=0, right_pad=0):
"""Wrap a widget in an Alignment that will align to the bottom.
"""
return align(widget, 0, 1, 1, 0, top_pad, bottom_pad, left_pad, right_pad)
def pad(widget, top=0, bottom=0, left=0, right=0):
"""Wrap a widget in an Alignment that will pad it.
"""
alignment = widgetset.Alignment(0, 0, 1, 1,
top, bottom, left, right)
alignment.add(widget)
return alignment
def round_rect(context, x, y, width, height, edge_radius):
"""Specifies path of a rectangle with rounded corners.
"""
edge_radius = min(edge_radius, min(width, height)/2.0)
inner_width = width - edge_radius*2
inner_height = height - edge_radius*2
x_inner1 = x + edge_radius
x_inner2 = x + width - edge_radius
y_inner1 = y + edge_radius
y_inner2 = y + height - edge_radius
context.move_to(x+edge_radius, y)
context.rel_line_to(inner_width, 0)
context.arc(x_inner2, y_inner1, edge_radius, -PI/2, 0)
context.rel_line_to(0, inner_height)
context.arc(x_inner2, y_inner2, edge_radius, 0, PI/2)
context.rel_line_to(-inner_width, 0)
context.arc(x_inner1, y_inner2, edge_radius, PI/2, PI)
context.rel_line_to(0, -inner_height)
context.arc(x_inner1, y_inner1, edge_radius, PI, PI*3/2)
def round_rect_reverse(context, x, y, width, height, edge_radius):
"""Specifies path of a rectangle with rounded corners.
This specifies the rectangle in a counter-clockwise fashion.
"""
edge_radius = min(edge_radius, min(width, height)/2.0)
inner_width = width - edge_radius*2
inner_height = height - edge_radius*2
x_inner1 = x + edge_radius
x_inner2 = x + width - edge_radius
y_inner1 = y + edge_radius
y_inner2 = y + height - edge_radius
context.move_to(x+edge_radius, y)
context.arc_negative(x_inner1, y_inner1, edge_radius, PI*3/2, PI)
context.rel_line_to(0, inner_height)
context.arc_negative(x_inner1, y_inner2, edge_radius, PI, PI/2)
context.rel_line_to(inner_width, 0)
context.arc_negative(x_inner2, y_inner2, edge_radius, PI/2, 0)
context.rel_line_to(0, -inner_height)
context.arc_negative(x_inner2, y_inner1, edge_radius, 0, -PI/2)
context.rel_line_to(-inner_width, 0)
def circular_rect(context, x, y, width, height):
"""Make a path for a rectangle with the left/right side being circles.
"""
radius = height / 2.0
inner_width = width - height
inner_y = y + radius
inner_x1 = x + radius
inner_x2 = inner_x1 + inner_width
context.move_to(inner_x1, y)
context.rel_line_to(inner_width, 0)
context.arc(inner_x2, inner_y, radius, -PI/2, PI/2)
context.rel_line_to(-inner_width, 0)
context.arc(inner_x1, inner_y, radius, PI/2, -PI/2)
def circular_rect_negative(context, x, y, width, height):
"""The same path as ``circular_rect()``, but going counter clockwise.
"""
radius = height / 2.0
inner_width = width - height
inner_y = y + radius
inner_x1 = x + radius
inner_x2 = inner_x1 + inner_width
context.move_to(inner_x1, y)
context.arc_negative(inner_x1, inner_y, radius, -PI/2, PI/2)
context.rel_line_to(inner_width, 0)
context.arc_negative(inner_x2, inner_y, radius, PI/2, -PI/2)
context.rel_line_to(-inner_width, 0)
class Shadow(object):
"""Encapsulates all parameters required to draw shadows.
"""
def __init__(self, color, opacity, offset, blur_radius):
self.color = color
self.opacity = opacity
self.offset = offset
self.blur_radius = blur_radius
class ThreeImageSurface(object):
"""Takes a left, center and right image and draws them to an arbitrary
width. If the width is greater than the combined width of the 3 images,
then the center image will be tiled to compensate.
Example:
>>> timelinebar = ThreeImageSurface("timelinebar")
This creates a ``ThreeImageSurface`` using the images
``images/timelinebar_left.png``, ``images/timelinebar_center.png``, and
``images/timelinebar_right.png``.
Example:
>>> timelinebar = ThreeImageSurface()
>>> img_left = make_surface("timelinebar_left")
>>> img_center = make_surface("timelinebar_center")
>>> img_right = make_surface("timelinebar_right")
>>> timelinebar.set_images(img_left, img_center, img_right)
This does the same thing, but allows you to explicitly set which images
get used.
"""
def __init__(self, basename=None, height=None):
self.left = self.center = self.right = None
self.height = 0
self.width = None
if basename is not None:
left = make_surface(basename + '_left', height)
center = make_surface(basename + '_center', height)
right = make_surface(basename + '_right', height)
self.set_images(left, center, right)
def set_images(self, left, center, right):
"""Sets the left, center and right images to use.
"""
self.left = left
self.center = center
self.right = right
if not (self.left.height == self.center.height == self.right.height):
raise ValueError("Images aren't the same height")
self.height = self.left.height
def set_width(self, width):
"""Manually set a width.
When ThreeImageSurface have a width, then they have pretty much the
same API as ImageSurface does. In particular, they can now be nested
in another ThreeImageSurface.
"""
self.width = width
def get_size(self):
return self.width, self.height
def draw(self, context, x, y, width, fraction=1.0):
left_width = min(self.left.width, width)
self.left.draw(context, x, y, left_width, self.height, fraction)
self.draw_right(context, x + left_width,
y, width - left_width, fraction)
def draw_right(self, context, x, y, width, fraction=1.0):
# draws only the right two images
right_width = min(self.right.width, width)
center_width = int(width - right_width)
self.center.draw(context, x, y, center_width, self.height, fraction)
self.right.draw(context, x + center_width,
y, right_width, self.height, fraction)
|