summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGary Bressler <garybressler@nc.rr.com>2010-04-06 12:52:38 -0400
committerGary Bressler <garybressler@nc.rr.com>2010-04-06 12:52:38 -0400
commit01abc8352aa2fd192678b4066b26ea749a203801 (patch)
tree5ada25feffcf02b3003f403bb4e3cc4e71b37a91
parent38c18a7992a59774bfc281348c718c5f7db4c557 (diff)
cleanup
-rw-r--r--unit_trace/viz/draw.py1377
1 files changed, 0 insertions, 1377 deletions
diff --git a/unit_trace/viz/draw.py b/unit_trace/viz/draw.py
deleted file mode 100644
index dced27d..0000000
--- a/unit_trace/viz/draw.py
+++ /dev/null
@@ -1,1377 +0,0 @@
1#!/usr/bin/python
2
3import math
4import cairo
5import os
6import copy
7
8import util
9import schedule
10from format import *
11
12def snap(pos):
13 """Takes in an x- or y-coordinate ``pos'' and snaps it to the pixel grid.
14 This is necessary because integer coordinates in Cairo actually denote
15 the spaces between pixels, not the pixels themselves, so if we draw a
16 line of width 1 on integer coordinates, it will come out blurry unless we shift it,
17 since the line will get distributed over two pixels. We actually apply this to all
18 coordinates to make sure everything is aligned."""
19 return pos
20
21class Surface(object):
22 def __init__(self, fname='temp', ctx=None):
23 self.virt_x = 0
24 self.virt_y = 0
25 self.surface = None
26 self.width = 0
27 self.height = 0
28 self.fname = fname
29 self.ctx = ctx
30
31 def renew(self, width, height):
32 raise NotImplementedError
33
34 def change_ctx(self, ctx):
35 self.ctx = ctx
36
37 def get_fname(self):
38 return self.fname
39
40 def write_out(self, fname):
41 raise NotImplementedError
42
43 def pan(self, x, y, width, height):
44 """A surface might actually represent just a ``window'' into
45 what we are drawing on. For instance, if we are scrolling through
46 a graph, then the surface represents the area in the GUI window,
47 not the entire graph (visible or not). So this method basically
48 moves the ``window's'' upper-left corner to (x, y), and resizes
49 the dimensions to (width, height)."""
50 self.virt_x = x
51 self.virt_y = y
52 self.width = width
53 self.height = height
54
55 def get_real_coor(self, x, y):
56 """Translates the coordinates (x, y)
57 in the ``theoretical'' plane to the true (x, y) coordinates on this surface
58 that we should draw to. Note that these might actually be outside the
59 bounds of the surface,
60 if we want something outside the surface's ``window''."""
61 return (x - self.virt_x, y - self.virt_y)
62
63class SVGSurface(Surface):
64 def renew(self, width, height):
65 iwidth = int(math.ceil(width))
66 iheight = int(math.ceil(height))
67 self.surface = cairo.SVGSurface(self.fname, iwidth, iheight)
68 self.ctx = cairo.Context(self.surface)
69
70 def write_out(self, fname):
71 os.execl('cp', self.fname, fname)
72
73class ImageSurface(Surface):
74 def renew(self, width, height):
75 iwidth = int(math.ceil(width))
76 iheight = int(math.ceil(height))
77 self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, iwidth, iheight)
78 self.ctx = cairo.Context(self.surface)
79
80 def write_out(self, fname):
81 if self.surface is None:
82 raise ValueError('Don\'t own surface, can\'t write to to file')
83
84 self.surface.write_to_png(fname)
85
86class Pattern(object):
87 DEF_STRIPE_SIZE = 10
88 MAX_FADE_WIDTH = 250
89
90 def __init__(self, color_list, stripe_size=DEF_STRIPE_SIZE):
91 self.color_list = color_list
92 self.stripe_size = stripe_size
93
94 def render_on_canvas(self, canvas, x, y, width, height, fade=False):
95 fade_span = min(width, Pattern.MAX_FADE_WIDTH)
96
97 if len(self.color_list) == 1:
98 if fade:
99 canvas.fill_rect_fade(x, y, fade_span, height, (1.0, 1.0, 1.0), \
100 self.color_list[0])
101 else:
102 canvas.fill_rect(x, y, width, height, self.color_list[0])
103
104 if width > Pattern.MAX_FADE_WIDTH:
105 canvas.fill_rect(x + Pattern.MAX_FADE_WIDTH, y, width - Pattern.MAX_FADE_WIDTH,
106 height, self.color_list[0])
107 else:
108 n = 0
109 bottom = y + height
110 while y < bottom:
111 i = n % len(self.color_list)
112 if fade:
113 canvas.fill_rect_fade(x, y, fade_span, \
114 min(self.stripe_size, bottom - y), (1.0, 1.0, 1.0), self.color_list[i])
115 else:
116 canvas.fill_rect(x, y, width, min(self.stripe_size, bottom - y), self.color_list[i])
117
118 if width > Pattern.MAX_FADE_WIDTH:
119 canvas.fill_rect(x + Pattern.MAX_FADE_WIDTH, y, width - Pattern.MAX_FADE_WIDTH,
120 min(self.stripe_size, bottom - y), self.color_list[i])
121
122 y += self.stripe_size
123 n += 1
124
125class Canvas(object):
126 """This is a basic class that stores and draws on a Cairo surface,
127 using various primitives related to drawing a real-time graph (up-arrows,
128 down-arrows, bars, ...).
129
130 This is the lowest-level representation (aside perhaps from the Cairo
131 surface itself) of a real-time graph. It allows the user to draw
132 primitives at certain locations, but for the most part does not know
133 anything about real-time scheduling, just how to draw the basic parts
134 that make up a schedule graph. For that, see Graph or its descendants."""
135
136 BOTTOM_LAYER = 0
137 MIDDLE_LAYER = 1
138 TOP_LAYER = 2
139
140 LAYERS = (BOTTOM_LAYER, MIDDLE_LAYER, TOP_LAYER)
141
142 NULL_PATTERN = -1
143
144 SQRT3 = math.sqrt(3.0)
145
146 def __init__(self, width, height, item_clist, bar_plist, surface):
147 """Creates a new Canvas of dimensions (width, height). The
148 parameters ``item_plist'' and ``bar_plist'' each specify a list
149 of patterns to choose from when drawing the items on the y-axis
150 or filling in bars, respectively."""
151
152 self.surface = surface
153
154 self.width = int(math.ceil(width))
155 self.height = int(math.ceil(height))
156 self.item_clist = item_clist
157 self.bar_plist = bar_plist
158
159 self.selectable_regions = {}
160
161 self.scale = 1.0
162
163 # clears the canvas.
164 def clear(self):
165 raise NotImplementedError
166
167 def scaled(self, *coors):
168 return [coor * self.scale for coor in coors]
169
170 def draw_rect(self, x, y, width, height, color, thickness, snap=True):
171 """Draws a rectangle somewhere (border only)."""
172 raise NotImplementedError
173
174 def fill_rect(self, x, y, width, height, color, snap=True):
175 """Draws a filled rectangle somewhere. ``color'' is a 3-tuple."""
176 raise NotImplementedError
177
178 def fill_rect_fade(self, x, y, width, height, lcolor, rcolor, snap=True):
179 """Draws a rectangle somewhere, filled in with the fade."""
180 raise NotImplementedError
181
182 def draw_line(self, p0, p1, color, thickness, snap=True):
183 """Draws a line from p0 to p1 with a certain color and thickness."""
184 raise NotImplementedError
185
186 def draw_polyline(self, coor_list, color, thickness, snap=True):
187 """Draws a polyline, where coor_list = [(x_0, y_0), (x_1, y_1), ... (x_m, y_m)]
188 specifies a polyline from (x_0, y_0) to (x_1, y_1), etc."""
189 raise NotImplementedError
190
191 def fill_polyline(self, coor_list, color, thickness, snap=True):
192 """Draws a polyline (probably a polygon) and fills it."""
193 raise NotImplementedError
194
195 def draw_label(self, text, x, y, fopts=GraphFormat.DEF_FOPTS_LABEL,
196 halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, snap=True):
197 """Draws text at a position with a certain alignment."""
198 raise NotImplementedError
199
200 def draw_label_with_sscripts(self, text, supscript, subscript, x, y, \
201 textfopts=GraphFormat.DEF_FOPTS_LABEL,
202 sscriptfopts=GraphFormat.DEF_FOPTS_LABEL_SSCRIPT, \
203 halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, snap=True):
204 """Draws text at a position with a certain alignment, along with optionally a superscript and
205 subscript (which are None if either is not used.)"""
206 raise NotImplementedError
207
208 def draw_y_axis(self, x, y, height):
209 """Draws the y-axis, starting from the bottom at the point x, y."""
210 self.surface.ctx.set_source_rgb(0.0, 0.0, 0.0)
211
212 self.draw_line((x, y), (x, y - height), (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS)
213
214 def draw_y_axis_labels(self, x, y, height, item_list, item_size, fopts=None):
215 """Draws the item labels on the y-axis. ``item_list'' is the list
216 of strings to print, while item_size gives the vertical amount of
217 space that each item shall take up, in pixels."""
218 if fopts is None:
219 fopts = GraphFormat.DEF_FOPTS_ITEM
220
221 x -= GraphFormat.Y_AXIS_ITEM_GAP
222 y -= height - item_size / 2.0
223
224 orig_color = fopts.color
225 for ctr, item in enumerate(item_list):
226 fopts.color = self.get_item_color(ctr)
227 self.draw_label(item, x, y, fopts, AlignMode.RIGHT, AlignMode.CENTER)
228 y += item_size
229
230 fopts.color = orig_color
231
232 def draw_x_axis(self, x, y, start_tick, end_tick, maj_sep, min_per_maj):
233 """Draws the x-axis, including all the major and minor ticks (but not the labels).
234 ``num_maj'' gives the number of major ticks, ``maj_sep'' the number of pixels between
235 major ticks, and ``min_per_maj'' the number of minor ticks between two major ticks
236 (including the first major tick)"""
237 self.draw_line((x, y), (x + GraphFormat.X_AXIS_MEASURE_OFS, y),
238 (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS)
239 x += GraphFormat.X_AXIS_MEASURE_OFS + start_tick * maj_sep
240
241 for i in range(start_tick, end_tick + 1):
242 self.draw_line((x, y), (x, y + GraphFormat.MAJ_TICK_SIZE),
243 (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS)
244
245 if (i < end_tick):
246 for j in range(0, min_per_maj):
247 self.draw_line((x, y), (x + maj_sep / min_per_maj, y),
248 (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS)
249
250 x += 1.0 * maj_sep / min_per_maj
251 if j < min_per_maj - 1:
252 self.draw_line((x, y), (x, y + GraphFormat.MIN_TICK_SIZE),
253 (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS)
254
255 def draw_x_axis_labels(self, x, y, start_tick, end_tick, maj_sep, min_per_maj, start=0, incr=1, show_min=False, \
256 majfopts=GraphFormat.DEF_FOPTS_MAJ, minfopts=GraphFormat.DEF_FOPTS_MIN):
257 """Draws the labels for the x-axis. (x, y) should give the origin.
258 how far down you want the text. ``incr'' gives the increment per major
259 tick. ``start'' gives the value of the first tick. ``show_min'' specifies
260 whether to draw labels at minor ticks."""
261
262 x += GraphFormat.X_AXIS_MEASURE_OFS + start_tick * maj_sep
263 y += GraphFormat.X_AXIS_LABEL_GAP + GraphFormat.MAJ_TICK_SIZE
264
265 minincr = incr / (min_per_maj * 1.0)
266
267 cur = start * 1.0
268
269 for i in range(start_tick, end_tick + 1):
270 text = util.format_float(cur, 2)
271 self.draw_label(text, x, y, majfopts, AlignMode.CENTER, AlignMode.TOP)
272
273 if (i < end_tick):
274 if show_min:
275 for j in range(0, min_per_maj):
276 x += 1.0 * maj_sep / min_per_maj
277 cur += minincr
278 text = util.format_float(cur, 2)
279
280 if j < min_per_maj - 1:
281 self.draw_label(text, x, y, minfopts, AlignMode.CENTER, AlignMode.TOP)
282 else:
283 x += maj_sep
284 cur += incr
285
286 def draw_grid(self, x, y, height, start_tick, end_tick, start_item, end_item, maj_sep, item_size, \
287 min_per_maj=None, show_min=False):
288 """Draws a grid dividing along the item boundaries and the major ticks.
289 (x, y) gives the origin. ``show_min'' specifies whether to draw vertical grid lines at minor ticks.
290 ``start_tick'' and ``end_tick'' give the major ticks to start and end at for drawing vertical lines.
291 ``start_item'' and ``end_item'' give the item boundaries to start and end drawing horizontal lines."""
292 if start_tick > end_tick or start_item > end_item:
293 return
294
295 line_width = (end_tick - start_tick) * maj_sep
296 line_height = (end_item - start_item) * item_size
297
298 origin = (x, y)
299
300 # draw horizontal lines first
301 x = origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + start_tick * maj_sep
302 y = origin[1] - height + start_item * item_size
303 for i in range(start_item, end_item + 1):
304 self.draw_line((x, y), (x + line_width, y), GraphFormat.GRID_COLOR, GraphFormat.GRID_THICKNESS)
305 y += item_size
306
307 x = origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + start_tick * maj_sep
308 y = origin[1] - height + start_item * item_size
309
310 if show_min:
311 for i in range(0, (end_tick - start_tick) * min_per_maj + 1):
312 self.draw_line((x, y), (x, y + line_height), GraphFormat.GRID_COLOR, GraphFormat.GRID_THICKNESS)
313 x += maj_sep * 1.0 / min_per_maj
314 else:
315 for i in range(start_tick, end_tick + 1):
316 self.draw_line((x, y), (x, y + line_height), GraphFormat.GRID_COLOR, GraphFormat.GRID_THICKNESS)
317 x += maj_sep
318
319 def _draw_bar_border_common(self, x, y, width, height, color, thickness, clip_side):
320 if clip_side is None:
321 self.draw_rect(x, y, width, height, color, thickness)
322 elif clip_side == AlignMode.LEFT:
323 self.draw_polyline([(x, y), (x + width, y), (x + width, y + height), (x, y + height)],
324 color, thickness)
325 elif clip_side == AlignMode.RIGHT:
326 self.draw_polyline([(x + width, y), (x, y), (x, y + height), (x + width, y + height)],
327 color, thickness)
328
329 def draw_bar(self, x, y, width, height, n, clip_side, selected):
330 """Draws a bar with a certain set of dimensions, using pattern ``n'' from the
331 bar pattern list."""
332
333 color, thickness = {False : (GraphFormat.BORDER_COLOR, GraphFormat.BORDER_THICKNESS),
334 True : (GraphFormat.HIGHLIGHT_COLOR, GraphFormat.BORDER_THICKNESS * 2.0)}[selected]
335
336 # use a pattern to be pretty
337 self.get_bar_pattern(n).render_on_canvas(self, x, y, width, height, True)
338
339 self._draw_bar_border_common(x, y, width, height, color, thickness, clip_side)
340
341 def add_sel_bar(self, x, y, width, height, event):
342 self.add_sel_region(SelectableRegion(x, y, width, height, event))
343
344 def draw_mini_bar(self, x, y, width, height, n, clip_side, selected):
345 """Like the above, except it draws a miniature version. This is usually used for
346 secondary purposes (i.e. to show jobs that _should_ have been running at a certain time).
347
348 Of course we don't enforce the fact that this is mini, since the user can pass in width
349 and height (but the mini bars do look slightly different: namely the borders are a different
350 color)"""
351
352 color, thickness = {False : (GraphFormat.LITE_BORDER_COLOR, GraphFormat.BORDER_THICKNESS),
353 True : (GraphFormat.HIGHLIGHT_COLOR, GraphFormat.BORDER_THICKNESS * 1.5)}[selected]
354
355 self.get_bar_pattern(n).render_on_canvas(self, x, y, width, height, True)
356
357 self._draw_bar_border_common(x, y, width, height, color, thickness, clip_side)
358
359 def add_sel_mini_bar(self, x, y, width, height, event):
360 self.add_sel_region(SelectableRegion(x, y, width, height, event))
361
362 def draw_completion_marker(self, x, y, height, selected):
363 """Draws the symbol that represents a job completion, using a certain height."""
364
365 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected]
366 self.draw_line((x - height * GraphFormat.TEE_FACTOR / 2.0, y),
367 (x + height * GraphFormat.TEE_FACTOR / 2.0, y),
368 color, GraphFormat.BORDER_THICKNESS)
369 self.draw_line((x, y), (x, y + height), color, GraphFormat.BORDER_THICKNESS)
370
371 def add_sel_completion_marker(self, x, y, height, event):
372 self.add_sel_region(SelectableRegion(x - height * GraphFormat.TEE_FACTOR / 2.0, y,
373 height * GraphFormat.TEE_FACTOR, height, event))
374
375 def draw_release_arrow_big(self, x, y, height, selected):
376 """Draws a release arrow of a certain height: (x, y) should give the top
377 (northernmost point) of the arrow. The height includes the arrowhead."""
378 big_arrowhead_height = GraphFormat.BIG_ARROWHEAD_FACTOR * height
379
380 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected]
381 colors = [(1.0, 1.0, 1.0), color]
382 draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline]
383 for i in range(0, 2):
384 color = colors[i]
385 draw_func = draw_funcs[i]
386
387 draw_func(self, [(x, y), (x - big_arrowhead_height / Canvas.SQRT3, y + big_arrowhead_height), \
388 (x + big_arrowhead_height / Canvas.SQRT3, y + big_arrowhead_height), (x, y)], \
389 color, GraphFormat.BORDER_THICKNESS)
390
391 self.draw_line((x, y + big_arrowhead_height), (x, y + height), color, GraphFormat.BORDER_THICKNESS)
392
393 def add_sel_release_arrow_big(self, x, y, height, event):
394 self.add_sel_arrow_big(x, y, height, event)
395
396 def draw_deadline_arrow_big(self, x, y, height, selected):
397 """Draws a release arrow: x, y should give the top (northernmost
398 point) of the arrow. The height includes the arrowhead."""
399 big_arrowhead_height = GraphFormat.BIG_ARROWHEAD_FACTOR * height
400
401 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected]
402 colors = [(1.0, 1.0, 1.0), color]
403 draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline]
404 for i in range(0, 2):
405 color = colors[i]
406 draw_func = draw_funcs[i]
407
408 draw_func(self, [(x, y + height), (x - big_arrowhead_height / Canvas.SQRT3, \
409 y + height - big_arrowhead_height), \
410 (x + big_arrowhead_height / Canvas.SQRT3, \
411 y + height - big_arrowhead_height), \
412 (x, y + height)], color, GraphFormat.BORDER_THICKNESS)
413
414 self.draw_line((x, y), (x, y + height - big_arrowhead_height),
415 color, GraphFormat.BORDER_THICKNESS)
416
417 def add_sel_deadline_arrow_big(self, x, y, height, event):
418 self.add_sel_arrow_big(x, y, height, event)
419
420 def add_sel_arrow_big(self, x, y, height, event):
421 big_arrowhead_height = GraphFormat.BIG_ARROWHEAD_FACTOR * height
422
423 self.add_sel_region(SelectableRegion(x - big_arrowhead_height / Canvas.SQRT3,
424 y, 2.0 * big_arrowhead_height / Canvas.SQRT3, height, event))
425
426 def draw_release_arrow_small(self, x, y, height, selected):
427 """Draws a small release arrow (most likely coming off the x-axis, although
428 this method doesn't enforce this): x, y should give the top of the arrow"""
429 small_arrowhead_height = GraphFormat.SMALL_ARROWHEAD_FACTOR * height
430
431 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected]
432
433 self.draw_line((x, y), (x - small_arrowhead_height, y + small_arrowhead_height), \
434 color, GraphFormat.BORDER_THICKNESS)
435 self.draw_line((x, y), (x + small_arrowhead_height, y + small_arrowhead_height), \
436 color, GraphFormat.BORDER_THICKNESS)
437 self.draw_line((x, y), (x, y + height), color, GraphFormat.BORDER_THICKNESS)
438
439 def add_sel_release_arrow_small(self, x, y, height, event):
440 self.add_sel_arrow_small(x, y, height, event)
441
442 def draw_deadline_arrow_small(self, x, y, height, selected):
443 """Draws a small deadline arrow (most likely coming off the x-axis, although
444 this method doesn't enforce this): x, y should give the top of the arrow"""
445 small_arrowhead_height = GraphFormat.SMALL_ARROWHEAD_FACTOR * height
446
447 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected]
448
449 self.draw_line((x, y), (x, y + height), color, GraphFormat.BORDER_THICKNESS)
450 self.draw_line((x - small_arrowhead_height, y + height - small_arrowhead_height), \
451 (x, y + height), color, GraphFormat.BORDER_THICKNESS)
452 self.draw_line((x + small_arrowhead_height, y + height - small_arrowhead_height), \
453 (x, y + height), color, GraphFormat.BORDER_THICKNESS)
454
455 def add_sel_deadline_arrow_small(self, x, y, height, event):
456 self.add_sel_arrow_small(x, y, height, event)
457
458 def add_sel_arrow_small(self, x, y, height, event):
459 small_arrowhead_height = GraphFormat.SMALL_ARROWHEAD_FACTOR * height
460
461 self.add_sel_region(SelectableRegion(x - small_arrowhead_height, y,
462 small_arrowhead_height * 2.0, height, event))
463
464 def draw_suspend_triangle(self, x, y, height, selected):
465 """Draws the triangle that marks a suspension. (x, y) gives the topmost (northernmost) point
466 of the symbol."""
467
468 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected]
469 colors = [(0.0, 0.0, 0.0), color]
470
471 draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline]
472 for i in range(0, 2):
473 color = colors[i]
474 draw_func = draw_funcs[i]
475 draw_func(self, [(x, y), (x + height / 2.0, y + height / 2.0), (x, y + height), (x, y)], \
476 color, GraphFormat.BORDER_THICKNESS)
477
478 def add_sel_suspend_triangle(self, x, y, height, event):
479 self.add_sel_region(SelectableRegion(x, y, height / 2.0, height, event))
480
481 def draw_resume_triangle(self, x, y, height, selected):
482 """Draws the triangle that marks a resumption. (x, y) gives the topmost (northernmost) point
483 of the symbol."""
484
485 color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected]
486 colors = [(1.0, 1.0, 1.0), color]
487
488 draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline]
489 for i in range(0, 2):
490 color = colors[i]
491 draw_func = draw_funcs[i]
492 draw_func(self, [(x, y), (x - height / 2.0, y + height / 2.0), (x, y + height), (x, y)], \
493 color, GraphFormat.BORDER_THICKNESS)
494
495 def add_sel_resume_triangle(self, x, y, height, event):
496 self.add_sel_region(SelectableRegion(x - height / 2.0, y, height / 2.0, height, event))
497
498 def clear_selectable_regions(self, real_x, real_y, width, height):
499 x = real_x + self.surface.virt_x
500 y = real_y + self.surface.virt_y
501 for event in self.selectable_regions.keys():
502 if self.selectable_regions[event].intersects(x, y, width, height):
503 del self.selectable_regions[event]
504
505 def add_sel_region(self, region):
506 self.selectable_regions[region.get_event()] = region
507
508 def get_sel_region(self, event):
509 return self.selectable_regions[event]
510
511 def get_selected_regions(self, real_x, real_y, width, height):
512 x = real_x + self.surface.virt_x
513 y = real_y + self.surface.virt_y
514
515 selected = {}
516 for event in self.selectable_regions:
517 region = self.selectable_regions[event]
518 if region.intersects(x, y, width, height):
519 selected[event] = region
520
521 return selected
522
523 def whiteout(self, real_x, real_y, width, height):
524 """Overwrites the surface completely white, but technically doesn't delete anything"""
525 self.fill_rect(self.surface.virt_x + real_x, self.surface.virt_y + real_y, width,
526 height, (1.0, 1.0, 1.0), False)
527
528 def get_item_color(self, n):
529 """Gets the nth color in the item color list, which are the colors used to draw the items
530 on the y-axis. Note that there are conceptually infinitely
531 many patterns because the patterns repeat -- that is, we just mod out by the size of the pattern
532 list when indexing."""
533 return self.item_clist[n % len(self.item_clist)]
534
535 def get_bar_pattern(self, n):
536 """Gets the nth pattern in the bar pattern list, which is a list of surfaces that are used to
537 fill in the bars. Note that there are conceptually infinitely
538 many patterns because the patterns repeat -- that is, we just mod out by the size of the pattern
539 list when indexing."""
540 if n < 0:
541 return self.bar_plist[-1]
542 return self.bar_plist[n % (len(self.bar_plist) - 1)]
543
544class CairoCanvas(Canvas):
545 """This is a basic class that stores and draws on a Cairo surface,
546 using various primitives related to drawing a real-time graph (up-arrows,
547 down-arrows, bars, ...).
548
549 This is the lowest-level non-abstract representation
550 (aside perhaps from the Cairo surface itself) of a real-time graph.
551 It allows the user to draw primitives at certain locations, but for
552 the most part does not know anything about real-time scheduling,
553 just how to draw the basic parts that make up a schedule graph.
554 For that, see Graph or its descendants."""
555
556 #def __init__(self, fname, width, height, item_clist, bar_plist, surface):
557 # """Creates a new Canvas of dimensions (width, height). The
558 # parameters ``item_plist'' and ``bar_plist'' each specify a list
559 # of patterns to choose from when drawing the items on the y-axis
560 # or filling in bars, respectively."""
561
562 # super(CairoCanvas, self).__init__(fname, width, height, item_clist, bar_plist, surface)
563
564 #def clear(self):
565 # self.surface = self.SurfaceType(self.width, self.height, self.fname)
566 # self.whiteout()
567
568 def get_surface(self):
569 """Gets the Surface that we are drawing on in its current state."""
570 return self.surface
571
572 def _rect_common(self, x, y, width, height, color, thickness, do_snap=True):
573 x, y, width, height = self.scaled(x, y, width, height)
574 x, y = self.surface.get_real_coor(x, y)
575 if do_snap:
576 self.surface.ctx.rectangle(snap(x), snap(y), width, height)
577 else:
578 self.surface.ctx.rectangle(x, y, width, height)
579
580 self.surface.ctx.set_line_width(thickness * self.scale)
581 self.surface.ctx.set_source_rgb(color[0], color[1], color[2])
582
583 def draw_rect(self, x, y, width, height, color, thickness, do_snap=True):
584 self._rect_common(x, y, width, height, color, thickness, do_snap)
585 self.surface.ctx.stroke()
586
587 def fill_rect(self, x, y, width, height, color, do_snap=True):
588 self._rect_common(x, y, width, height, color, 1, do_snap)
589 self.surface.ctx.fill()
590
591 def fill_rect_fade(self, x, y, width, height, lcolor, rcolor, do_snap=True):
592 """Draws a rectangle somewhere, filled in with the fade."""
593 x, y, width, height = self.scaled(x, y, width, height)
594 x, y = self.surface.get_real_coor(x, y)
595
596 if do_snap:
597 linear = cairo.LinearGradient(snap(x), snap(y), \
598 snap(x + width), snap(y + height))
599 else:
600 linear = cairo.LinearGradient(x, y, \
601 x + width, y + height)
602 linear.add_color_stop_rgb(0.0, lcolor[0], lcolor[1], lcolor[2])
603 linear.add_color_stop_rgb(1.0, rcolor[0], rcolor[1], rcolor[2])
604 self.surface.ctx.set_source(linear)
605 if do_snap:
606 self.surface.ctx.rectangle(snap(x), snap(y), width, height)
607 else:
608 self.surface.ctx.rectangle(snap(x), snap(y), width, height)
609 self.surface.ctx.fill()
610
611 def draw_line(self, p0, p1, color, thickness, do_snap=True):
612 """Draws a line from p0 to p1 with a certain color and thickness."""
613 p0 = self.scaled(p0[0], p0[1])
614 p0 = self.surface.get_real_coor(p0[0], p0[1])
615 p1 = self.scaled(p1[0], p1[1])
616 p1 = self.surface.get_real_coor(p1[0], p1[1])
617 if do_snap:
618 p0 = (snap(p0[0]), snap(p0[1]))
619 p1 = (snap(p1[0]), snap(p1[1]))
620
621 self.surface.ctx.move_to(p0[0], p0[1])
622 self.surface.ctx.line_to(p1[0], p1[1])
623 self.surface.ctx.set_source_rgb(color[0], color[1], color[2])
624 self.surface.ctx.set_line_width(thickness * self.scale)
625 self.surface.ctx.stroke()
626
627 def _polyline_common(self, coor_list, color, thickness, do_snap=True):
628 real_coor_list = [self.surface.get_real_coor(coor[0], coor[1]) for coor in coor_list]
629 self.surface.ctx.move_to(real_coor_list[0][0], real_coor_list[0][1])
630 if do_snap:
631 for i in range(0, len(real_coor_list)):
632 real_coor_list[i] = (snap(real_coor_list[i][0]), snap(real_coor_list[i][1]))
633
634 for coor in real_coor_list[1:]:
635 self.surface.ctx.line_to(coor[0], coor[1])
636
637 self.surface.ctx.set_line_width(thickness)
638 self.surface.ctx.set_source_rgb(color[0], color[1], color[2])
639
640 def draw_polyline(self, coor_list, color, thickness, do_snap=True):
641 self._polyline_common(coor_list, color, thickness, do_snap)
642 self.surface.ctx.stroke()
643
644 def fill_polyline(self, coor_list, color, thickness, do_snap=True):
645 self._polyline_common(coor_list, color, thickness, do_snap)
646 self.surface.ctx.fill()
647
648 def _draw_label_common(self, text, x, y, fopts, x_bearing_factor, \
649 f_descent_factor, width_factor, f_height_factor, do_snap=True):
650 """Helper function for drawing a label with some alignment. Instead of taking in an alignment,
651 it takes in the scale factor for the font extent parameters, which give the raw data of how much to adjust
652 the x and y parameters. Only should be used internally."""
653 x, y = self.scaled(x, y)
654 x, y = self.surface.get_real_coor(x, y)
655
656 self.surface.ctx.set_source_rgb(0.0, 0.0, 0.0)
657
658 self.surface.ctx.select_font_face(fopts.name, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
659 self.surface.ctx.set_font_size(fopts.size)
660
661 fe = self.surface.ctx.font_extents()
662 f_ascent, f_descent, f_height = fe[:3]
663
664 te = self.surface.ctx.text_extents(text)
665 x_bearing, y_bearing, width, height = te[:4]
666
667 actual_x = x - x_bearing * x_bearing_factor - width * width_factor
668 actual_y = y - f_descent * f_descent_factor + f_height * f_height_factor
669
670 self.surface.ctx.set_source_rgb(fopts.color[0], fopts.color[1], fopts.color[2])
671
672 if do_snap:
673 self.surface.ctx.move_to(snap(actual_x), snap(actual_y))
674 else:
675 self.surface.ctx.move_to(actual_x, actual_y)
676
677 self.surface.ctx.show_text(text)
678
679 def draw_label(self, text, x, y, fopts=GraphFormat.DEF_FOPTS_LABEL, halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, do_snap=True):
680 """Draws a label with the given parameters, with the given horizontal and vertical justification. One can override
681 the color from ``fopts'' by passing something in to ``pattern'', which overrides the color with an arbitrary
682 pattern."""
683 x_bearing_factor, f_descent_factor, width_factor, f_height_factor = 0.0, 0.0, 0.0, 0.0
684 halign_factors = {AlignMode.LEFT : (0.0, 0.0), AlignMode.CENTER : (1.0, 0.5), AlignMode.RIGHT : (1.0, 1.0)}
685 if halign not in halign_factors:
686 raise ValueError('Invalid alignment value')
687 x_bearing_factor, width_factor = halign_factors[halign]
688
689 valign_factors = {AlignMode.BOTTOM : (0.0, 0.0), AlignMode.CENTER : (1.0, 0.5), AlignMode.TOP : (1.0, 1.0)}
690 if valign not in valign_factors:
691 raise ValueError('Invalid alignment value')
692 f_descent_factor, f_height_factor = valign_factors[valign]
693
694 self._draw_label_common(text, x, y, fopts, x_bearing_factor, \
695 f_descent_factor, width_factor, f_height_factor, do_snap)
696
697 def draw_label_with_sscripts(self, text, supscript, subscript, x, y, \
698 textfopts=GraphFormat.DEF_FOPTS_LABEL, sscriptfopts=GraphFormat.DEF_FOPTS_LABEL_SSCRIPT, \
699 halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, do_snap=True):
700 """Draws a label, but also optionally allows a superscript and subscript to be rendered."""
701 self.draw_label(text, x, y, textfopts, halign, valign)
702
703 self.surface.ctx.set_source_rgb(0.0, 0.0, 0.0)
704 self.surface.ctx.select_font_face(textfopts.name, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
705 self.surface.ctx.set_font_size(textfopts.size)
706 te = self.surface.ctx.text_extents(text)
707 fe = self.surface.ctx.font_extents()
708 if supscript is not None:
709 f_height = fe[2]
710 x_advance = te[4]
711 xtmp = x + x_advance
712 ytmp = y
713 ytmp = y - f_height / 4.0
714 self.draw_label(supscript, xtmp, ytmp, sscriptfopts, halign, valign, do_snap)
715 if subscript is not None:
716 f_height = fe[2]
717 x_advance = te[4]
718 xtmp = x + x_advance
719 ytmp = y
720 ytmp = y + f_height / 4.0
721 self.draw_label(subscript, xtmp, ytmp, sscriptfopts, halign, valign, do_snap)
722
723# represents a selectable region of the graph
724class SelectableRegion(object):
725 def __init__(self, x, y, width, height, event):
726 self.x = x
727 self.y = y
728 self.width = width
729 self.height = height
730 self.event = event
731
732 def get_dimensions(self):
733 return (self.x, self.y, self.width, self.height)
734
735 def get_event(self):
736 return self.event
737
738 def intersects(self, x, y, width, height):
739 return x <= self.x + self.width and x + width >= self.x and y <= self.y + self.height and y + height >= self.y
740
741class Graph(object):
742 DEF_BAR_PLIST = [Pattern([(0.0, 0.9, 0.9)]), Pattern([(0.9, 0.3, 0.0)]), Pattern([(0.9, 0.7, 0.0)]),
743 Pattern([(0.0, 0.0, 0.8)]), Pattern([(0.0, 0.2, 0.9)]), Pattern([(0.0, 0.6, 0.6)]),
744 Pattern([(0.75, 0.75, 0.75)])]
745 DEF_ITEM_CLIST = [(0.3, 0.0, 0.0), (0.0, 0.3, 0.0), (0.0, 0.0, 0.3), (0.3, 0.3, 0.0), (0.0, 0.3, 0.3),
746 (0.3, 0.0, 0.3)]
747
748 def __init__(self, CanvasType, surface, start_time, end_time, y_item_list, attrs=GraphFormat(),
749 item_clist=DEF_ITEM_CLIST, bar_plist=DEF_BAR_PLIST):
750 # deal with possibly blank schedules
751 if start_time is None:
752 start_time = 0
753 if end_time is None:
754 end_time = 0
755
756 if start_time > end_time:
757 raise ValueError("Litmus is not a time machine")
758
759 self.attrs = attrs
760 self.start_time = start_time
761 self.end_time = end_time
762 self.y_item_list = y_item_list
763 self.num_maj = int(math.ceil((self.end_time - self.start_time) * 1.0 / self.attrs.time_per_maj)) + 1
764
765 width = self.num_maj * self.attrs.maj_sep + GraphFormat.X_AXIS_MEASURE_OFS + GraphFormat.WIDTH_PAD
766 height = (len(self.y_item_list) + 1) * self.attrs.y_item_size + GraphFormat.HEIGHT_PAD
767
768 # We need to stretch the width in order to fit the y-axis labels. To do this we need
769 # the extents information, but we haven't set up a surface yet, so we just use a
770 # temporary one.
771 extra_width = 0.0
772 dummy_surface = surface.__class__()
773 dummy_surface.renew(10, 10)
774
775 dummy_surface.ctx.select_font_face(self.attrs.item_fopts.name, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
776 dummy_surface.ctx.set_font_size(self.attrs.item_fopts.size)
777 for item in self.y_item_list:
778 dummy_surface.ctx.set_source_rgb(0.0, 0.0, 0.0)
779 te = dummy_surface.ctx.text_extents(item)
780 cur_width = te[2]
781 if cur_width > extra_width:
782 extra_width = cur_width
783
784 width += extra_width
785
786 self.origin = (extra_width + GraphFormat.WIDTH_PAD / 2.0, height - GraphFormat.HEIGHT_PAD / 2.0)
787
788 self.width = width
789 self.height = height
790
791 #if surface.ctx is None:
792 # surface.renew(width, height)
793
794 self.canvas = CanvasType(width, height, item_clist, bar_plist, surface)
795
796 def get_selected_regions(self, real_x, real_y, width, height):
797 return self.canvas.get_selected_regions(real_x, real_y, width, height)
798
799 def get_width(self):
800 return self.width
801
802 def get_height(self):
803 return self.height
804
805 def get_origin(self):
806 return self.origin
807
808 def get_attrs(self):
809 return self.attrs
810
811 def add_sel_region(self, region):
812 self.canvas.add_sel_region(region)
813
814 def get_sel_region(self, event):
815 return self.canvas.get_sel_region(event)
816
817 def update_view(self, x, y, width, height, ctx):
818 """Proxy into the surface's pan."""
819 self.canvas.surface.pan(x, y, width, height)
820 self.canvas.surface.change_ctx(ctx)
821
822 def _recomp_min_max(self, start_time, end_time, start_item, end_item):
823 if self.min_time is None or start_time < self.min_time:
824 self.min_time = start_time
825 if self.max_time is None or end_time > self.max_time:
826 self.max_time = end_time
827 if self.min_item is None or start_item < self.min_item:
828 self.min_item = start_item
829 if self.max_item is None or end_item > self.max_item:
830 self.max_item = end_item
831
832 def _get_time_xpos(self, time):
833 """get x so that x is at instant ``time'' on the graph"""
834 return self.origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + 1.0 * (time - self.start_time) / self.attrs.time_per_maj * self.attrs.maj_sep
835
836 def _get_item_ypos(self, item_no):
837 """get y so that y is where the top of a bar would be in item #n's area"""
838 return self.origin[1] - self._get_y_axis_height() + self.attrs.y_item_size * (item_no + 0.5 - GraphFormat.BAR_SIZE_FACTOR / 2.0)
839
840 def _get_bar_width(self, start_time, end_time):
841 return 1.0 * (end_time - start_time) / self.attrs.time_per_maj * self.attrs.maj_sep
842
843 def _get_bar_height(self):
844 return self.attrs.y_item_size * GraphFormat.BAR_SIZE_FACTOR
845
846 def _get_mini_bar_height(self):
847 return self.attrs.y_item_size * GraphFormat.MINI_BAR_SIZE_FACTOR
848
849 def _get_mini_bar_ofs(self):
850 return self.attrs.y_item_size * (GraphFormat.MINI_BAR_SIZE_FACTOR + GraphFormat.BAR_MINI_BAR_GAP_FACTOR)
851
852 def _get_y_axis_height(self):
853 return (len(self.y_item_list) + 1) * self.attrs.y_item_size
854
855 def _get_bottom_tick(self, time):
856 return int(math.floor((time - self.start_time) / self.attrs.time_per_maj))
857
858 def _get_top_tick(self, time):
859 return int(math.ceil((time - self.start_time) / self.attrs.time_per_maj))
860
861 def get_surface(self):
862 """Gets the underlying surface."""
863 return self.canvas.get_surface()
864
865 def xcoor_to_time(self, x):
866 #x = self.origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + (time - self.start) / self.attrs.time_per_maj * self.attrs.maj_sep
867 return (x - self.origin[0] - GraphFormat.X_AXIS_MEASURE_OFS) / self.attrs.maj_sep \
868 * self.attrs.time_per_maj + self.start_time
869
870 def ycoor_to_item_no(self, y):
871 return int((y - self.origin[1] + self._get_y_axis_height()) // self.attrs.y_item_size)
872
873 def get_offset_params(self, real_x, real_y, width, height):
874 start_time = self.xcoor_to_time(self.canvas.surface.virt_x + real_x)
875 end_time = self.xcoor_to_time(self.canvas.surface.virt_x + real_x + width)
876
877 start_item = self.ycoor_to_item_no(self.canvas.surface.virt_y + real_y)
878 end_item = 2 + self.ycoor_to_item_no(self.canvas.surface.virt_y + real_y + height)
879
880 return (start_time, end_time, start_item, end_item)
881
882 def draw_skeleton(self, start_time, end_time, start_item, end_item):
883 self.draw_grid_at_time(start_time, end_time, start_item, end_item)
884 self.draw_x_axis_with_labels_at_time(start_time, end_time)
885 self.draw_y_axis_with_labels()
886
887 def render_surface(self, sched, regions, selectable=False):
888 raise NotImplementedError
889
890 def render_all(self, schedule):
891 raise NotImplementedError
892
893 def render_events(self, event_list):
894 for layer in Canvas.LAYERS:
895 prev_events = {}
896 for event in event_list:
897 event.render(self, layer, prev_events)
898
899 def draw_axes(self, x_axis_label, y_axis_label):
900 """Draws and labels the axes according to the parameters that we were initialized
901 with."""
902 self.draw_grid_at_time(self.start_time, self.end_time, 0, len(self.attrs.y_item_list) - 1)
903
904 self.canvas.draw_x_axis(self.origin[0], self.origin[1], self.num_maj, self.attrs.maj_sep, self.attrs.min_per_maj)
905 self.canvas.draw_y_axis(self.origin[0], self.origin[1], self._get_y_axis_height())
906 self.canvas.draw_x_axis_labels(self.origin[0], self.origin[1], 0, self.num_maj - 1,\
907 self.attrs.maj_sep, self.attrs.min_per_maj, self.start_time, \
908 self.attrs.time_per_maj, self.attrs.show_min, self.attrs.majfopts, self.attrs.minfopts)
909 self.canvas.draw_y_axis_labels(self.origin[0], self.origin[1], self._get_y_axis_height(), self.y_item_list, \
910 self.attrs.y_item_size, self.attrs.item_fopts)
911
912 def draw_grid_at_time(self, start_time, end_time, start_item, end_item):
913 """Draws the grid, but only in a certain time and item range."""
914 start_tick = max(0, self._get_bottom_tick(start_time))
915 end_tick = min(self.num_maj - 1, self._get_top_tick(end_time))
916
917 start_item = max(0, start_item)
918 end_item = min(len(self.y_item_list), end_item)
919
920 self.canvas.draw_grid(self.origin[0], self.origin[1], self._get_y_axis_height(),
921 start_tick, end_tick, start_item, end_item, self.attrs.maj_sep, self.attrs.y_item_size, \
922 self.attrs.min_per_maj, True)
923
924 def draw_x_axis_with_labels_at_time(self, start_time, end_time):
925 start_tick = max(0, self._get_bottom_tick(start_time))
926 end_tick = min(self.num_maj - 1, self._get_top_tick(end_time))
927
928 self.canvas.draw_x_axis(self.origin[0], self.origin[1], start_tick, end_tick, \
929 self.attrs.maj_sep, self.attrs.min_per_maj)
930 self.canvas.draw_x_axis_labels(self.origin[0], self.origin[1], start_tick, \
931 end_tick, self.attrs.maj_sep, self.attrs.min_per_maj,
932 self.start_time + start_tick * self.attrs.time_per_maj,
933 self.attrs.time_per_maj, False)
934
935 def draw_y_axis_with_labels(self):
936 self.canvas.draw_y_axis(self.origin[0], self.origin[1], self._get_y_axis_height())
937 self.canvas.draw_y_axis_labels(self.origin[0], self.origin[1], self._get_y_axis_height(), \
938 self.y_item_list, self.attrs.y_item_size)
939
940 def draw_suspend_triangle_at_time(self, time, task_no, cpu_no, selected=False):
941 """Draws a suspension symbol for a certain task at an instant in time."""
942 raise NotImplementedError
943
944 def add_sel_suspend_triangle_at_time(self, time, task_no, cpu_no, event):
945 """Same as above, except instead of drawing adds a selectable region at
946 a certain time."""
947 raise NotImplementedError
948
949 def draw_resume_triangle_at_time(self, time, task_no, cpu_no, selected=False):
950 """Draws a resumption symbol for a certain task at an instant in time."""
951 raise NotImplementedError
952
953 def add_sel_resume_triangle_at_time(self, time, task_no, cpu_no, event):
954 """Same as above, except instead of drawing adds a selectable region at
955 a certain time."""
956 raise NotImplementedError
957
958 def draw_completion_marker_at_time(self, time, task_no, cpu_no, selected=False):
959 """Draws a completion marker for a certain task at an instant in time."""
960 raise NotImplementedError
961
962 def add_sel_completion_marker_at_time(self, time, task_no, cpu_no, event):
963 """Same as above, except instead of drawing adds a selectable region at
964 a certain time."""
965 raise NotImplementedError
966
967 def draw_release_arrow_at_time(self, time, task_no, job_no, selected=False):
968 """Draws a release arrow at a certain time for some task and job"""
969 raise NotImplementedError
970
971 def add_sel_release_arrow_at_time(self, time, task_no, event):
972 """Same as above, except instead of drawing adds a selectable region at
973 a certain time."""
974 raise NotImplementedError
975
976 def draw_deadline_arrow_at_time(self, time, task_no, job_no, selected=False):
977 """Draws a deadline arrow at a certain time for some task and job"""
978 raise NotImplementedError
979
980 def add_sel_deadline_arrow_at_time(self, time, task_no, event):
981 """Same as above, except instead of drawing adds a selectable region at
982 a certain time."""
983 raise NotImplementedError
984
985 def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None):
986 """Draws a bar over a certain time period for some task, optionally labelling it."""
987 raise NotImplementedError
988
989 def add_sel_bar_at_time(self, start_time, end_time, task_no, cpu_no, event):
990 """Same as above, except instead of drawing adds a selectable region at
991 a certain time."""
992 raise NotImplementedError
993
994 def draw_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, clip_side=None, job_no=None):
995 """Draws a mini bar over a certain time period for some task, optionally labelling it."""
996 raise NotImplementedError
997
998 def add_sel_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, event):
999 """Same as above, except instead of drawing adds a selectable region at
1000 a certain time."""
1001 raise NotImplementedError
1002
1003class TaskGraph(Graph):
1004 def render_surface(self, sched, regions, selectable=False):
1005 events_to_render = {}
1006 for layer in Canvas.LAYERS:
1007 events_to_render[layer] = {}
1008
1009 for region in regions:
1010 x, y, width, height = region
1011 if not selectable:
1012 self.canvas.whiteout(x, y, width, height)
1013 else:
1014 self.canvas.clear_selectable_regions(x, y, width, height)
1015
1016 self.min_time, self.max_time, self.min_item, self.max_item = None, None, None, None
1017 for region in regions:
1018 x, y, width, height = region
1019 start_time, end_time, start_item, end_item = self.get_offset_params(x, y, width, height)
1020 self._recomp_min_max(start_time, end_time, start_item, end_item)
1021
1022 for event in sched.get_time_slot_array().iter_over_period(
1023 start_time, end_time, start_item, end_item,
1024 schedule.TimeSlotArray.TASK_LIST, schedule.EVENT_LIST):
1025 events_to_render[event.get_layer()][event] = None
1026
1027 if not selectable:
1028 self.draw_skeleton(self.min_time, self.max_time,
1029 self.min_item, self.max_item)
1030
1031 #if not selectable:
1032 # for layer in events_to_render:
1033 # print 'task render on layer', layer, ':', [str(e) for e in events_to_render[layer].keys()]
1034
1035 for layer in Canvas.LAYERS:
1036 prev_events = {}
1037 for event in events_to_render[layer]:
1038 event.render(self, layer, prev_events, selectable)
1039
1040 def draw_suspend_triangle_at_time(self, time, task_no, cpu_no, selected=False):
1041 height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR
1042 x = self._get_time_xpos(time)
1043 y = self._get_item_ypos(task_no) + self._get_bar_height() / 2.0 - height / 2.0
1044 self.canvas.draw_suspend_triangle(x, y, height, selected)
1045
1046 def add_sel_suspend_triangle_at_time(self, time, task_no, cpu_no, event):
1047 height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR
1048 x = self._get_time_xpos(time)
1049 y = self._get_item_ypos(task_no) + self._get_bar_height() / 2.0 - height / 2.0
1050
1051 self.canvas.add_sel_suspend_triangle(x, y, height, event)
1052
1053 def draw_resume_triangle_at_time(self, time, task_no, cpu_no, selected=False):
1054 height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR
1055 x = self._get_time_xpos(time)
1056 y = self._get_item_ypos(task_no) + self._get_bar_height() / 2.0 - height / 2.0
1057
1058 self.canvas.draw_resume_triangle(x, y, height, selected)
1059
1060 def add_sel_resume_triangle_at_time(self, time, task_no, cpu_no, event):
1061 height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR
1062 x = self._get_time_xpos(time)
1063 y = self._get_item_ypos(task_no) + self._get_bar_height() / 2.0 - height / 2.0
1064
1065 self.canvas.add_sel_resume_triangle(x, y, height, event)
1066
1067 def draw_completion_marker_at_time(self, time, task_no, cpu_no, selected=False):
1068 height = self._get_bar_height() * GraphFormat.COMPLETION_MARKER_FACTOR
1069 x = self._get_time_xpos(time)
1070 y = self._get_item_ypos(task_no) + self._get_bar_height() - height
1071
1072 self.canvas.draw_completion_marker(x, y, height, selected)
1073
1074 def add_sel_completion_marker_at_time(self, time, task_no, cpu_no, event):
1075 height = self._get_bar_height() * GraphFormat.COMPLETION_MARKER_FACTOR
1076
1077 x = self._get_time_xpos(time)
1078 y = self._get_item_ypos(task_no) + self._get_bar_height() - height
1079
1080 self.canvas.add_sel_completion_marker(x, y, height, event)
1081
1082 def draw_release_arrow_at_time(self, time, task_no, job_no=None, selected=False):
1083 height = self._get_bar_height() * GraphFormat.BIG_ARROW_FACTOR
1084
1085 x = self._get_time_xpos(time)
1086 y = self._get_item_ypos(task_no) + self._get_bar_height() - height
1087
1088 self.canvas.draw_release_arrow_big(x, y, height, selected)
1089
1090 def add_sel_release_arrow_at_time(self, time, task_no, event):
1091 height = self._get_bar_height() * GraphFormat.BIG_ARROW_FACTOR
1092
1093 x = self._get_time_xpos(time)
1094 y = self._get_item_ypos(task_no) + self._get_bar_height() - height
1095
1096 self.canvas.add_sel_release_arrow_big(x, y, height, event)
1097
1098 def draw_deadline_arrow_at_time(self, time, task_no, job_no=None, selected=False):
1099 height = self._get_bar_height() * GraphFormat.BIG_ARROW_FACTOR
1100
1101 x = self._get_time_xpos(time)
1102 y = self._get_item_ypos(task_no)
1103
1104 self.canvas.draw_deadline_arrow_big(x, y, height, selected)
1105
1106 def add_sel_deadline_arrow_at_time(self, time, task_no, event):
1107 height = self._get_bar_height() * GraphFormat.BIG_ARROW_FACTOR
1108
1109 x = self._get_time_xpos(time)
1110 y = self._get_item_ypos(task_no)
1111
1112 self.canvas.add_sel_deadline_arrow_big(x, y, height, event)
1113
1114 def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None, selected=False):
1115 if start_time > end_time:
1116 raise ValueError("Litmus is not a time machine")
1117
1118 x = self._get_time_xpos(start_time)
1119 y = self._get_item_ypos(task_no)
1120 width = self._get_bar_width(start_time, end_time)
1121 height = self._get_bar_height()
1122
1123 self.canvas.draw_bar(x, y, width, height, cpu_no, clip_side, selected)
1124
1125 # if a job number is specified, we want to draw a superscript and subscript for the task and job number, respectively
1126 if job_no is not None:
1127 x += GraphFormat.BAR_LABEL_OFS
1128 y += self.attrs.y_item_size * GraphFormat.BAR_SIZE_FACTOR / 2.0
1129 self.canvas.draw_label_with_sscripts('T', str(task_no), str(job_no), x, y, \
1130 GraphFormat.DEF_FOPTS_BAR, GraphFormat.DEF_FOPTS_BAR_SSCRIPT, AlignMode.LEFT, AlignMode.CENTER)
1131
1132 def add_sel_bar_at_time(self, start_time, end_time, task_no, cpu_no, event):
1133 if start_time > end_time:
1134 raise ValueError("Litmus is not a time machine")
1135
1136 x = self._get_time_xpos(start_time)
1137 y = self._get_item_ypos(task_no)
1138 width = self._get_bar_width(start_time, end_time)
1139 height = self._get_bar_height()
1140
1141 self.canvas.add_sel_bar(x, y, width, height, event)
1142
1143 def draw_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None, selected=False):
1144 if start_time > end_time:
1145 raise ValueError("Litmus is not a time machine")
1146
1147 x = self._get_time_xpos(start_time)
1148 y = self._get_item_ypos(task_no) - self._get_mini_bar_ofs()
1149 width = self._get_bar_width(start_time, end_time)
1150 height = self._get_mini_bar_height()
1151
1152 self.canvas.draw_mini_bar(x, y, width, height, Canvas.NULL_PATTERN, clip_side, selected)
1153
1154 if job_no is not None:
1155 x += GraphFormat.MINI_BAR_LABEL_OFS
1156 y += self.attrs.y_item_size * GraphFormat.MINI_BAR_SIZE_FACTOR / 2.0
1157 self.canvas.draw_label_with_sscripts('T', str(task_no), str(job_no), x, y, \
1158 GraphFormat.DEF_FOPTS_MINI_BAR, GraphFormat.DEF_FOPTS_MINI_BAR_SSCRIPT, AlignMode.LEFT, AlignMode.CENTER)
1159
1160 def add_sel_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, event):
1161 x = self._get_time_xpos(start_time)
1162 y = self._get_item_ypos(task_no) - self._get_mini_bar_ofs()
1163 width = self._get_bar_width(start_time, end_time)
1164 height = self._get_mini_bar_height()
1165
1166 self.canvas.add_sel_mini_bar(x, y, width, height, event)
1167
1168class CpuGraph(Graph):
1169 def render_surface(self, sched, regions, selectable=False):
1170 BOTTOM_EVENTS = [schedule.ReleaseEvent, schedule.DeadlineEvent, schedule.InversionStartEvent,
1171 schedule.InversionEndEvent, schedule.InversionDummy]
1172 TOP_EVENTS = [schedule.SuspendEvent, schedule.ResumeEvent, schedule.CompleteEvent,
1173 schedule.SwitchAwayEvent, schedule.SwitchToEvent, schedule.IsRunningDummy]
1174
1175 events_to_render = {}
1176 for layer in Canvas.LAYERS:
1177 events_to_render[layer] = {}
1178
1179 self.min_time, self.max_time, self.min_item, self.max_item = None, None, None, None
1180 for region in regions:
1181 x, y, width, height = region
1182 if not selectable:
1183 #self.canvas.whiteout(x, y, width, height)
1184 self.canvas.whiteout(0, 0, self.canvas.surface.width, self.canvas.surface.height)
1185 else:
1186 self.canvas.clear_selectable_regions(x, y, width, height)
1187
1188 for region in regions:
1189 x, y, width, height = region
1190 start_time, end_time, start_item, end_item = self.get_offset_params(x, y, width, height)
1191 self._recomp_min_max(start_time, end_time, start_item, end_item)
1192
1193 for event in sched.get_time_slot_array().iter_over_period(
1194 start_time, end_time, start_item, end_item,
1195 schedule.TimeSlotArray.CPU_LIST,
1196 TOP_EVENTS):
1197 events_to_render[event.get_layer()][event] = None
1198
1199 if end_item >= len(self.y_item_list):
1200 # we are far down enough that we should render the releases and deadlines and inversions,
1201 # which appear near the x-axis
1202 x, y, width, height = region
1203 for event in sched.get_time_slot_array().iter_over_period(
1204 start_time, end_time, 0, sched.get_num_cpus(),
1205 schedule.TimeSlotArray.CPU_LIST,
1206 BOTTOM_EVENTS):
1207 events_to_render[event.get_layer()][event] = None
1208
1209 if not selectable:
1210 self.draw_skeleton(self.min_time, self.max_time,
1211 self.min_item, self.max_item)
1212
1213 for layer in Canvas.LAYERS:
1214 prev_events = {}
1215 for event in events_to_render[layer]:
1216 event.render(self, layer, prev_events, selectable)
1217
1218 def render(self, schedule, start_time=None, end_time=None):
1219 if end_time < start_time:
1220 raise ValueError('start must be less than end')
1221
1222 if start_time is None:
1223 start_time = self.start
1224 if end_time is None:
1225 end_time = self.end
1226 start_slot = self.get_time_slot(start_time)
1227 end_slot = min(len(self.time_slots), self.get_time_slot(end_time) + 1)
1228
1229 for layer in Canvas.LAYERS:
1230 prev_events = {}
1231 for i in range(start_slot, end_slot):
1232 for event in self.time_slots[i]:
1233 event.render(graph, layer, prev_events)
1234
1235 def draw_suspend_triangle_at_time(self, time, task_no, cpu_no, selected=False):
1236 height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR
1237 x = self._get_time_xpos(time)
1238 y = self._get_item_ypos(cpu_no) + self._get_bar_height() / 2.0 - height / 2.0
1239 self.canvas.draw_suspend_triangle(x, y, height, selected)
1240
1241 def add_sel_suspend_triangle_at_time(self, time, task_no, cpu_no, event):
1242 height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR
1243 x = self._get_time_xpos(time)
1244 y = self._get_item_ypos(cpu_no) + self._get_bar_height() / 2.0 - height / 2.0
1245
1246 self.canvas.add_sel_suspend_triangle(x, y, height, event)
1247
1248 def draw_resume_triangle_at_time(self, time, task_no, cpu_no, selected=False):
1249 height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR
1250 x = self._get_time_xpos(time)
1251 y = self._get_item_ypos(cpu_no) + self._get_bar_height() / 2.0 - height / 2.0
1252
1253 self.canvas.draw_resume_triangle(x, y, height, selected)
1254
1255 def add_sel_resume_triangle_at_time(self, time, task_no, cpu_no, event):
1256 height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR
1257 x = self._get_time_xpos(time)
1258 y = self._get_item_ypos(cpu_no) + self._get_bar_height() / 2.0 - height / 2.0
1259
1260 self.canvas.add_sel_resume_triangle(x, y, height, event)
1261
1262 def draw_completion_marker_at_time(self, time, task_no, cpu_no, selected=False):
1263 height = self._get_bar_height() * GraphFormat.COMPLETION_MARKER_FACTOR
1264 x = self._get_time_xpos(time)
1265 y = self._get_item_ypos(cpu_no) + self._get_bar_height() - height
1266
1267 self.canvas.draw_completion_marker(x, y, height, selected)
1268
1269 def add_sel_completion_marker_at_time(self, time, task_no, cpu_no, event):
1270 height = self._get_bar_height() * GraphFormat.COMPLETION_MARKER_FACTOR
1271
1272 x = self._get_time_xpos(time)
1273 y = self._get_item_ypos(cpu_no) + self._get_bar_height() - height
1274
1275 self.canvas.add_sel_completion_marker(x, y, height, event)
1276
1277 def draw_release_arrow_at_time(self, time, task_no, job_no=None, selected=False):
1278 if job_no is None and task_no is not None:
1279 raise ValueError("Must specify a job number along with the task number")
1280
1281 height = self._get_bar_height() * GraphFormat.SMALL_ARROW_FACTOR
1282
1283 x = self._get_time_xpos(time)
1284 y = self.origin[1] - height
1285
1286 self.canvas.draw_release_arrow_small(x, y, height, selected)
1287
1288 if task_no is not None:
1289 y -= GraphFormat.ARROW_LABEL_OFS
1290 self.canvas.draw_label_with_sscripts('T', str(task_no), str(job_no), x, y, \
1291 GraphFormat.DEF_FOPTS_ARROW, GraphFormat.DEF_FOPTS_ARROW_SSCRIPT, \
1292 AlignMode.CENTER, AlignMode.BOTTOM)
1293
1294 def add_sel_release_arrow_at_time(self, time, task_no, event):
1295 height = self._get_bar_height() * GraphFormat.SMALL_ARROW_FACTOR
1296
1297 x = self._get_time_xpos(time)
1298 y = self.origin[1] - height
1299
1300 self.canvas.add_sel_release_arrow_small(x, y, height, event)
1301
1302 def draw_deadline_arrow_at_time(self, time, task_no, job_no=None, selected=False):
1303 if job_no is None and task_no is not None:
1304 raise ValueError("Must specify a job number along with the task number")
1305
1306 height = self._get_bar_height() * GraphFormat.SMALL_ARROW_FACTOR
1307
1308 x = self._get_time_xpos(time)
1309 y = self.origin[1] - height
1310
1311 self.canvas.draw_deadline_arrow_small(x, y, height, selected)
1312
1313 if task_no is not None:
1314 y -= GraphFormat.ARROW_LABEL_OFS
1315 self.canvas.draw_label_with_sscripts('T', str(task_no), str(job_no), x, y, \
1316 GraphFormat.DEF_FOPTS_ARROW, GraphFormat.DEF_FOPTS_ARROW_SSCRIPT, \
1317 AlignMode.CENTER, AlignMode.BOTTOM)
1318
1319 def add_sel_deadline_arrow_at_time(self, time, task_no, event):
1320 height = self._get_bar_height() * GraphFormat.SMALL_ARROW_FACTOR
1321
1322 x = self._get_time_xpos(time)
1323 y = self.origin[1] - height
1324
1325 self.canvas.add_sel_deadline_arrow_small(x, y, height, event)
1326
1327 def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None, selected=False):
1328 if start_time > end_time:
1329 raise ValueError("Litmus is not a time machine")
1330
1331 x = self._get_time_xpos(start_time)
1332 y = self._get_item_ypos(cpu_no)
1333 width = self._get_bar_width(start_time, end_time)
1334 height = self._get_bar_height()
1335
1336 self.canvas.draw_bar(x, y, width, height, task_no, clip_side, selected)
1337
1338 # if a job number is specified, we want to draw a superscript and subscript for the task and job number, respectively
1339 if job_no is not None:
1340 x += GraphFormat.BAR_LABEL_OFS
1341 y += self.attrs.y_item_size * GraphFormat.BAR_SIZE_FACTOR / 2.0
1342 self.canvas.draw_label_with_sscripts('T', str(task_no), str(job_no), x, y, \
1343 GraphFormat.DEF_FOPTS_BAR, GraphFormat.DEF_FOPTS_BAR_SSCRIPT, \
1344 AlignMode.LEFT, AlignMode.CENTER)
1345
1346 def add_sel_bar_at_time(self, start_time, end_time, task_no, cpu_no, event):
1347 x = self._get_time_xpos(start_time)
1348 y = self._get_item_ypos(cpu_no)
1349 width = self._get_bar_width(start_time, end_time)
1350 height = self._get_bar_height()
1351
1352 self.canvas.add_sel_bar(x, y, width, height, event)
1353
1354 def draw_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None, selected=False):
1355 if start_time > end_time:
1356 raise ValueError("Litmus is not a time machine")
1357
1358 x = self._get_time_xpos(start_time)
1359 y = self._get_item_ypos(len(self.y_item_list))
1360 width = self._get_bar_width(start_time, end_time)
1361 height = self._get_mini_bar_height()
1362
1363 self.canvas.draw_mini_bar(x, y, width, height, task_no, clip_side, selected)
1364
1365 if job_no is not None:
1366 x += GraphFormat.MINI_BAR_LABEL_OFS
1367 y += self.attrs.y_item_size * GraphFormat.MINI_BAR_SIZE_FACTOR / 2.0
1368 self.canvas.draw_label_with_sscripts('T', str(task_no), str(job_no), x, y, \
1369 GraphFormat.DEF_FOPTS_MINI_BAR, GraphFormat.DEF_FOPTS_MINI_BAR_SSCRIPT, AlignMode.LEFT, AlignMode.CENTER)
1370
1371 def add_sel_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, event):
1372 x = self._get_time_xpos(start_time)
1373 y = self._get_item_ypos(len(self.y_item_list))
1374 width = self._get_bar_width(start_time, end_time)
1375 height = self._get_mini_bar_height()
1376
1377 self.canvas.add_sel_mini_bar(x, y, width, height, event)