summaryrefslogtreecommitdiffstats
path: root/unit_trace/viz/canvas.py
diff options
context:
space:
mode:
authorGary Bressler <garybressler@nc.rr.com>2010-04-08 17:11:08 -0400
committerGary Bressler <garybressler@nc.rr.com>2010-04-08 17:11:08 -0400
commitceff6457bfeb5642616f4711f14e0bb652d12164 (patch)
treed05b4ebd1c3ee6e28884c669d65fd31700941086 /unit_trace/viz/canvas.py
parent01abc8352aa2fd192678b4066b26ea749a203801 (diff)
Updated the documentation to describe the visualizer, made unit-trace itself not require gtk/cairo, and a few other minor things.
Diffstat (limited to 'unit_trace/viz/canvas.py')
-rw-r--r--unit_trace/viz/canvas.py340
1 files changed, 170 insertions, 170 deletions
diff --git a/unit_trace/viz/canvas.py b/unit_trace/viz/canvas.py
index 758dea3..ea73d98 100644
--- a/unit_trace/viz/canvas.py
+++ b/unit_trace/viz/canvas.py
@@ -33,19 +33,19 @@ class Surface(object):
33 self.scale = 1.0 33 self.scale = 1.0
34 self.fname = fname 34 self.fname = fname
35 self.ctx = ctx 35 self.ctx = ctx
36 36
37 def renew(self, width, height): 37 def renew(self, width, height):
38 raise NotImplementedError 38 raise NotImplementedError
39 39
40 def change_ctx(self, ctx): 40 def change_ctx(self, ctx):
41 self.ctx = ctx 41 self.ctx = ctx
42 42
43 def get_fname(self): 43 def get_fname(self):
44 return self.fname 44 return self.fname
45 45
46 def write_out(self, fname): 46 def write_out(self, fname):
47 raise NotImplementedError 47 raise NotImplementedError
48 48
49 def pan(self, x, y, width, height): 49 def pan(self, x, y, width, height):
50 """A surface actually represents just a ``window'' into 50 """A surface actually represents just a ``window'' into
51 what we are drawing on. For instance, if we are scrolling through 51 what we are drawing on. For instance, if we are scrolling through
@@ -57,11 +57,11 @@ class Surface(object):
57 self.virt_y = y 57 self.virt_y = y
58 self.width = width 58 self.width = width
59 self.height = height 59 self.height = height
60 60
61 def set_scale(self, scale): 61 def set_scale(self, scale):
62 """Sets the scale factor.""" 62 """Sets the scale factor."""
63 self.scale = scale 63 self.scale = scale
64 64
65 def get_real_coor(self, x, y): 65 def get_real_coor(self, x, y):
66 """Translates the coordinates (x, y) 66 """Translates the coordinates (x, y)
67 in the ``theoretical'' plane to the true (x, y) coordinates on this surface 67 in the ``theoretical'' plane to the true (x, y) coordinates on this surface
@@ -69,57 +69,57 @@ class Surface(object):
69 bounds of the surface, 69 bounds of the surface,
70 if we want something outside the surface's ``window''.""" 70 if we want something outside the surface's ``window''."""
71 return (x - self.virt_x * self.scale, y - self.virt_y * self.scale) 71 return (x - self.virt_x * self.scale, y - self.virt_y * self.scale)
72 72
73 def get_virt_coor(self, x, y): 73 def get_virt_coor(self, x, y):
74 """Does the inverse of the last method.""" 74 """Does the inverse of the last method."""
75 return (x + self.virt_x * self.scale, y + self.virt_y * self.scale) 75 return (x + self.virt_x * self.scale, y + self.virt_y * self.scale)
76 76
77 def get_virt_coor_unscaled(self, x, y): 77 def get_virt_coor_unscaled(self, x, y):
78 """Does the same, but removes the scale factor (i.e. behaves as if 78 """Does the same, but removes the scale factor (i.e. behaves as if
79 the scale was 1.0 all along).""" 79 the scale was 1.0 all along)."""
80 return (x / self.scale + self.virt_x, y / self.scale + self.virt_y) 80 return (x / self.scale + self.virt_x, y / self.scale + self.virt_y)
81 81
82class SVGSurface(Surface): 82class SVGSurface(Surface):
83 def renew(self, width, height): 83 def renew(self, width, height):
84 iwidth = int(math.ceil(width)) 84 iwidth = int(math.ceil(width))
85 iheight = int(math.ceil(height)) 85 iheight = int(math.ceil(height))
86 self.surface = cairo.SVGSurface(self.fname, iwidth, iheight) 86 self.surface = cairo.SVGSurface(self.fname, iwidth, iheight)
87 self.ctx = cairo.Context(self.surface) 87 self.ctx = cairo.Context(self.surface)
88 88
89 def write_out(self, fname): 89 def write_out(self, fname):
90 os.execl('cp', self.fname, fname) 90 os.execl('cp', self.fname, fname)
91 91
92class ImageSurface(Surface): 92class ImageSurface(Surface):
93 def renew(self, width, height): 93 def renew(self, width, height):
94 iwidth = int(math.ceil(width)) 94 iwidth = int(math.ceil(width))
95 iheight = int(math.ceil(height)) 95 iheight = int(math.ceil(height))
96 self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, iwidth, iheight) 96 self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, iwidth, iheight)
97 self.ctx = cairo.Context(self.surface) 97 self.ctx = cairo.Context(self.surface)
98 98
99 def write_out(self, fname): 99 def write_out(self, fname):
100 if self.surface is None: 100 if self.surface is None:
101 raise ValueError('Don\'t own surface, can\'t write to to file') 101 raise ValueError('Don\'t own surface, can\'t write to to file')
102 102
103 self.surface.write_to_png(fname) 103 self.surface.write_to_png(fname)
104 104
105class Pattern(object): 105class Pattern(object):
106 DEF_STRIPE_SIZE = 10 106 DEF_STRIPE_SIZE = 10
107 MAX_FADE_WIDTH = 250 107 MAX_FADE_WIDTH = 250
108 108
109 def __init__(self, color_list, stripe_size=DEF_STRIPE_SIZE): 109 def __init__(self, color_list, stripe_size=DEF_STRIPE_SIZE):
110 self.color_list = color_list 110 self.color_list = color_list
111 self.stripe_size = stripe_size 111 self.stripe_size = stripe_size
112 112
113 def render_on_canvas(self, canvas, x, y, width, height, fade=False): 113 def render_on_canvas(self, canvas, x, y, width, height, fade=False):
114 fade_span = min(width, Pattern.MAX_FADE_WIDTH) 114 fade_span = min(width, Pattern.MAX_FADE_WIDTH)
115 115
116 if len(self.color_list) == 1: 116 if len(self.color_list) == 1:
117 if fade: 117 if fade:
118 canvas.fill_rect_fade(x, y, fade_span, height, (1.0, 1.0, 1.0), \ 118 canvas.fill_rect_fade(x, y, fade_span, height, (1.0, 1.0, 1.0), \
119 self.color_list[0]) 119 self.color_list[0])
120 else: 120 else:
121 canvas.fill_rect(x, y, width, height, self.color_list[0]) 121 canvas.fill_rect(x, y, width, height, self.color_list[0])
122 122
123 if width > Pattern.MAX_FADE_WIDTH: 123 if width > Pattern.MAX_FADE_WIDTH:
124 canvas.fill_rect(x + Pattern.MAX_FADE_WIDTH, y, width - Pattern.MAX_FADE_WIDTH, 124 canvas.fill_rect(x + Pattern.MAX_FADE_WIDTH, y, width - Pattern.MAX_FADE_WIDTH,
125 height, self.color_list[0]) 125 height, self.color_list[0])
@@ -133,100 +133,100 @@ class Pattern(object):
133 min(self.stripe_size, bottom - y), (1.0, 1.0, 1.0), self.color_list[i]) 133 min(self.stripe_size, bottom - y), (1.0, 1.0, 1.0), self.color_list[i])
134 else: 134 else:
135 canvas.fill_rect(x, y, width, min(self.stripe_size, bottom - y), self.color_list[i]) 135 canvas.fill_rect(x, y, width, min(self.stripe_size, bottom - y), self.color_list[i])
136 136
137 if width > Pattern.MAX_FADE_WIDTH: 137 if width > Pattern.MAX_FADE_WIDTH:
138 canvas.fill_rect(x + Pattern.MAX_FADE_WIDTH, y, width - Pattern.MAX_FADE_WIDTH, 138 canvas.fill_rect(x + Pattern.MAX_FADE_WIDTH, y, width - Pattern.MAX_FADE_WIDTH,
139 min(self.stripe_size, bottom - y), self.color_list[i]) 139 min(self.stripe_size, bottom - y), self.color_list[i])
140 140
141 y += self.stripe_size 141 y += self.stripe_size
142 n += 1 142 n += 1
143 143
144class Canvas(object): 144class Canvas(object):
145 """This is a basic class that stores and draws on a Cairo surface, 145 """This is a basic class that stores and draws on a Cairo surface,
146 using various primitives related to drawing a real-time graph (up-arrows, 146 using various primitives related to drawing a real-time graph (up-arrows,
147 down-arrows, bars, ...). 147 down-arrows, bars, ...).
148 148
149 This is the lowest-level representation (aside perhaps from the Cairo 149 This is the lowest-level representation (aside perhaps from the Cairo
150 surface itself) of a real-time graph. It allows the user to draw 150 surface itself) of a real-time graph. It allows the user to draw
151 primitives at certain locations, but for the most part does not know 151 primitives at certain locations, but for the most part does not know
152 anything about real-time scheduling, just how to draw the basic parts 152 anything about real-time scheduling, just how to draw the basic parts
153 that make up a schedule graph. For that, see Graph or its descendants.""" 153 that make up a schedule graph. For that, see Graph or its descendants."""
154 154
155 BOTTOM_LAYER = 0 155 BOTTOM_LAYER = 0
156 MIDDLE_LAYER = 1 156 MIDDLE_LAYER = 1
157 TOP_LAYER = 2 157 TOP_LAYER = 2
158 158
159 LAYERS = (BOTTOM_LAYER, MIDDLE_LAYER, TOP_LAYER) 159 LAYERS = (BOTTOM_LAYER, MIDDLE_LAYER, TOP_LAYER)
160 160
161 NULL_PATTERN = -1 161 NULL_PATTERN = -1
162 162
163 SQRT3 = math.sqrt(3.0) 163 SQRT3 = math.sqrt(3.0)
164 164
165 def __init__(self, width, height, item_clist, bar_plist, surface): 165 def __init__(self, width, height, item_clist, bar_plist, surface):
166 """Creates a new Canvas of dimensions (width, height). The 166 """Creates a new Canvas of dimensions (width, height). The
167 parameters ``item_plist'' and ``bar_plist'' each specify a list 167 parameters ``item_plist'' and ``bar_plist'' each specify a list
168 of patterns to choose from when drawing the items on the y-axis 168 of patterns to choose from when drawing the items on the y-axis
169 or filling in bars, respectively.""" 169 or filling in bars, respectively."""
170 170
171 self.surface = surface 171 self.surface = surface
172 172
173 self.width = int(math.ceil(width)) 173 self.width = int(math.ceil(width))
174 self.height = int(math.ceil(height)) 174 self.height = int(math.ceil(height))
175 self.item_clist = item_clist 175 self.item_clist = item_clist
176 self.bar_plist = bar_plist 176 self.bar_plist = bar_plist
177 177
178 self.selectable_regions = {} 178 self.selectable_regions = {}
179 179
180 self.scale = 1.0 180 self.scale = 1.0
181 181
182 # clears the canvas. 182 # clears the canvas.
183 def clear(self): 183 def clear(self):
184 raise NotImplementedError 184 raise NotImplementedError
185 185
186 def set_scale(self, scale): 186 def set_scale(self, scale):
187 self.scale = scale 187 self.scale = scale
188 self.surface.set_scale(scale) 188 self.surface.set_scale(scale)
189 for event in self.selectable_regions: 189 for event in self.selectable_regions:
190 self.selectable_regions[event].set_scale(scale) 190 self.selectable_regions[event].set_scale(scale)
191 191
192 def scaled(self, *coors): 192 def scaled(self, *coors):
193 """Scales a series of coordinates.""" 193 """Scales a series of coordinates."""
194 return [coor * self.scale for coor in coors] 194 return [coor * self.scale for coor in coors]
195 195
196 def unscaled(self, *coors): 196 def unscaled(self, *coors):
197 """Inverse of scale().""" 197 """Inverse of scale()."""
198 return [coor / self.scale for coor in coors] 198 return [coor / self.scale for coor in coors]
199 199
200 def draw_rect(self, x, y, width, height, color, thickness, snap=True): 200 def draw_rect(self, x, y, width, height, color, thickness, snap=True):
201 """Draws a rectangle somewhere (border only).""" 201 """Draws a rectangle somewhere (border only)."""
202 raise NotImplementedError 202 raise NotImplementedError
203 203
204 def fill_rect(self, x, y, width, height, color, snap=True): 204 def fill_rect(self, x, y, width, height, color, snap=True):
205 """Draws a filled rectangle somewhere. ``color'' is a 3-tuple.""" 205 """Draws a filled rectangle somewhere. ``color'' is a 3-tuple."""
206 raise NotImplementedError 206 raise NotImplementedError
207 207
208 def fill_rect_fade(self, x, y, width, height, lcolor, rcolor, snap=True): 208 def fill_rect_fade(self, x, y, width, height, lcolor, rcolor, snap=True):
209 """Draws a rectangle somewhere, filled in with the fade.""" 209 """Draws a rectangle somewhere, filled in with the fade."""
210 raise NotImplementedError 210 raise NotImplementedError
211 211
212 def draw_line(self, p0, p1, color, thickness, snap=True): 212 def draw_line(self, p0, p1, color, thickness, snap=True):
213 """Draws a line from p0 to p1 with a certain color and thickness.""" 213 """Draws a line from p0 to p1 with a certain color and thickness."""
214 raise NotImplementedError 214 raise NotImplementedError
215 215
216 def draw_polyline(self, coor_list, color, thickness, snap=True): 216 def draw_polyline(self, coor_list, color, thickness, snap=True):
217 """Draws a polyline, where coor_list = [(x_0, y_0), (x_1, y_1), ... (x_m, y_m)] 217 """Draws a polyline, where coor_list = [(x_0, y_0), (x_1, y_1), ... (x_m, y_m)]
218 specifies a polyline from (x_0, y_0) to (x_1, y_1), etc.""" 218 specifies a polyline from (x_0, y_0) to (x_1, y_1), etc."""
219 raise NotImplementedError 219 raise NotImplementedError
220 220
221 def fill_polyline(self, coor_list, color, thickness, snap=True): 221 def fill_polyline(self, coor_list, color, thickness, snap=True):
222 """Draws a polyline (probably a polygon) and fills it.""" 222 """Draws a polyline (probably a polygon) and fills it."""
223 raise NotImplementedError 223 raise NotImplementedError
224 224
225 def draw_label(self, text, x, y, fopts=GraphFormat.DEF_FOPTS_LABEL, 225 def draw_label(self, text, x, y, fopts=GraphFormat.DEF_FOPTS_LABEL,
226 halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, snap=True): 226 halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, snap=True):
227 """Draws text at a position with a certain alignment.""" 227 """Draws text at a position with a certain alignment."""
228 raise NotImplementedError 228 raise NotImplementedError
229 229
230 def draw_label_with_sscripts(self, text, supscript, subscript, x, y, \ 230 def draw_label_with_sscripts(self, text, supscript, subscript, x, y, \
231 textfopts=GraphFormat.DEF_FOPTS_LABEL, 231 textfopts=GraphFormat.DEF_FOPTS_LABEL,
232 sscriptfopts=GraphFormat.DEF_FOPTS_LABEL_SSCRIPT, \ 232 sscriptfopts=GraphFormat.DEF_FOPTS_LABEL_SSCRIPT, \
@@ -234,31 +234,31 @@ class Canvas(object):
234 """Draws text at a position with a certain alignment, along with optionally a superscript and 234 """Draws text at a position with a certain alignment, along with optionally a superscript and
235 subscript (which are None if either is not used.)""" 235 subscript (which are None if either is not used.)"""
236 raise NotImplementedError 236 raise NotImplementedError
237 237
238 def draw_y_axis(self, x, y, height): 238 def draw_y_axis(self, x, y, height):
239 """Draws the y-axis, starting from the bottom at the point x, y.""" 239 """Draws the y-axis, starting from the bottom at the point x, y."""
240 self.surface.ctx.set_source_rgb(0.0, 0.0, 0.0) 240 self.surface.ctx.set_source_rgb(0.0, 0.0, 0.0)
241 241
242 self.draw_line((x, y), (x, y - height), (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS) 242 self.draw_line((x, y), (x, y - height), (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS)
243 243
244 def draw_y_axis_labels(self, x, y, height, item_list, item_size, fopts=None): 244 def draw_y_axis_labels(self, x, y, height, item_list, item_size, fopts=None):
245 """Draws the item labels on the y-axis. ``item_list'' is the list 245 """Draws the item labels on the y-axis. ``item_list'' is the list
246 of strings to print, while item_size gives the vertical amount of 246 of strings to print, while item_size gives the vertical amount of
247 space that each item shall take up, in pixels.""" 247 space that each item shall take up, in pixels."""
248 if fopts is None: 248 if fopts is None:
249 fopts = GraphFormat.DEF_FOPTS_ITEM 249 fopts = GraphFormat.DEF_FOPTS_ITEM
250 250
251 x -= GraphFormat.Y_AXIS_ITEM_GAP 251 x -= GraphFormat.Y_AXIS_ITEM_GAP
252 y -= height - item_size / 2.0 252 y -= height - item_size / 2.0
253 253
254 orig_color = fopts.color 254 orig_color = fopts.color
255 for ctr, item in enumerate(item_list): 255 for ctr, item in enumerate(item_list):
256 fopts.color = self.get_item_color(ctr) 256 fopts.color = self.get_item_color(ctr)
257 self.draw_label(item, x, y, fopts, AlignMode.RIGHT, AlignMode.CENTER) 257 self.draw_label(item, x, y, fopts, AlignMode.RIGHT, AlignMode.CENTER)
258 y += item_size 258 y += item_size
259 259
260 fopts.color = orig_color 260 fopts.color = orig_color
261 261
262 def draw_x_axis(self, x, y, start_tick, end_tick, maj_sep, min_per_maj): 262 def draw_x_axis(self, x, y, start_tick, end_tick, maj_sep, min_per_maj):
263 """Draws the x-axis, including all the major and minor ticks (but not the labels). 263 """Draws the x-axis, including all the major and minor ticks (but not the labels).
264 ``num_maj'' gives the number of major ticks, ``maj_sep'' the number of pixels between 264 ``num_maj'' gives the number of major ticks, ``maj_sep'' the number of pixels between
@@ -271,48 +271,48 @@ class Canvas(object):
271 for i in range(start_tick, end_tick + 1): 271 for i in range(start_tick, end_tick + 1):
272 self.draw_line((x, y), (x, y + GraphFormat.MAJ_TICK_SIZE), 272 self.draw_line((x, y), (x, y + GraphFormat.MAJ_TICK_SIZE),
273 (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS) 273 (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS)
274 274
275 if (i < end_tick): 275 if (i < end_tick):
276 for j in range(0, min_per_maj): 276 for j in range(0, min_per_maj):
277 self.draw_line((x, y), (x + maj_sep / min_per_maj, y), 277 self.draw_line((x, y), (x + maj_sep / min_per_maj, y),
278 (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS) 278 (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS)
279 279
280 x += 1.0 * maj_sep / min_per_maj 280 x += 1.0 * maj_sep / min_per_maj
281 if j < min_per_maj - 1: 281 if j < min_per_maj - 1:
282 self.draw_line((x, y), (x, y + GraphFormat.MIN_TICK_SIZE), 282 self.draw_line((x, y), (x, y + GraphFormat.MIN_TICK_SIZE),
283 (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS) 283 (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS)
284 284
285 def draw_x_axis_labels(self, x, y, start_tick, end_tick, maj_sep, min_per_maj, start=0, incr=1, show_min=False, \ 285 def draw_x_axis_labels(self, x, y, start_tick, end_tick, maj_sep, min_per_maj, start=0, incr=1, show_min=False, \
286 majfopts=GraphFormat.DEF_FOPTS_MAJ, minfopts=GraphFormat.DEF_FOPTS_MIN): 286 majfopts=GraphFormat.DEF_FOPTS_MAJ, minfopts=GraphFormat.DEF_FOPTS_MIN):
287 """Draws the labels for the x-axis. (x, y) should give the origin. 287 """Draws the labels for the x-axis. (x, y) should give the origin.
288 how far down you want the text. ``incr'' gives the increment per major 288 how far down you want the text. ``incr'' gives the increment per major
289 tick. ``start'' gives the value of the first tick. ``show_min'' specifies 289 tick. ``start'' gives the value of the first tick. ``show_min'' specifies
290 whether to draw labels at minor ticks.""" 290 whether to draw labels at minor ticks."""
291 291
292 x += GraphFormat.X_AXIS_MEASURE_OFS + start_tick * maj_sep 292 x += GraphFormat.X_AXIS_MEASURE_OFS + start_tick * maj_sep
293 y += GraphFormat.X_AXIS_LABEL_GAP + GraphFormat.MAJ_TICK_SIZE 293 y += GraphFormat.X_AXIS_LABEL_GAP + GraphFormat.MAJ_TICK_SIZE
294 294
295 minincr = incr / (min_per_maj * 1.0) 295 minincr = incr / (min_per_maj * 1.0)
296 296
297 cur = start * 1.0 297 cur = start * 1.0
298 298
299 for i in range(start_tick, end_tick + 1): 299 for i in range(start_tick, end_tick + 1):
300 text = util.format_float(cur, 2) 300 text = util.format_float(cur, 2)
301 self.draw_label(text, x, y, majfopts, AlignMode.CENTER, AlignMode.TOP) 301 self.draw_label(text, x, y, majfopts, AlignMode.CENTER, AlignMode.TOP)
302 302
303 if (i < end_tick): 303 if (i < end_tick):
304 if show_min: 304 if show_min:
305 for j in range(0, min_per_maj): 305 for j in range(0, min_per_maj):
306 x += 1.0 * maj_sep / min_per_maj 306 x += 1.0 * maj_sep / min_per_maj
307 cur += minincr 307 cur += minincr
308 text = util.format_float(cur, 2) 308 text = util.format_float(cur, 2)
309 309
310 if j < min_per_maj - 1: 310 if j < min_per_maj - 1:
311 self.draw_label(text, x, y, minfopts, AlignMode.CENTER, AlignMode.TOP) 311 self.draw_label(text, x, y, minfopts, AlignMode.CENTER, AlignMode.TOP)
312 else: 312 else:
313 x += maj_sep 313 x += maj_sep
314 cur += incr 314 cur += incr
315 315
316 def draw_grid(self, x, y, height, start_tick, end_tick, start_item, end_item, maj_sep, item_size, \ 316 def draw_grid(self, x, y, height, start_tick, end_tick, start_item, end_item, maj_sep, item_size, \
317 min_per_maj=None, show_min=False): 317 min_per_maj=None, show_min=False):
318 """Draws a grid dividing along the item boundaries and the major ticks. 318 """Draws a grid dividing along the item boundaries and the major ticks.
@@ -321,22 +321,22 @@ class Canvas(object):
321 ``start_item'' and ``end_item'' give the item boundaries to start and end drawing horizontal lines.""" 321 ``start_item'' and ``end_item'' give the item boundaries to start and end drawing horizontal lines."""
322 if start_tick > end_tick or start_item > end_item: 322 if start_tick > end_tick or start_item > end_item:
323 return 323 return
324 324
325 line_width = (end_tick - start_tick) * maj_sep 325 line_width = (end_tick - start_tick) * maj_sep
326 line_height = (end_item - start_item) * item_size 326 line_height = (end_item - start_item) * item_size
327 327
328 origin = (x, y) 328 origin = (x, y)
329 329
330 # draw horizontal lines first 330 # draw horizontal lines first
331 x = origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + start_tick * maj_sep 331 x = origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + start_tick * maj_sep
332 y = origin[1] - height + start_item * item_size 332 y = origin[1] - height + start_item * item_size
333 for i in range(start_item, end_item + 1): 333 for i in range(start_item, end_item + 1):
334 self.draw_line((x, y), (x + line_width, y), GraphFormat.GRID_COLOR, GraphFormat.GRID_THICKNESS) 334 self.draw_line((x, y), (x + line_width, y), GraphFormat.GRID_COLOR, GraphFormat.GRID_THICKNESS)
335 y += item_size 335 y += item_size
336 336
337 x = origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + start_tick * maj_sep 337 x = origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + start_tick * maj_sep
338 y = origin[1] - height + start_item * item_size 338 y = origin[1] - height + start_item * item_size
339 339
340 if show_min: 340 if show_min:
341 for i in range(0, (end_tick - start_tick) * min_per_maj + 1): 341 for i in range(0, (end_tick - start_tick) * min_per_maj + 1):
342 self.draw_line((x, y), (x, y + line_height), GraphFormat.GRID_COLOR, GraphFormat.GRID_THICKNESS) 342 self.draw_line((x, y), (x, y + line_height), GraphFormat.GRID_COLOR, GraphFormat.GRID_THICKNESS)
@@ -345,222 +345,222 @@ class Canvas(object):
345 for i in range(start_tick, end_tick + 1): 345 for i in range(start_tick, end_tick + 1):
346 self.draw_line((x, y), (x, y + line_height), GraphFormat.GRID_COLOR, GraphFormat.GRID_THICKNESS) 346 self.draw_line((x, y), (x, y + line_height), GraphFormat.GRID_COLOR, GraphFormat.GRID_THICKNESS)
347 x += maj_sep 347 x += maj_sep
348 348
349 def draw_bar(self, x, y, width, height, n, clip_side, selected): 349 def draw_bar(self, x, y, width, height, n, clip_side, selected):
350 """Draws a bar with a certain set of dimensions, using pattern ``n'' from the 350 """Draws a bar with a certain set of dimensions, using pattern ``n'' from the
351 bar pattern list.""" 351 bar pattern list."""
352 352
353 color, thickness = {False : (GraphFormat.BORDER_COLOR, GraphFormat.BORDER_THICKNESS), 353 color, thickness = {False : (GraphFormat.BORDER_COLOR, GraphFormat.BORDER_THICKNESS),
354 True : (GraphFormat.HIGHLIGHT_COLOR, GraphFormat.BORDER_THICKNESS * 2.0)}[selected] 354 True : (GraphFormat.HIGHLIGHT_COLOR, GraphFormat.BORDER_THICKNESS * 2.0)}[selected]
355 355
356 # use a pattern to be pretty 356 # use a pattern to be pretty
357 self.get_bar_pattern(n).render_on_canvas(self, x, y, width, height, True) 357 self.get_bar_pattern(n).render_on_canvas(self, x, y, width, height, True)
358 358
359 self.draw_rect(x, y, width, height, color, thickness, clip_side) 359 self.draw_rect(x, y, width, height, color, thickness, clip_side)
360 360
361 def add_sel_bar(self, x, y, width, height, event): 361 def add_sel_bar(self, x, y, width, height, event):
362 self.add_sel_region(SelectableRegion(x, y, width, height, event)) 362 self.add_sel_region(SelectableRegion(x, y, width, height, event))
363 363
364 def draw_mini_bar(self, x, y, width, height, n, clip_side, selected): 364 def draw_mini_bar(self, x, y, width, height, n, clip_side, selected):
365 """Like the above, except it draws a miniature version. This is usually used for 365 """Like the above, except it draws a miniature version. This is usually used for
366 secondary purposes (i.e. to show jobs that _should_ have been running at a certain time). 366 secondary purposes (i.e. to show jobs that _should_ have been running at a certain time).
367 367
368 Of course we don't enforce the fact that this is mini, since the user can pass in width 368 Of course we don't enforce the fact that this is mini, since the user can pass in width
369 and height (but the mini bars do look slightly different: namely the borders are a different 369 and height (but the mini bars do look slightly different: namely the borders are a different
370 color)""" 370 color)"""
371 371
372 color, thickness = {False : (GraphFormat.LITE_BORDER_COLOR, GraphFormat.BORDER_THICKNESS), 372 color, thickness = {False : (GraphFormat.LITE_BORDER_COLOR, GraphFormat.BORDER_THICKNESS),
373 True : (GraphFormat.HIGHLIGHT_COLOR, GraphFormat.BORDER_THICKNESS * 1.5)}[selected] 373 True : (GraphFormat.HIGHLIGHT_COLOR, GraphFormat.BORDER_THICKNESS * 1.5)}[selected]
374 374
375 self.get_bar_pattern(n).render_on_canvas(self, x, y, width, height, True) 375 self.get_bar_pattern(n).render_on_canvas(self, x, y, width, height, True)
376 376
377 self.draw_rect(x, y, width, height, color, thickness, clip_side) 377 self.draw_rect(x, y, width, height, color, thickness, clip_side)
378 378
379 def add_sel_mini_bar(self, x, y, width, height, event): 379 def add_sel_mini_bar(self, x, y, width, height, event):
380 self.add_sel_region(SelectableRegion(x, y, width, height, event)) 380 self.add_sel_region(SelectableRegion(x, y, width, height, event))
381 381
382 def draw_completion_marker(self, x, y, height, selected): 382 def draw_completion_marker(self, x, y, height, selected):
383 """Draws the symbol that represents a job completion, using a certain height.""" 383 """Draws the symbol that represents a job completion, using a certain height."""
384 384
385 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] 385 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected]
386 self.draw_line((x - height * GraphFormat.TEE_FACTOR / 2.0, y), 386 self.draw_line((x - height * GraphFormat.TEE_FACTOR / 2.0, y),
387 (x + height * GraphFormat.TEE_FACTOR / 2.0, y), 387 (x + height * GraphFormat.TEE_FACTOR / 2.0, y),
388 color, GraphFormat.BORDER_THICKNESS) 388 color, GraphFormat.BORDER_THICKNESS)
389 self.draw_line((x, y), (x, y + height), color, GraphFormat.BORDER_THICKNESS) 389 self.draw_line((x, y), (x, y + height), color, GraphFormat.BORDER_THICKNESS)
390 390
391 def add_sel_completion_marker(self, x, y, height, event): 391 def add_sel_completion_marker(self, x, y, height, event):
392 self.add_sel_region(SelectableRegion(x - height * GraphFormat.TEE_FACTOR / 2.0, y, 392 self.add_sel_region(SelectableRegion(x - height * GraphFormat.TEE_FACTOR / 2.0, y,
393 height * GraphFormat.TEE_FACTOR, height, event)) 393 height * GraphFormat.TEE_FACTOR, height, event))
394 394
395 def draw_release_arrow_big(self, x, y, height, selected): 395 def draw_release_arrow_big(self, x, y, height, selected):
396 """Draws a release arrow of a certain height: (x, y) should give the top 396 """Draws a release arrow of a certain height: (x, y) should give the top
397 (northernmost point) of the arrow. The height includes the arrowhead.""" 397 (northernmost point) of the arrow. The height includes the arrowhead."""
398 big_arrowhead_height = GraphFormat.BIG_ARROWHEAD_FACTOR * height 398 big_arrowhead_height = GraphFormat.BIG_ARROWHEAD_FACTOR * height
399 399
400 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] 400 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected]
401 colors = [(1.0, 1.0, 1.0), color] 401 colors = [(1.0, 1.0, 1.0), color]
402 draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline] 402 draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline]
403 for i in range(0, 2): 403 for i in range(0, 2):
404 color = colors[i] 404 color = colors[i]
405 draw_func = draw_funcs[i] 405 draw_func = draw_funcs[i]
406 406
407 draw_func(self, [(x, y), (x - big_arrowhead_height / Canvas.SQRT3, y + big_arrowhead_height), \ 407 draw_func(self, [(x, y), (x - big_arrowhead_height / Canvas.SQRT3, y + big_arrowhead_height), \
408 (x + big_arrowhead_height / Canvas.SQRT3, y + big_arrowhead_height), (x, y)], \ 408 (x + big_arrowhead_height / Canvas.SQRT3, y + big_arrowhead_height), (x, y)], \
409 color, GraphFormat.BORDER_THICKNESS) 409 color, GraphFormat.BORDER_THICKNESS)
410 410
411 self.draw_line((x, y + big_arrowhead_height), (x, y + height), color, GraphFormat.BORDER_THICKNESS) 411 self.draw_line((x, y + big_arrowhead_height), (x, y + height), color, GraphFormat.BORDER_THICKNESS)
412 412
413 def add_sel_release_arrow_big(self, x, y, height, event): 413 def add_sel_release_arrow_big(self, x, y, height, event):
414 self.add_sel_arrow_big(x, y, height, event) 414 self.add_sel_arrow_big(x, y, height, event)
415 415
416 def draw_deadline_arrow_big(self, x, y, height, selected): 416 def draw_deadline_arrow_big(self, x, y, height, selected):
417 """Draws a release arrow: x, y should give the top (northernmost 417 """Draws a release arrow: x, y should give the top (northernmost
418 point) of the arrow. The height includes the arrowhead.""" 418 point) of the arrow. The height includes the arrowhead."""
419 big_arrowhead_height = GraphFormat.BIG_ARROWHEAD_FACTOR * height 419 big_arrowhead_height = GraphFormat.BIG_ARROWHEAD_FACTOR * height
420 420
421 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] 421 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected]
422 colors = [(1.0, 1.0, 1.0), color] 422 colors = [(1.0, 1.0, 1.0), color]
423 draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline] 423 draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline]
424 for i in range(0, 2): 424 for i in range(0, 2):
425 color = colors[i] 425 color = colors[i]
426 draw_func = draw_funcs[i] 426 draw_func = draw_funcs[i]
427 427
428 draw_func(self, [(x, y + height), (x - big_arrowhead_height / Canvas.SQRT3, \ 428 draw_func(self, [(x, y + height), (x - big_arrowhead_height / Canvas.SQRT3, \
429 y + height - big_arrowhead_height), \ 429 y + height - big_arrowhead_height), \
430 (x + big_arrowhead_height / Canvas.SQRT3, \ 430 (x + big_arrowhead_height / Canvas.SQRT3, \
431 y + height - big_arrowhead_height), \ 431 y + height - big_arrowhead_height), \
432 (x, y + height)], color, GraphFormat.BORDER_THICKNESS) 432 (x, y + height)], color, GraphFormat.BORDER_THICKNESS)
433 433
434 self.draw_line((x, y), (x, y + height - big_arrowhead_height), 434 self.draw_line((x, y), (x, y + height - big_arrowhead_height),
435 color, GraphFormat.BORDER_THICKNESS) 435 color, GraphFormat.BORDER_THICKNESS)
436 436
437 def add_sel_deadline_arrow_big(self, x, y, height, event): 437 def add_sel_deadline_arrow_big(self, x, y, height, event):
438 self.add_sel_arrow_big(x, y, height, event) 438 self.add_sel_arrow_big(x, y, height, event)
439 439
440 def add_sel_arrow_big(self, x, y, height, event): 440 def add_sel_arrow_big(self, x, y, height, event):
441 big_arrowhead_height = GraphFormat.BIG_ARROWHEAD_FACTOR * height 441 big_arrowhead_height = GraphFormat.BIG_ARROWHEAD_FACTOR * height
442 442
443 self.add_sel_region(SelectableRegion(x - big_arrowhead_height / Canvas.SQRT3, 443 self.add_sel_region(SelectableRegion(x - big_arrowhead_height / Canvas.SQRT3,
444 y, 2.0 * big_arrowhead_height / Canvas.SQRT3, height, event)) 444 y, 2.0 * big_arrowhead_height / Canvas.SQRT3, height, event))
445 445
446 def draw_release_arrow_small(self, x, y, height, selected): 446 def draw_release_arrow_small(self, x, y, height, selected):
447 """Draws a small release arrow (most likely coming off the x-axis, although 447 """Draws a small release arrow (most likely coming off the x-axis, although
448 this method doesn't enforce this): x, y should give the top of the arrow""" 448 this method doesn't enforce this): x, y should give the top of the arrow"""
449 small_arrowhead_height = GraphFormat.SMALL_ARROWHEAD_FACTOR * height 449 small_arrowhead_height = GraphFormat.SMALL_ARROWHEAD_FACTOR * height
450 450
451 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] 451 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected]
452 452
453 self.draw_line((x, y), (x - small_arrowhead_height, y + small_arrowhead_height), \ 453 self.draw_line((x, y), (x - small_arrowhead_height, y + small_arrowhead_height), \
454 color, GraphFormat.BORDER_THICKNESS) 454 color, GraphFormat.BORDER_THICKNESS)
455 self.draw_line((x, y), (x + small_arrowhead_height, y + small_arrowhead_height), \ 455 self.draw_line((x, y), (x + small_arrowhead_height, y + small_arrowhead_height), \
456 color, GraphFormat.BORDER_THICKNESS) 456 color, GraphFormat.BORDER_THICKNESS)
457 self.draw_line((x, y), (x, y + height), color, GraphFormat.BORDER_THICKNESS) 457 self.draw_line((x, y), (x, y + height), color, GraphFormat.BORDER_THICKNESS)
458 458
459 def add_sel_release_arrow_small(self, x, y, height, event): 459 def add_sel_release_arrow_small(self, x, y, height, event):
460 self.add_sel_arrow_small(x, y, height, event) 460 self.add_sel_arrow_small(x, y, height, event)
461 461
462 def draw_deadline_arrow_small(self, x, y, height, selected): 462 def draw_deadline_arrow_small(self, x, y, height, selected):
463 """Draws a small deadline arrow (most likely coming off the x-axis, although 463 """Draws a small deadline arrow (most likely coming off the x-axis, although
464 this method doesn't enforce this): x, y should give the top of the arrow""" 464 this method doesn't enforce this): x, y should give the top of the arrow"""
465 small_arrowhead_height = GraphFormat.SMALL_ARROWHEAD_FACTOR * height 465 small_arrowhead_height = GraphFormat.SMALL_ARROWHEAD_FACTOR * height
466 466
467 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] 467 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected]
468 468
469 self.draw_line((x, y), (x, y + height), color, GraphFormat.BORDER_THICKNESS) 469 self.draw_line((x, y), (x, y + height), color, GraphFormat.BORDER_THICKNESS)
470 self.draw_line((x - small_arrowhead_height, y + height - small_arrowhead_height), \ 470 self.draw_line((x - small_arrowhead_height, y + height - small_arrowhead_height), \
471 (x, y + height), color, GraphFormat.BORDER_THICKNESS) 471 (x, y + height), color, GraphFormat.BORDER_THICKNESS)
472 self.draw_line((x + small_arrowhead_height, y + height - small_arrowhead_height), \ 472 self.draw_line((x + small_arrowhead_height, y + height - small_arrowhead_height), \
473 (x, y + height), color, GraphFormat.BORDER_THICKNESS) 473 (x, y + height), color, GraphFormat.BORDER_THICKNESS)
474 474
475 def add_sel_deadline_arrow_small(self, x, y, height, event): 475 def add_sel_deadline_arrow_small(self, x, y, height, event):
476 self.add_sel_arrow_small(x, y, height, event) 476 self.add_sel_arrow_small(x, y, height, event)
477 477
478 def add_sel_arrow_small(self, x, y, height, event): 478 def add_sel_arrow_small(self, x, y, height, event):
479 small_arrowhead_height = GraphFormat.SMALL_ARROWHEAD_FACTOR * height 479 small_arrowhead_height = GraphFormat.SMALL_ARROWHEAD_FACTOR * height
480 480
481 self.add_sel_region(SelectableRegion(x - small_arrowhead_height, y, 481 self.add_sel_region(SelectableRegion(x - small_arrowhead_height, y,
482 small_arrowhead_height * 2.0, height, event)) 482 small_arrowhead_height * 2.0, height, event))
483 483
484 def draw_suspend_triangle(self, x, y, height, selected): 484 def draw_suspend_triangle(self, x, y, height, selected):
485 """Draws the triangle that marks a suspension. (x, y) gives the topmost (northernmost) point 485 """Draws the triangle that marks a suspension. (x, y) gives the topmost (northernmost) point
486 of the symbol.""" 486 of the symbol."""
487 487
488 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] 488 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected]
489 colors = [(0.0, 0.0, 0.0), color] 489 colors = [(0.0, 0.0, 0.0), color]
490 490
491 draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline] 491 draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline]
492 for i in range(0, 2): 492 for i in range(0, 2):
493 color = colors[i] 493 color = colors[i]
494 draw_func = draw_funcs[i] 494 draw_func = draw_funcs[i]
495 draw_func(self, [(x, y), (x + height / 2.0, y + height / 2.0), (x, y + height), (x, y)], \ 495 draw_func(self, [(x, y), (x + height / 2.0, y + height / 2.0), (x, y + height), (x, y)], \
496 color, GraphFormat.BORDER_THICKNESS) 496 color, GraphFormat.BORDER_THICKNESS)
497 497
498 def add_sel_suspend_triangle(self, x, y, height, event): 498 def add_sel_suspend_triangle(self, x, y, height, event):
499 self.add_sel_region(SelectableRegion(x, y, height / 2.0, height, event)) 499 self.add_sel_region(SelectableRegion(x, y, height / 2.0, height, event))
500 500
501 def draw_resume_triangle(self, x, y, height, selected): 501 def draw_resume_triangle(self, x, y, height, selected):
502 """Draws the triangle that marks a resumption. (x, y) gives the topmost (northernmost) point 502 """Draws the triangle that marks a resumption. (x, y) gives the topmost (northernmost) point
503 of the symbol.""" 503 of the symbol."""
504 504
505 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] 505 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected]
506 colors = [(1.0, 1.0, 1.0), color] 506 colors = [(1.0, 1.0, 1.0), color]
507 507
508 draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline] 508 draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline]
509 for i in range(0, 2): 509 for i in range(0, 2):
510 color = colors[i] 510 color = colors[i]
511 draw_func = draw_funcs[i] 511 draw_func = draw_funcs[i]
512 draw_func(self, [(x, y), (x - height / 2.0, y + height / 2.0), (x, y + height), (x, y)], \ 512 draw_func(self, [(x, y), (x - height / 2.0, y + height / 2.0), (x, y + height), (x, y)], \
513 color, GraphFormat.BORDER_THICKNESS) 513 color, GraphFormat.BORDER_THICKNESS)
514 514
515 def add_sel_resume_triangle(self, x, y, height, event): 515 def add_sel_resume_triangle(self, x, y, height, event):
516 self.add_sel_region(SelectableRegion(x - height / 2.0, y, height / 2.0, height, event)) 516 self.add_sel_region(SelectableRegion(x - height / 2.0, y, height / 2.0, height, event))
517 517
518 def clear_selectable_regions(self): 518 def clear_selectable_regions(self):
519 self.selectable_regions = {} 519 self.selectable_regions = {}
520 520
521 #def clear_selectable_regions(self, real_x, real_y, width, height): 521 #def clear_selectable_regions(self, real_x, real_y, width, height):
522 # x, y = self.surface.get_virt_coor(real_x, real_y) 522 # x, y = self.surface.get_virt_coor(real_x, real_y)
523 # for event in self.selectable_regions.keys(): 523 # for event in self.selectable_regions.keys():
524 # if self.selectable_regions[event].intersects(x, y, width, height): 524 # if self.selectable_regions[event].intersects(x, y, width, height):
525 # del self.selectable_regions[event] 525 # del self.selectable_regions[event]
526 526
527 def add_sel_region(self, region): 527 def add_sel_region(self, region):
528 region.set_scale(self.scale) 528 region.set_scale(self.scale)
529 self.selectable_regions[region.get_event()] = region 529 self.selectable_regions[region.get_event()] = region
530 530
531 def get_sel_region(self, event): 531 def get_sel_region(self, event):
532 return self.selectable_regions[event] 532 return self.selectable_regions[event]
533 533
534 def has_sel_region(self, event): 534 def has_sel_region(self, event):
535 return event in self.selectable_regions 535 return event in self.selectable_regions
536 536
537 def get_selected_regions(self, real_x, real_y, width, height): 537 def get_selected_regions(self, real_x, real_y, width, height):
538 x, y = self.surface.get_virt_coor(real_x, real_y) 538 x, y = self.surface.get_virt_coor(real_x, real_y)
539 539
540 selected = {} 540 selected = {}
541 for event in self.selectable_regions: 541 for event in self.selectable_regions:
542 region = self.selectable_regions[event] 542 region = self.selectable_regions[event]
543 if region.intersects(x, y, width, height): 543 if region.intersects(x, y, width, height):
544 selected[event] = region 544 selected[event] = region
545 545
546 return selected 546 return selected
547 547
548 def whiteout(self): 548 def whiteout(self):
549 """Overwrites the surface completely white, but technically doesn't delete anything""" 549 """Overwrites the surface completely white, but technically doesn't delete anything"""
550 # Make sure we don't scale here (we want to literally white out just this region) 550 # Make sure we don't scale here (we want to literally white out just this region)
551 551
552 x, y = self.surface.get_virt_coor_unscaled(0, 0) 552 x, y = self.surface.get_virt_coor_unscaled(0, 0)
553 width, height = self.unscaled(self.surface.width, self.surface.height) 553 width, height = self.unscaled(self.surface.width, self.surface.height)
554 554
555 self.fill_rect(x, y, width, height, (1.0, 1.0, 1.0), False) 555 self.fill_rect(x, y, width, height, (1.0, 1.0, 1.0), False)
556 556
557 def get_item_color(self, n): 557 def get_item_color(self, n):
558 """Gets the nth color in the item color list, which are the colors used to draw the items 558 """Gets the nth color in the item color list, which are the colors used to draw the items
559 on the y-axis. Note that there are conceptually infinitely 559 on the y-axis. Note that there are conceptually infinitely
560 many patterns because the patterns repeat -- that is, we just mod out by the size of the pattern 560 many patterns because the patterns repeat -- that is, we just mod out by the size of the pattern
561 list when indexing.""" 561 list when indexing."""
562 return self.item_clist[n % len(self.item_clist)] 562 return self.item_clist[n % len(self.item_clist)]
563 563
564 def get_bar_pattern(self, n): 564 def get_bar_pattern(self, n):
565 """Gets the nth pattern in the bar pattern list, which is a list of surfaces that are used to 565 """Gets the nth pattern in the bar pattern list, which is a list of surfaces that are used to
566 fill in the bars. Note that there are conceptually infinitely 566 fill in the bars. Note that there are conceptually infinitely
@@ -574,38 +574,38 @@ class CairoCanvas(Canvas):
574 """This is a basic class that stores and draws on a Cairo surface, 574 """This is a basic class that stores and draws on a Cairo surface,
575 using various primitives related to drawing a real-time graph (up-arrows, 575 using various primitives related to drawing a real-time graph (up-arrows,
576 down-arrows, bars, ...). 576 down-arrows, bars, ...).
577 577
578 This is the lowest-level non-abstract representation 578 This is the lowest-level non-abstract representation
579 (aside perhaps from the Cairo surface itself) of a real-time graph. 579 (aside perhaps from the Cairo surface itself) of a real-time graph.
580 It allows the user to draw primitives at certain locations, but for 580 It allows the user to draw primitives at certain locations, but for
581 the most part does not know anything about real-time scheduling, 581 the most part does not know anything about real-time scheduling,
582 just how to draw the basic parts that make up a schedule graph. 582 just how to draw the basic parts that make up a schedule graph.
583 For that, see Graph or its descendants.""" 583 For that, see Graph or its descendants."""
584 584
585 #def __init__(self, fname, width, height, item_clist, bar_plist, surface): 585 #def __init__(self, fname, width, height, item_clist, bar_plist, surface):
586 # """Creates a new Canvas of dimensions (width, height). The 586 # """Creates a new Canvas of dimensions (width, height). The
587 # parameters ``item_plist'' and ``bar_plist'' each specify a list 587 # parameters ``item_plist'' and ``bar_plist'' each specify a list
588 # of patterns to choose from when drawing the items on the y-axis 588 # of patterns to choose from when drawing the items on the y-axis
589 # or filling in bars, respectively.""" 589 # or filling in bars, respectively."""
590 590
591 # super(CairoCanvas, self).__init__(fname, width, height, item_clist, bar_plist, surface) 591 # super(CairoCanvas, self).__init__(fname, width, height, item_clist, bar_plist, surface)
592 592
593 #def clear(self): 593 #def clear(self):
594 # self.surface = self.SurfaceType(self.width, self.height, self.fname) 594 # self.surface = self.SurfaceType(self.width, self.height, self.fname)
595 # self.whiteout() 595 # self.whiteout()
596 596
597 def get_surface(self): 597 def get_surface(self):
598 """Gets the Surface that we are drawing on in its current state.""" 598 """Gets the Surface that we are drawing on in its current state."""
599 return self.surface 599 return self.surface
600 600
601 def _rect_common(self, x, y, width, height, color, thickness, clip_side=None, do_snap=True): 601 def _rect_common(self, x, y, width, height, color, thickness, clip_side=None, do_snap=True):
602 EXTRA_FACTOR = 2.0 602 EXTRA_FACTOR = 2.0
603 603
604 x, y, width, height = self.scaled(x, y, width, height) 604 x, y, width, height = self.scaled(x, y, width, height)
605 x, y = self.surface.get_real_coor(x, y) 605 x, y = self.surface.get_real_coor(x, y)
606 max_width = self.surface.width + EXTRA_FACTOR * thickness 606 max_width = self.surface.width + EXTRA_FACTOR * thickness
607 max_height = self.surface.height + EXTRA_FACTOR * thickness 607 max_height = self.surface.height + EXTRA_FACTOR * thickness
608 608
609 # if dimensions are really large this can cause Cairo problems -- 609 # if dimensions are really large this can cause Cairo problems --
610 # so clip it to the size of the surface, which is the only part we see anyway 610 # so clip it to the size of the surface, which is the only part we see anyway
611 if x < 0: 611 if x < 0:
@@ -618,11 +618,11 @@ class CairoCanvas(Canvas):
618 width = max_width 618 width = max_width
619 if height > max_height: 619 if height > max_height:
620 height = max_height 620 height = max_height
621 621
622 if do_snap: 622 if do_snap:
623 x = snap(x) 623 x = snap(x)
624 y = snap(y) 624 y = snap(y)
625 625
626 if clip_side == AlignMode.LEFT: 626 if clip_side == AlignMode.LEFT:
627 self.surface.ctx.move_to(x, y) 627 self.surface.ctx.move_to(x, y)
628 self.surface.ctx.line_to(x + width, y) 628 self.surface.ctx.line_to(x + width, y)
@@ -636,23 +636,23 @@ class CairoCanvas(Canvas):
636 else: 636 else:
637 # don't clip one edge of the rectangle -- just draw a Cairo rectangle 637 # don't clip one edge of the rectangle -- just draw a Cairo rectangle
638 self.surface.ctx.rectangle(x, y, width, height) 638 self.surface.ctx.rectangle(x, y, width, height)
639 639
640 self.surface.ctx.set_line_width(thickness * self.scale) 640 self.surface.ctx.set_line_width(thickness * self.scale)
641 self.surface.ctx.set_source_rgb(color[0], color[1], color[2]) 641 self.surface.ctx.set_source_rgb(color[0], color[1], color[2])
642 642
643 def draw_rect(self, x, y, width, height, color, thickness, clip_side=None, do_snap=True): 643 def draw_rect(self, x, y, width, height, color, thickness, clip_side=None, do_snap=True):
644 self._rect_common(x, y, width, height, color, thickness, clip_side, do_snap) 644 self._rect_common(x, y, width, height, color, thickness, clip_side, do_snap)
645 self.surface.ctx.stroke() 645 self.surface.ctx.stroke()
646 646
647 def fill_rect(self, x, y, width, height, color, do_snap=True): 647 def fill_rect(self, x, y, width, height, color, do_snap=True):
648 self._rect_common(x, y, width, height, color, 1, do_snap) 648 self._rect_common(x, y, width, height, color, 1, do_snap)
649 self.surface.ctx.fill() 649 self.surface.ctx.fill()
650 650
651 def fill_rect_fade(self, x, y, width, height, lcolor, rcolor, do_snap=True): 651 def fill_rect_fade(self, x, y, width, height, lcolor, rcolor, do_snap=True):
652 """Draws a rectangle somewhere, filled in with the fade.""" 652 """Draws a rectangle somewhere, filled in with the fade."""
653 x, y, width, height = self.scaled(x, y, width, height) 653 x, y, width, height = self.scaled(x, y, width, height)
654 x, y = self.surface.get_real_coor(x, y) 654 x, y = self.surface.get_real_coor(x, y)
655 655
656 if do_snap: 656 if do_snap:
657 linear = cairo.LinearGradient(snap(x), snap(y), \ 657 linear = cairo.LinearGradient(snap(x), snap(y), \
658 snap(x + width), snap(y + height)) 658 snap(x + width), snap(y + height))
@@ -667,7 +667,7 @@ class CairoCanvas(Canvas):
667 else: 667 else:
668 self.surface.ctx.rectangle(x, y, width, height) 668 self.surface.ctx.rectangle(x, y, width, height)
669 self.surface.ctx.fill() 669 self.surface.ctx.fill()
670 670
671 def draw_line(self, p0, p1, color, thickness, do_snap=True): 671 def draw_line(self, p0, p1, color, thickness, do_snap=True):
672 """Draws a line from p0 to p1 with a certain color and thickness.""" 672 """Draws a line from p0 to p1 with a certain color and thickness."""
673 p0 = self.scaled(p0[0], p0[1]) 673 p0 = self.scaled(p0[0], p0[1])
@@ -677,36 +677,36 @@ class CairoCanvas(Canvas):
677 if do_snap: 677 if do_snap:
678 p0 = (snap(p0[0]), snap(p0[1])) 678 p0 = (snap(p0[0]), snap(p0[1]))
679 p1 = (snap(p1[0]), snap(p1[1])) 679 p1 = (snap(p1[0]), snap(p1[1]))
680 680
681 self.surface.ctx.move_to(p0[0], p0[1]) 681 self.surface.ctx.move_to(p0[0], p0[1])
682 self.surface.ctx.line_to(p1[0], p1[1]) 682 self.surface.ctx.line_to(p1[0], p1[1])
683 self.surface.ctx.set_source_rgb(color[0], color[1], color[2]) 683 self.surface.ctx.set_source_rgb(color[0], color[1], color[2])
684 self.surface.ctx.set_line_width(thickness * self.scale) 684 self.surface.ctx.set_line_width(thickness * self.scale)
685 self.surface.ctx.stroke() 685 self.surface.ctx.stroke()
686 686
687 def _polyline_common(self, coor_list, color, thickness, do_snap=True): 687 def _polyline_common(self, coor_list, color, thickness, do_snap=True):
688 scaled_coor_list = [self.scaled(coor[0], coor[1]) for coor in coor_list] 688 scaled_coor_list = [self.scaled(coor[0], coor[1]) for coor in coor_list]
689 real_coor_list = [self.surface.get_real_coor(coor[0], coor[1]) for coor in scaled_coor_list] 689 real_coor_list = [self.surface.get_real_coor(coor[0], coor[1]) for coor in scaled_coor_list]
690 690
691 self.surface.ctx.move_to(real_coor_list[0][0], real_coor_list[0][1]) 691 self.surface.ctx.move_to(real_coor_list[0][0], real_coor_list[0][1])
692 if do_snap: 692 if do_snap:
693 for i in range(0, len(real_coor_list)): 693 for i in range(0, len(real_coor_list)):
694 real_coor_list[i] = (snap(real_coor_list[i][0]), snap(real_coor_list[i][1])) 694 real_coor_list[i] = (snap(real_coor_list[i][0]), snap(real_coor_list[i][1]))
695 695
696 for coor in real_coor_list[1:]: 696 for coor in real_coor_list[1:]:
697 self.surface.ctx.line_to(coor[0], coor[1]) 697 self.surface.ctx.line_to(coor[0], coor[1])
698 698
699 self.surface.ctx.set_line_width(thickness * self.scale) 699 self.surface.ctx.set_line_width(thickness * self.scale)
700 self.surface.ctx.set_source_rgb(color[0], color[1], color[2]) 700 self.surface.ctx.set_source_rgb(color[0], color[1], color[2])
701 701
702 def draw_polyline(self, coor_list, color, thickness, do_snap=True): 702 def draw_polyline(self, coor_list, color, thickness, do_snap=True):
703 self._polyline_common(coor_list, color, thickness, do_snap) 703 self._polyline_common(coor_list, color, thickness, do_snap)
704 self.surface.ctx.stroke() 704 self.surface.ctx.stroke()
705 705
706 def fill_polyline(self, coor_list, color, thickness, do_snap=True): 706 def fill_polyline(self, coor_list, color, thickness, do_snap=True):
707 self._polyline_common(coor_list, color, thickness, do_snap) 707 self._polyline_common(coor_list, color, thickness, do_snap)
708 self.surface.ctx.fill() 708 self.surface.ctx.fill()
709 709
710 def _draw_label_common(self, text, x, y, fopts, x_bearing_factor, \ 710 def _draw_label_common(self, text, x, y, fopts, x_bearing_factor, \
711 f_descent_factor, width_factor, f_height_factor, do_snap=True): 711 f_descent_factor, width_factor, f_height_factor, do_snap=True):
712 """Helper function for drawing a label with some alignment. Instead of taking in an alignment, 712 """Helper function for drawing a label with some alignment. Instead of taking in an alignment,
@@ -714,30 +714,30 @@ class CairoCanvas(Canvas):
714 the x and y parameters. Only should be used internally.""" 714 the x and y parameters. Only should be used internally."""
715 x, y = self.scaled(x, y) 715 x, y = self.scaled(x, y)
716 x, y = self.surface.get_real_coor(x, y) 716 x, y = self.surface.get_real_coor(x, y)
717 717
718 self.surface.ctx.set_source_rgb(0.0, 0.0, 0.0) 718 self.surface.ctx.set_source_rgb(0.0, 0.0, 0.0)
719 719
720 self.surface.ctx.select_font_face(fopts.name, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) 720 self.surface.ctx.select_font_face(fopts.name, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
721 self.surface.ctx.set_font_size(fopts.size * self.scale) 721 self.surface.ctx.set_font_size(fopts.size * self.scale)
722 722
723 fe = self.surface.ctx.font_extents() 723 fe = self.surface.ctx.font_extents()
724 f_ascent, f_descent, f_height = fe[:3] 724 f_ascent, f_descent, f_height = fe[:3]
725 725
726 te = self.surface.ctx.text_extents(text) 726 te = self.surface.ctx.text_extents(text)
727 x_bearing, y_bearing, width, height = te[:4] 727 x_bearing, y_bearing, width, height = te[:4]
728 728
729 actual_x = x - x_bearing * x_bearing_factor - width * width_factor 729 actual_x = x - x_bearing * x_bearing_factor - width * width_factor
730 actual_y = y - f_descent * f_descent_factor + f_height * f_height_factor 730 actual_y = y - f_descent * f_descent_factor + f_height * f_height_factor
731 731
732 self.surface.ctx.set_source_rgb(fopts.color[0], fopts.color[1], fopts.color[2]) 732 self.surface.ctx.set_source_rgb(fopts.color[0], fopts.color[1], fopts.color[2])
733 733
734 if do_snap: 734 if do_snap:
735 self.surface.ctx.move_to(snap(actual_x), snap(actual_y)) 735 self.surface.ctx.move_to(snap(actual_x), snap(actual_y))
736 else: 736 else:
737 self.surface.ctx.move_to(actual_x, actual_y) 737 self.surface.ctx.move_to(actual_x, actual_y)
738 738
739 self.surface.ctx.show_text(text) 739 self.surface.ctx.show_text(text)
740 740
741 def draw_label(self, text, x, y, fopts=GraphFormat.DEF_FOPTS_LABEL, halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, do_snap=True): 741 def draw_label(self, text, x, y, fopts=GraphFormat.DEF_FOPTS_LABEL, halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, do_snap=True):
742 """Draws a label with the given parameters, with the given horizontal and vertical justification. One can override 742 """Draws a label with the given parameters, with the given horizontal and vertical justification. One can override
743 the color from ``fopts'' by passing something in to ``pattern'', which overrides the color with an arbitrary 743 the color from ``fopts'' by passing something in to ``pattern'', which overrides the color with an arbitrary
@@ -747,21 +747,21 @@ class CairoCanvas(Canvas):
747 if halign not in halign_factors: 747 if halign not in halign_factors:
748 raise ValueError('Invalid alignment value') 748 raise ValueError('Invalid alignment value')
749 x_bearing_factor, width_factor = halign_factors[halign] 749 x_bearing_factor, width_factor = halign_factors[halign]
750 750
751 valign_factors = {AlignMode.BOTTOM : (0.0, 0.0), AlignMode.CENTER : (1.0, 0.5), AlignMode.TOP : (1.0, 1.0)} 751 valign_factors = {AlignMode.BOTTOM : (0.0, 0.0), AlignMode.CENTER : (1.0, 0.5), AlignMode.TOP : (1.0, 1.0)}
752 if valign not in valign_factors: 752 if valign not in valign_factors:
753 raise ValueError('Invalid alignment value') 753 raise ValueError('Invalid alignment value')
754 f_descent_factor, f_height_factor = valign_factors[valign] 754 f_descent_factor, f_height_factor = valign_factors[valign]
755 755
756 self._draw_label_common(text, x, y, fopts, x_bearing_factor, \ 756 self._draw_label_common(text, x, y, fopts, x_bearing_factor, \
757 f_descent_factor, width_factor, f_height_factor, do_snap) 757 f_descent_factor, width_factor, f_height_factor, do_snap)
758 758
759 def draw_label_with_sscripts(self, text, supscript, subscript, x, y, \ 759 def draw_label_with_sscripts(self, text, supscript, subscript, x, y, \
760 textfopts=GraphFormat.DEF_FOPTS_LABEL, sscriptfopts=GraphFormat.DEF_FOPTS_LABEL_SSCRIPT, \ 760 textfopts=GraphFormat.DEF_FOPTS_LABEL, sscriptfopts=GraphFormat.DEF_FOPTS_LABEL_SSCRIPT, \
761 halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, do_snap=True): 761 halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, do_snap=True):
762 """Draws a label, but also optionally allows a superscript and subscript to be rendered.""" 762 """Draws a label, but also optionally allows a superscript and subscript to be rendered."""
763 self.draw_label(text, x, y, textfopts, halign, valign) 763 self.draw_label(text, x, y, textfopts, halign, valign)
764 764
765 self.surface.ctx.set_source_rgb(0.0, 0.0, 0.0) 765 self.surface.ctx.set_source_rgb(0.0, 0.0, 0.0)
766 self.surface.ctx.select_font_face(textfopts.name, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) 766 self.surface.ctx.select_font_face(textfopts.name, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
767 self.surface.ctx.set_font_size(textfopts.size) 767 self.surface.ctx.set_font_size(textfopts.size)
@@ -781,7 +781,7 @@ class CairoCanvas(Canvas):
781 ytmp = y 781 ytmp = y
782 ytmp = y + f_height / 4.0 782 ytmp = y + f_height / 4.0
783 self.draw_label(subscript, xtmp, ytmp, sscriptfopts, halign, valign, do_snap) 783 self.draw_label(subscript, xtmp, ytmp, sscriptfopts, halign, valign, do_snap)
784 784
785# represents a selectable region of the graph 785# represents a selectable region of the graph
786class SelectableRegion(object): 786class SelectableRegion(object):
787 def __init__(self, x, y, width, height, event): 787 def __init__(self, x, y, width, height, event):
@@ -791,19 +791,19 @@ class SelectableRegion(object):
791 self.height = height 791 self.height = height
792 self.event = event 792 self.event = event
793 self.scale = 1.0 793 self.scale = 1.0
794 794
795 def get_dimensions(self): 795 def get_dimensions(self):
796 return (self.x, self.y, self.width, self.height) 796 return (self.x, self.y, self.width, self.height)
797 797
798 def get_event(self): 798 def get_event(self):
799 return self.event 799 return self.event
800 800
801 def set_scale(self, scale): 801 def set_scale(self, scale):
802 self.scale = scale 802 self.scale = scale
803 803
804 def intersects(self, x, y, width, height): 804 def intersects(self, x, y, width, height):
805 return x <= (self.x + self.width) * self.scale \ 805 return x <= (self.x + self.width) * self.scale \
806 and x + width >= self.x * self.scale \ 806 and x + width >= self.x * self.scale \
807 and y <= (self.y + self.height) * self.scale \ 807 and y <= (self.y + self.height) * self.scale \
808 and y + height >= self.y * self.scale 808 and y + height >= self.y * self.scale
809 809