diff options
author | Gary Bressler <garybressler@nc.rr.com> | 2010-04-06 12:52:38 -0400 |
---|---|---|
committer | Gary Bressler <garybressler@nc.rr.com> | 2010-04-06 12:52:38 -0400 |
commit | 01abc8352aa2fd192678b4066b26ea749a203801 (patch) | |
tree | 5ada25feffcf02b3003f403bb4e3cc4e71b37a91 | |
parent | 38c18a7992a59774bfc281348c718c5f7db4c557 (diff) |
cleanup
-rw-r--r-- | unit_trace/viz/draw.py | 1377 |
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 | |||
3 | import math | ||
4 | import cairo | ||
5 | import os | ||
6 | import copy | ||
7 | |||
8 | import util | ||
9 | import schedule | ||
10 | from format import * | ||
11 | |||
12 | def 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 | |||
21 | class 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 | |||
63 | class 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 | |||
73 | class 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 | |||
86 | class 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 | |||
125 | class 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 | |||
544 | class 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 | ||
724 | class 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 | |||
741 | class 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 | |||
1003 | class 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 | |||
1168 | class 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) | ||