diff options
author | Gary Bressler <garybressler@nc.rr.com> | 2010-04-30 14:04:11 -0400 |
---|---|---|
committer | Gary Bressler <garybressler@nc.rr.com> | 2010-04-30 14:04:11 -0400 |
commit | c21dea9f97dd31ad8a915469edc01f44345a469e (patch) | |
tree | 9a66ff34dc8c90c695572038b443bc2fd05e8cbf /unit_trace | |
parent | 6cfc2cc3bb104ef81e4d9e70f63ab803949f9764 (diff) |
Fixes related to scrolling, along with the display of some extra information. Updated documentation.
Diffstat (limited to 'unit_trace')
-rw-r--r-- | unit_trace/viz/__init__.py | 2 | ||||
-rw-r--r-- | unit_trace/viz/canvas.py | 158 | ||||
-rw-r--r-- | unit_trace/viz/format.py | 7 | ||||
-rw-r--r-- | unit_trace/viz/graph.py | 135 | ||||
-rw-r--r-- | unit_trace/viz/schedule.py | 304 | ||||
-rw-r--r-- | unit_trace/viz/util.py | 75 | ||||
-rw-r--r-- | unit_trace/viz/viewer.py | 246 | ||||
-rw-r--r-- | unit_trace/viz/windows.py | 24 |
8 files changed, 586 insertions, 365 deletions
diff --git a/unit_trace/viz/__init__.py b/unit_trace/viz/__init__.py index 592f697..91093c1 100644 --- a/unit_trace/viz/__init__.py +++ b/unit_trace/viz/__init__.py | |||
@@ -18,7 +18,7 @@ except ImportError: | |||
18 | gobject.signal_new('set-scroll-adjustments', viewer.GraphArea, gobject.SIGNAL_RUN_FIRST, | 18 | gobject.signal_new('set-scroll-adjustments', viewer.GraphArea, gobject.SIGNAL_RUN_FIRST, |
19 | None, (gtk.Adjustment, gtk.Adjustment)) | 19 | None, (gtk.Adjustment, gtk.Adjustment)) |
20 | gobject.signal_new('update-event-description', viewer.GraphArea, gobject.SIGNAL_RUN_FIRST, | 20 | gobject.signal_new('update-event-description', viewer.GraphArea, gobject.SIGNAL_RUN_FIRST, |
21 | None, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)) | 21 | None, (gobject.TYPE_PYOBJECT,)) |
22 | gobject.signal_new('update-sel-changes', viewer.GraphArea, gobject.SIGNAL_RUN_FIRST, | 22 | gobject.signal_new('update-sel-changes', viewer.GraphArea, gobject.SIGNAL_RUN_FIRST, |
23 | None, (gobject.TYPE_PYOBJECT,)) | 23 | None, (gobject.TYPE_PYOBJECT,)) |
24 | gobject.signal_new('request-context-menu', viewer.GraphArea, gobject.SIGNAL_RUN_FIRST, | 24 | gobject.signal_new('request-context-menu', viewer.GraphArea, gobject.SIGNAL_RUN_FIRST, |
diff --git a/unit_trace/viz/canvas.py b/unit_trace/viz/canvas.py index 9ac0e8b..badebe9 100644 --- a/unit_trace/viz/canvas.py +++ b/unit_trace/viz/canvas.py | |||
@@ -47,7 +47,7 @@ class Surface(object): | |||
47 | self.virt_y = y | 47 | self.virt_y = y |
48 | self.width = width | 48 | self.width = width |
49 | self.height = height | 49 | self.height = height |
50 | 50 | ||
51 | def set_scale(self, scale): | 51 | def set_scale(self, scale): |
52 | """Sets the scale factor.""" | 52 | """Sets the scale factor.""" |
53 | self.scale = scale | 53 | self.scale = scale |
@@ -58,17 +58,21 @@ class Surface(object): | |||
58 | that we should draw to. Note that these might actually be outside the | 58 | that we should draw to. Note that these might actually be outside the |
59 | bounds of the surface, | 59 | bounds of the surface, |
60 | if we want something outside the surface's ``window''.""" | 60 | if we want something outside the surface's ``window''.""" |
61 | return (x - self.virt_x * self.scale, y - self.virt_y * self.scale) | 61 | return ((x - self.virt_x) * self.scale, (y - self.virt_y) * self.scale) |
62 | |||
63 | def get_real_dim(self, w, h): | ||
64 | """Translates virtual width and height dimensions to real width and height | ||
65 | dimensions.""" | ||
66 | return (w * self.scale, h * self.scale) | ||
62 | 67 | ||
63 | def get_virt_coor(self, x, y): | 68 | def get_virt_coor(self, x, y): |
64 | """Does the inverse of the last method.""" | 69 | """Does the inverse of the last method.""" |
65 | return (x + self.virt_x * self.scale, y + self.virt_y * self.scale) | ||
66 | |||
67 | def get_virt_coor_unscaled(self, x, y): | ||
68 | """Does the same, but removes the scale factor (i.e. behaves as if | ||
69 | the scale was 1.0 all along).""" | ||
70 | return (x / self.scale + self.virt_x, y / self.scale + self.virt_y) | 70 | return (x / self.scale + self.virt_x, y / self.scale + self.virt_y) |
71 | 71 | ||
72 | def get_virt_dim(self, w, h): | ||
73 | """Does the inverse of the last method.""" | ||
74 | return (w / self.scale, h / self.scale) | ||
75 | |||
72 | class SVGSurface(Surface): | 76 | class SVGSurface(Surface): |
73 | def renew(self, width, height): | 77 | def renew(self, width, height): |
74 | iwidth = int(math.ceil(width)) | 78 | iwidth = int(math.ceil(width)) |
@@ -77,7 +81,7 @@ class SVGSurface(Surface): | |||
77 | self.height = height | 81 | self.height = height |
78 | self.surface = cairo.SVGSurface(self.fname, iwidth, iheight) | 82 | self.surface = cairo.SVGSurface(self.fname, iwidth, iheight) |
79 | self.ctx = cairo.Context(self.surface) | 83 | self.ctx = cairo.Context(self.surface) |
80 | 84 | ||
81 | def write_out(self, fname): | 85 | def write_out(self, fname): |
82 | os.execl('cp', self.fname, fname) | 86 | os.execl('cp', self.fname, fname) |
83 | 87 | ||
@@ -89,7 +93,7 @@ class ImageSurface(Surface): | |||
89 | self.height = height | 93 | self.height = height |
90 | self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, iwidth, iheight) | 94 | self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, iwidth, iheight) |
91 | self.ctx = cairo.Context(self.surface) | 95 | self.ctx = cairo.Context(self.surface) |
92 | 96 | ||
93 | def write_out(self, fname): | 97 | def write_out(self, fname): |
94 | if self.surface is None: | 98 | if self.surface is None: |
95 | raise ValueError("Don't own surface, can't write to file") | 99 | raise ValueError("Don't own surface, can't write to file") |
@@ -98,6 +102,7 @@ class ImageSurface(Surface): | |||
98 | 102 | ||
99 | class Pattern(object): | 103 | class Pattern(object): |
100 | DEF_STRIPE_SIZE = 10 | 104 | DEF_STRIPE_SIZE = 10 |
105 | COVER_EXTRA = 1 | ||
101 | MAX_FADE_WIDTH = 250 | 106 | MAX_FADE_WIDTH = 250 |
102 | 107 | ||
103 | def __init__(self, color_list, stripe_size=DEF_STRIPE_SIZE): | 108 | def __init__(self, color_list, stripe_size=DEF_STRIPE_SIZE): |
@@ -106,7 +111,7 @@ class Pattern(object): | |||
106 | 111 | ||
107 | def get_color_list(self): | 112 | def get_color_list(self): |
108 | return self.color_list | 113 | return self.color_list |
109 | 114 | ||
110 | def render_on_canvas(self, canvas, x, y, width, height, fade=False): | 115 | def render_on_canvas(self, canvas, x, y, width, height, fade=False): |
111 | fade_span = min(width, Pattern.MAX_FADE_WIDTH) | 116 | fade_span = min(width, Pattern.MAX_FADE_WIDTH) |
112 | 117 | ||
@@ -118,7 +123,8 @@ class Pattern(object): | |||
118 | canvas.fill_rect(x, y, width, height, self.color_list[0]) | 123 | canvas.fill_rect(x, y, width, height, self.color_list[0]) |
119 | 124 | ||
120 | if width > Pattern.MAX_FADE_WIDTH: | 125 | if width > Pattern.MAX_FADE_WIDTH: |
121 | canvas.fill_rect(x + Pattern.MAX_FADE_WIDTH, y, width - Pattern.MAX_FADE_WIDTH, | 126 | canvas.fill_rect(x + Pattern.MAX_FADE_WIDTH - Pattern.COVER_EXTRA, y, |
127 | width - Pattern.MAX_FADE_WIDTH + Pattern.COVER_EXTRA, | ||
122 | height, self.color_list[0]) | 128 | height, self.color_list[0]) |
123 | else: | 129 | else: |
124 | n = 0 | 130 | n = 0 |
@@ -126,7 +132,7 @@ class Pattern(object): | |||
126 | while y < bottom: | 132 | while y < bottom: |
127 | i = n % len(self.color_list) | 133 | i = n % len(self.color_list) |
128 | if fade: | 134 | if fade: |
129 | canvas.fill_rect_fade(x, y, fade_span, \ | 135 | canvas.fill_rect_fade(x, y, fade_span, |
130 | min(self.stripe_size, bottom - y), (1.0, 1.0, 1.0), self.color_list[i]) | 136 | min(self.stripe_size, bottom - y), (1.0, 1.0, 1.0), self.color_list[i]) |
131 | else: | 137 | else: |
132 | canvas.fill_rect(x, y, width, min(self.stripe_size, bottom - y), self.color_list[i]) | 138 | canvas.fill_rect(x, y, width, min(self.stripe_size, bottom - y), self.color_list[i]) |
@@ -167,7 +173,7 @@ class Canvas(object): | |||
167 | 173 | ||
168 | self.surface = surface | 174 | self.surface = surface |
169 | self.def_surface = None | 175 | self.def_surface = None |
170 | 176 | ||
171 | self.width = int(math.ceil(width)) | 177 | self.width = int(math.ceil(width)) |
172 | self.height = int(math.ceil(height)) | 178 | self.height = int(math.ceil(height)) |
173 | self.item_clist = item_clist | 179 | self.item_clist = item_clist |
@@ -182,23 +188,20 @@ class Canvas(object): | |||
182 | or drawing to a file.""" | 188 | or drawing to a file.""" |
183 | if self.def_surface is not None: | 189 | if self.def_surface is not None: |
184 | raise ValueError | 190 | raise ValueError |
185 | 191 | ||
186 | self.def_surface = self.surface | 192 | self.def_surface = self.surface |
187 | self.surface = surface | 193 | self.surface = surface |
188 | 194 | ||
189 | def revert_surface(self): | 195 | def revert_surface(self): |
190 | """Goes back to the default surface; that is, the one used for | 196 | """Goes back to the default surface; that is, the one used for |
191 | drawing to the screen.""" | 197 | drawing to the screen.""" |
192 | if self.def_surface is not None: | 198 | if self.def_surface is not None: |
193 | self.surface = self.def_surface | 199 | self.surface = self.def_surface |
194 | self.def_surface = None | 200 | self.def_surface = None |
195 | 201 | ||
196 | def set_scale(self, scale): | 202 | def set_scale(self, scale): |
197 | self.scale = scale | 203 | self.scale = scale |
198 | self.surface.set_scale(scale) | 204 | self.surface.set_scale(scale) |
199 | for layer in self.selectable_regions: | ||
200 | for event in self.selectable_regions[layer]: | ||
201 | self.selectable_regions[layer][event].set_scale(scale) | ||
202 | 205 | ||
203 | def scaled(self, *coors): | 206 | def scaled(self, *coors): |
204 | """Scales a series of coordinates.""" | 207 | """Scales a series of coordinates.""" |
@@ -265,7 +268,7 @@ class Canvas(object): | |||
265 | self.draw_line((x, y), (x, y + GraphFormat.MAJ_TICK_SIZE), | 268 | self.draw_line((x, y), (x, y + GraphFormat.MAJ_TICK_SIZE), |
266 | (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS) | 269 | (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS) |
267 | 270 | ||
268 | if (i < end_tick): | 271 | if i < end_tick: |
269 | for j in range(0, min_per_maj): | 272 | for j in range(0, min_per_maj): |
270 | self.draw_line((x, y), (x + maj_sep / min_per_maj, y), | 273 | self.draw_line((x, y), (x + maj_sep / min_per_maj, y), |
271 | (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS) | 274 | (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS) |
@@ -276,7 +279,8 @@ class Canvas(object): | |||
276 | (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS) | 279 | (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS) |
277 | 280 | ||
278 | def draw_x_axis_labels(self, x, y, start_tick, end_tick, maj_sep, min_per_maj, start=0, incr=1, show_min=False, \ | 281 | def draw_x_axis_labels(self, x, y, start_tick, end_tick, maj_sep, min_per_maj, start=0, incr=1, show_min=False, \ |
279 | majfopts=GraphFormat.DEF_FOPTS_MAJ, minfopts=GraphFormat.DEF_FOPTS_MIN): | 282 | unit=GraphFormat.DEF_TIME_UNIT, majfopts=GraphFormat.DEF_FOPTS_MAJ, |
283 | minfopts=GraphFormat.DEF_FOPTS_MIN): | ||
280 | """Draws the labels for the x-axis. (x, y) should give the origin. | 284 | """Draws the labels for the x-axis. (x, y) should give the origin. |
281 | how far down you want the text. ``incr'' gives the increment per major | 285 | how far down you want the text. ``incr'' gives the increment per major |
282 | tick. ``start'' gives the value of the first tick. ``show_min'' specifies | 286 | tick. ``start'' gives the value of the first tick. ``show_min'' specifies |
@@ -288,9 +292,9 @@ class Canvas(object): | |||
288 | minincr = incr / (min_per_maj * 1.0) | 292 | minincr = incr / (min_per_maj * 1.0) |
289 | 293 | ||
290 | cur = start * 1.0 | 294 | cur = start * 1.0 |
291 | 295 | ||
292 | for i in range(start_tick, end_tick + 1): | 296 | for i in range(start_tick, end_tick + 1): |
293 | text = util.format_float(cur, 2) | 297 | text = util.format_float(unit.nsec_to_native(cur), 2) |
294 | self.draw_label(text, x, y, majfopts, AlignMode.CENTER, AlignMode.TOP) | 298 | self.draw_label(text, x, y, majfopts, AlignMode.CENTER, AlignMode.TOP) |
295 | 299 | ||
296 | if (i < end_tick): | 300 | if (i < end_tick): |
@@ -362,7 +366,7 @@ class Canvas(object): | |||
362 | y -= axis_height - item_size / 2.0 - item_size * item_no | 366 | y -= axis_height - item_size / 2.0 - item_size * item_no |
363 | 367 | ||
364 | orig_color = fopts.color | 368 | orig_color = fopts.color |
365 | 369 | ||
366 | if selected: | 370 | if selected: |
367 | fopts.color = GraphFormat.HIGHLIGHT_COLOR | 371 | fopts.color = GraphFormat.HIGHLIGHT_COLOR |
368 | else: | 372 | else: |
@@ -370,19 +374,19 @@ class Canvas(object): | |||
370 | self.draw_label(item, x, y, fopts, AlignMode.RIGHT, AlignMode.CENTER) | 374 | self.draw_label(item, x, y, fopts, AlignMode.RIGHT, AlignMode.CENTER) |
371 | 375 | ||
372 | fopts.color = orig_color | 376 | fopts.color = orig_color |
373 | 377 | ||
374 | def add_sel_y_axis_item(self, x, y, axis_height, item, item_no, item_size, event, fopts=None): | 378 | def add_sel_y_axis_item(self, x, y, axis_height, item, item_no, item_size, event, fopts=None): |
375 | if fopts is None: | 379 | if fopts is None: |
376 | fopts = GraphFormat.DEF_FOPTS_ITEM | 380 | fopts = GraphFormat.DEF_FOPTS_ITEM |
377 | 381 | ||
378 | x -= GraphFormat.Y_AXIS_ITEM_GAP | 382 | x -= GraphFormat.Y_AXIS_ITEM_GAP |
379 | y -= axis_height - item_size / 2.0 - item_size * item_no | 383 | y -= axis_height - item_size / 2.0 - item_size * item_no |
380 | 384 | ||
381 | x, y, width, height, f_height = self.get_label_dim(item, x, y, fopts, AlignMode.RIGHT, AlignMode.CENTER, False) | 385 | x, y, width, height, f_height = self.get_label_dim(item, x, y, fopts, AlignMode.RIGHT, AlignMode.CENTER, False) |
382 | #(x, y) gives bottom-left, not top-left | 386 | #(x, y) gives bottom-left, not top-left |
383 | y -= height | 387 | y -= height |
384 | self.add_sel_region(SelectableRegion(x, y, width, height, event)) | 388 | self.add_sel_region(SelectableRegion(x, y, width, height, event)) |
385 | 389 | ||
386 | def draw_top_item(self, x, y, axis_height, item, item_no, prev_items, selected, fopts=None): | 390 | def draw_top_item(self, x, y, axis_height, item, item_no, prev_items, selected, fopts=None): |
387 | """Draws the item labels at the top. ``item'' is the string to print, | 391 | """Draws the item labels at the top. ``item'' is the string to print, |
388 | while item_size gives the vertical amount of | 392 | while item_size gives the vertical amount of |
@@ -393,22 +397,22 @@ class Canvas(object): | |||
393 | fopts = GraphFormat.DEF_FOPTS_ITEM | 397 | fopts = GraphFormat.DEF_FOPTS_ITEM |
394 | 398 | ||
395 | y -= axis_height + GraphFormat.TOP_ITEM_VERT_GAP | 399 | y -= axis_height + GraphFormat.TOP_ITEM_VERT_GAP |
396 | 400 | ||
397 | for prev_item in prev_items: | 401 | for prev_item in prev_items: |
398 | x += GraphFormat.TOP_ITEM_HORIZ_GAP + self.get_label_dim(prev_item, x, y, fopts, | 402 | x += GraphFormat.TOP_ITEM_HORIZ_GAP + self.get_label_dim(prev_item, x, y, fopts, |
399 | AlignMode.LEFT, AlignMode.TOP)[2] | 403 | AlignMode.LEFT, AlignMode.TOP)[2] |
400 | 404 | ||
401 | orig_color = fopts.color | 405 | orig_color = fopts.color |
402 | 406 | ||
403 | if selected: | 407 | if selected: |
404 | fopts.color = GraphFormat.HIGHLIGHT_COLOR | 408 | fopts.color = GraphFormat.HIGHLIGHT_COLOR |
405 | else: | 409 | else: |
406 | fopts.color = self.get_bar_pattern(item_no).get_color_list()[0] | 410 | fopts.color = self.get_bar_pattern(item_no).get_color_list()[0] |
407 | 411 | ||
408 | self.draw_label(item, x, y, fopts, AlignMode.LEFT, AlignMode.BOTTOM) | 412 | self.draw_label(item, x, y, fopts, AlignMode.LEFT, AlignMode.BOTTOM) |
409 | 413 | ||
410 | fopts.color = orig_color | 414 | fopts.color = orig_color |
411 | 415 | ||
412 | def add_sel_top_item(self, x, y, axis_height, item, item_no, prev_items, event, fopts=None): | 416 | def add_sel_top_item(self, x, y, axis_height, item, item_no, prev_items, event, fopts=None): |
413 | """Draws the item labels at the top. ``item'' is the string to print, | 417 | """Draws the item labels at the top. ``item'' is the string to print, |
414 | while item_size gives the vertical amount of | 418 | while item_size gives the vertical amount of |
@@ -419,19 +423,19 @@ class Canvas(object): | |||
419 | fopts = GraphFormat.DEF_FOPTS_ITEM | 423 | fopts = GraphFormat.DEF_FOPTS_ITEM |
420 | 424 | ||
421 | y -= axis_height + GraphFormat.TOP_ITEM_VERT_GAP | 425 | y -= axis_height + GraphFormat.TOP_ITEM_VERT_GAP |
422 | 426 | ||
423 | for item in prev_items: | 427 | for item in prev_items: |
424 | x += GraphFormat.TOP_ITEM_HORIZ_GAP + self.get_label_dim(item, x, y, fopts, | 428 | x += GraphFormat.TOP_ITEM_HORIZ_GAP + self.get_label_dim(item, x, y, fopts, |
425 | AlignMode.LEFT, AlignMode.BOTTOM)[2] | 429 | AlignMode.LEFT, AlignMode.BOTTOM)[2] |
426 | 430 | ||
427 | x, y, width, height, f_height = self.get_label_dim(item, x, y, fopts, AlignMode.LEFT, AlignMode.BOTTOM, False) | 431 | x, y, width, height, f_height = self.get_label_dim(item, x, y, fopts, AlignMode.LEFT, AlignMode.BOTTOM, False) |
428 | #(x, y) gives bottom-left, not top-left | 432 | #(x, y) gives bottom-left, not top-left |
429 | y -= height | 433 | y -= height |
430 | self.add_sel_region(SelectableRegion(x, y, width, height, event)) | 434 | self.add_sel_region(SelectableRegion(x, y, width, height, event)) |
431 | 435 | ||
432 | def add_sel_dummy(self, event): | 436 | def add_sel_dummy(self, event): |
433 | self.add_sel_region(SelectableRegion(None, None, None, None, event)) | 437 | self.add_sel_region(SelectableRegion(None, None, None, None, event)) |
434 | 438 | ||
435 | def add_sel_bar(self, x, y, width, height, event): | 439 | def add_sel_bar(self, x, y, width, height, event): |
436 | self.add_sel_region(SelectableRegion(x, y, width, height, event)) | 440 | self.add_sel_region(SelectableRegion(x, y, width, height, event)) |
437 | 441 | ||
@@ -593,7 +597,6 @@ class Canvas(object): | |||
593 | self.selectable_regions = {} | 597 | self.selectable_regions = {} |
594 | 598 | ||
595 | def add_sel_region(self, region): | 599 | def add_sel_region(self, region): |
596 | region.set_scale(self.scale) | ||
597 | layer = region.get_event().get_layer() | 600 | layer = region.get_event().get_layer() |
598 | if layer not in self.selectable_regions: | 601 | if layer not in self.selectable_regions: |
599 | self.selectable_regions[layer] = {} | 602 | self.selectable_regions[layer] = {} |
@@ -606,9 +609,7 @@ class Canvas(object): | |||
606 | return event.get_layer() in self.selectable_regions \ | 609 | return event.get_layer() in self.selectable_regions \ |
607 | and event in self.selectable_regions[event.get_layer()] | 610 | and event in self.selectable_regions[event.get_layer()] |
608 | 611 | ||
609 | def get_intersecting_regions(self, real_x, real_y, width, height): | 612 | def get_intersecting_regions(self, x, y, width, height): |
610 | x, y = self.surface.get_virt_coor(real_x, real_y) | ||
611 | |||
612 | selected = {} | 613 | selected = {} |
613 | for layer in self.selectable_regions: | 614 | for layer in self.selectable_regions: |
614 | for event in self.selectable_regions[layer]: | 615 | for event in self.selectable_regions[layer]: |
@@ -624,23 +625,23 @@ class Canvas(object): | |||
624 | """Overwrites the surface completely white, but technically doesn't delete anything""" | 625 | """Overwrites the surface completely white, but technically doesn't delete anything""" |
625 | # Make sure we don't scale here (we want to literally white out just this region) | 626 | # Make sure we don't scale here (we want to literally white out just this region) |
626 | 627 | ||
627 | x, y = self.surface.get_virt_coor_unscaled(0, 0) | 628 | x, y = self.surface.get_virt_coor(0, 0) |
628 | width, height = self.unscaled(self.surface.width, self.surface.height) | 629 | width, height = self.unscaled(self.surface.width, self.surface.height) |
629 | 630 | ||
630 | self.fill_rect(x, y, width, height, (1.0, 1.0, 1.0), False) | 631 | self.fill_rect(x, y, width, height, (1.0, 1.0, 1.0), False) |
631 | 632 | ||
632 | def get_item_color(self, n): | 633 | def get_item_color(self, n): |
633 | return self.item_clist[n] | 634 | return self.item_clist[n] |
634 | 635 | ||
635 | def get_bar_pattern(self, n): | 636 | def get_bar_pattern(self, n): |
636 | return self.bar_plist[n] | 637 | return self.bar_plist[n] |
637 | 638 | ||
638 | def set_item_color(self, n, color): | 639 | def set_item_color(self, n, color): |
639 | self.item_clist[n] = color | 640 | self.item_clist[n] = color |
640 | 641 | ||
641 | def set_bar_pattern(self, n, pattern): | 642 | def set_bar_pattern(self, n, pattern): |
642 | self.bar_plist[n] = pattern | 643 | self.bar_plist[n] = pattern |
643 | 644 | ||
644 | class CairoCanvas(Canvas): | 645 | class CairoCanvas(Canvas): |
645 | """This is a basic class that stores and draws on a Cairo surface, | 646 | """This is a basic class that stores and draws on a Cairo surface, |
646 | using various primitives related to drawing a real-time graph (up-arrows, | 647 | using various primitives related to drawing a real-time graph (up-arrows, |
@@ -672,8 +673,8 @@ class CairoCanvas(Canvas): | |||
672 | def _rect_common(self, x, y, width, height, color, thickness, clip_side=None, do_snap=True): | 673 | def _rect_common(self, x, y, width, height, color, thickness, clip_side=None, do_snap=True): |
673 | EXTRA_FACTOR = 2.0 | 674 | EXTRA_FACTOR = 2.0 |
674 | 675 | ||
675 | x, y, width, height = self.scaled(x, y, width, height) | ||
676 | x, y = self.surface.get_real_coor(x, y) | 676 | x, y = self.surface.get_real_coor(x, y) |
677 | width, height = self.surface.get_real_dim(width, height) | ||
677 | max_width = self.surface.width + EXTRA_FACTOR * thickness | 678 | max_width = self.surface.width + EXTRA_FACTOR * thickness |
678 | max_height = self.surface.height + EXTRA_FACTOR * thickness | 679 | max_height = self.surface.height + EXTRA_FACTOR * thickness |
679 | 680 | ||
@@ -721,8 +722,8 @@ class CairoCanvas(Canvas): | |||
721 | 722 | ||
722 | def fill_rect_fade(self, x, y, width, height, lcolor, rcolor, do_snap=True): | 723 | def fill_rect_fade(self, x, y, width, height, lcolor, rcolor, do_snap=True): |
723 | """Draws a rectangle somewhere, filled in with the fade.""" | 724 | """Draws a rectangle somewhere, filled in with the fade.""" |
724 | x, y, width, height = self.scaled(x, y, width, height) | ||
725 | x, y = self.surface.get_real_coor(x, y) | 725 | x, y = self.surface.get_real_coor(x, y) |
726 | width, height = self.surface.get_real_dim(width, height) | ||
726 | 727 | ||
727 | if do_snap: | 728 | if do_snap: |
728 | linear = cairo.LinearGradient(snap(x), snap(y), \ | 729 | linear = cairo.LinearGradient(snap(x), snap(y), \ |
@@ -741,9 +742,7 @@ class CairoCanvas(Canvas): | |||
741 | 742 | ||
742 | def draw_line(self, p0, p1, color, thickness, do_snap=True): | 743 | def draw_line(self, p0, p1, color, thickness, do_snap=True): |
743 | """Draws a line from p0 to p1 with a certain color and thickness.""" | 744 | """Draws a line from p0 to p1 with a certain color and thickness.""" |
744 | p0 = self.scaled(p0[0], p0[1]) | ||
745 | p0 = self.surface.get_real_coor(p0[0], p0[1]) | 745 | p0 = self.surface.get_real_coor(p0[0], p0[1]) |
746 | p1 = self.scaled(p1[0], p1[1]) | ||
747 | p1 = self.surface.get_real_coor(p1[0], p1[1]) | 746 | p1 = self.surface.get_real_coor(p1[0], p1[1]) |
748 | if do_snap: | 747 | if do_snap: |
749 | p0 = (snap(p0[0]), snap(p0[1])) | 748 | p0 = (snap(p0[0]), snap(p0[1])) |
@@ -756,8 +755,7 @@ class CairoCanvas(Canvas): | |||
756 | self.surface.ctx.stroke() | 755 | self.surface.ctx.stroke() |
757 | 756 | ||
758 | def _polyline_common(self, coor_list, color, thickness, do_snap=True): | 757 | def _polyline_common(self, coor_list, color, thickness, do_snap=True): |
759 | scaled_coor_list = [self.scaled(coor[0], coor[1]) for coor in coor_list] | 758 | real_coor_list = [self.surface.get_real_coor(coor[0], coor[1]) for coor in coor_list] |
760 | real_coor_list = [self.surface.get_real_coor(coor[0], coor[1]) for coor in scaled_coor_list] | ||
761 | 759 | ||
762 | self.surface.ctx.move_to(real_coor_list[0][0], real_coor_list[0][1]) | 760 | self.surface.ctx.move_to(real_coor_list[0][0], real_coor_list[0][1]) |
763 | if do_snap: | 761 | if do_snap: |
@@ -780,7 +778,7 @@ class CairoCanvas(Canvas): | |||
780 | 778 | ||
781 | def _get_label_dim_common(self, text, x, y, fopts, x_bearing_factor, \ | 779 | def _get_label_dim_common(self, text, x, y, fopts, x_bearing_factor, \ |
782 | f_descent_factor, width_factor, f_height_factor, | 780 | f_descent_factor, width_factor, f_height_factor, |
783 | scale, do_snap=True): | 781 | do_snap=True): |
784 | """Helper function for drawing a label with some alignment. Instead of taking in an alignment, | 782 | """Helper function for drawing a label with some alignment. Instead of taking in an alignment, |
785 | it takes in the scale factor for the font extent parameters, which give the raw data of how much to adjust | 783 | it takes in the scale factor for the font extent parameters, which give the raw data of how much to adjust |
786 | the x and y parameters. Only should be used internally.""" | 784 | the x and y parameters. Only should be used internally.""" |
@@ -793,30 +791,30 @@ class CairoCanvas(Canvas): | |||
793 | surface.renew(10, 10) | 791 | surface.renew(10, 10) |
794 | else: | 792 | else: |
795 | surface = self.surface | 793 | surface = self.surface |
796 | 794 | ||
797 | surface.ctx.select_font_face(fopts.name, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) | 795 | surface.ctx.select_font_face(fopts.name, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) |
798 | surface.ctx.set_font_size(fopts.size * scale) | 796 | surface.ctx.set_font_size(fopts.size) |
799 | 797 | ||
800 | fe = surface.ctx.font_extents() | 798 | fe = surface.ctx.font_extents() |
801 | f_ascent, f_descent, f_height = fe[:3] | 799 | f_ascent, f_descent, f_height = fe[:3] |
802 | 800 | ||
803 | te = surface.ctx.text_extents(text) | 801 | te = surface.ctx.text_extents(text) |
804 | x_bearing, y_bearing, width, height = te[:4] | 802 | x_bearing, y_bearing, width, height = te[:4] |
805 | 803 | ||
806 | actual_x = x - x_bearing * x_bearing_factor - width * width_factor | 804 | actual_x = x - x_bearing * x_bearing_factor - width * width_factor |
807 | actual_y = y - f_descent * f_descent_factor + f_height * f_height_factor | 805 | actual_y = y - f_descent * f_descent_factor + f_height * f_height_factor |
808 | 806 | ||
809 | if do_snap: | 807 | if do_snap: |
810 | actual_x = snap(actual_x) | 808 | actual_x = snap(actual_x) |
811 | actual_y = snap(actual_y) | 809 | actual_y = snap(actual_y) |
812 | 810 | ||
813 | return actual_x, actual_y, width, height, f_height | 811 | return actual_x, actual_y, width, height, f_height |
814 | 812 | ||
815 | def get_label_dim(self, text, x, y, fopts=GraphFormat.DEF_FOPTS_LABEL, halign=AlignMode.LEFT, | 813 | def get_label_dim(self, text, x, y, fopts=GraphFormat.DEF_FOPTS_LABEL, halign=AlignMode.LEFT, |
816 | valign=AlignMode.BOTTOM, do_scale=True, do_snap=True): | 814 | valign=AlignMode.BOTTOM, do_snap=True): |
817 | """Gets the dimensions of the label if it were to be drawn with the given parameters, and with the given | 815 | """Gets the dimensions of the label if it were to be drawn with the given parameters, and with the given |
818 | justification. Note: this does everything except actually draw the label""" | 816 | justification. Note: this does everything except actually draw the label""" |
819 | 817 | ||
820 | x_bearing_factor, f_descent_factor, width_factor, f_height_factor = 0.0, 0.0, 0.0, 0.0 | 818 | x_bearing_factor, f_descent_factor, width_factor, f_height_factor = 0.0, 0.0, 0.0, 0.0 |
821 | halign_factors = {AlignMode.LEFT : (0.0, 0.0), AlignMode.CENTER : (1.0, 0.5), AlignMode.RIGHT : (1.0, 1.0)} | 819 | halign_factors = {AlignMode.LEFT : (0.0, 0.0), AlignMode.CENTER : (1.0, 0.5), AlignMode.RIGHT : (1.0, 1.0)} |
822 | if halign not in halign_factors: | 820 | if halign not in halign_factors: |
@@ -827,24 +825,22 @@ class CairoCanvas(Canvas): | |||
827 | if valign not in valign_factors: | 825 | if valign not in valign_factors: |
828 | raise ValueError('Invalid alignment value') | 826 | raise ValueError('Invalid alignment value') |
829 | f_descent_factor, f_height_factor = valign_factors[valign] | 827 | f_descent_factor, f_height_factor = valign_factors[valign] |
830 | 828 | ||
831 | scale = 1.0 | ||
832 | if do_scale: | ||
833 | scale = self.scale | ||
834 | |||
835 | return self._get_label_dim_common(text, x, y, fopts, x_bearing_factor, | 829 | return self._get_label_dim_common(text, x, y, fopts, x_bearing_factor, |
836 | f_descent_factor, width_factor, f_height_factor, | 830 | f_descent_factor, width_factor, f_height_factor, |
837 | scale, do_snap) | 831 | do_snap) |
838 | 832 | ||
839 | def draw_label(self, text, x, y, fopts=GraphFormat.DEF_FOPTS_LABEL, halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, do_snap=True): | 833 | def draw_label(self, text, x, y, fopts=GraphFormat.DEF_FOPTS_LABEL, halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, do_snap=True): |
840 | """Draws a label with the given parameters, with the given horizontal and vertical justification.""" | 834 | """Draws a label with the given parameters, with the given horizontal and vertical justification.""" |
841 | 835 | ||
842 | x, y = self.scaled(x, y) | 836 | actual_x, actual_y, width, height, f_height = self.get_label_dim(text, x, y, fopts, halign, valign, do_snap) |
843 | actual_x, actual_y, width, height, f_height = self.get_label_dim(text, x, y, fopts, halign, valign, True, do_snap) | ||
844 | actual_x, actual_y = self.surface.get_real_coor(actual_x, actual_y) | 837 | actual_x, actual_y = self.surface.get_real_coor(actual_x, actual_y) |
845 | 838 | ||
839 | self.surface.ctx.select_font_face(fopts.name, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) | ||
840 | self.surface.ctx.set_font_size(fopts.size * self.scale) | ||
846 | self.surface.ctx.set_source_rgb(fopts.color[0], fopts.color[1], fopts.color[2]) | 841 | self.surface.ctx.set_source_rgb(fopts.color[0], fopts.color[1], fopts.color[2]) |
847 | self.surface.ctx.move_to(actual_x, actual_y) | 842 | self.surface.ctx.move_to(actual_x, actual_y) |
843 | |||
848 | self.surface.ctx.show_text(text) | 844 | self.surface.ctx.show_text(text) |
849 | 845 | ||
850 | def draw_label_with_sscripts(self, text, supscript, subscript, x, y, \ | 846 | def draw_label_with_sscripts(self, text, supscript, subscript, x, y, \ |
@@ -881,7 +877,7 @@ def snap(pos): | |||
881 | since the line will get distributed over two pixels. We actually apply this to all | 877 | since the line will get distributed over two pixels. We actually apply this to all |
882 | coordinates to make sure everything is aligned.""" | 878 | coordinates to make sure everything is aligned.""" |
883 | return pos - 0.5 | 879 | return pos - 0.5 |
884 | 880 | ||
885 | # represents a selectable region of the graph | 881 | # represents a selectable region of the graph |
886 | class SelectableRegion(object): | 882 | class SelectableRegion(object): |
887 | def __init__(self, x, y, width, height, event): | 883 | def __init__(self, x, y, width, height, event): |
@@ -892,7 +888,6 @@ class SelectableRegion(object): | |||
892 | self.width = width | 888 | self.width = width |
893 | self.height = height | 889 | self.height = height |
894 | self.event = event | 890 | self.event = event |
895 | self.scale = 1.0 | ||
896 | 891 | ||
897 | def get_dimensions(self): | 892 | def get_dimensions(self): |
898 | """Gets the dimensions of the region.""" | 893 | """Gets the dimensions of the region.""" |
@@ -902,18 +897,15 @@ class SelectableRegion(object): | |||
902 | """Gets the event associated with the region.""" | 897 | """Gets the event associated with the region.""" |
903 | return self.event | 898 | return self.event |
904 | 899 | ||
905 | def set_scale(self, scale): | ||
906 | self.scale = scale | ||
907 | |||
908 | def is_dummy(self): | 900 | def is_dummy(self): |
909 | return self.x is None or self.y is None | 901 | return self.x is None or self.y is None |
910 | 902 | ||
911 | def intersects(self, x, y, width, height): | 903 | def intersects(self, x, y, width, height): |
912 | if self.is_dummy(): | 904 | if self.is_dummy(): |
913 | return False | 905 | return False |
914 | 906 | ||
915 | return x <= (self.x + self.width) * self.scale \ | 907 | return x <= (self.x + self.width) \ |
916 | and x + width >= self.x * self.scale \ | 908 | and x + width >= self.x \ |
917 | and y <= (self.y + self.height) * self.scale \ | 909 | and y <= (self.y + self.height) \ |
918 | and y + height >= self.y * self.scale | 910 | and y + height >= self.y |
919 | 911 | ||
diff --git a/unit_trace/viz/format.py b/unit_trace/viz/format.py index 18dfd5a..13d4843 100644 --- a/unit_trace/viz/format.py +++ b/unit_trace/viz/format.py | |||
@@ -1,5 +1,7 @@ | |||
1 | """Various formatting parameters intended to be accessible by the client.""" | 1 | """Various formatting parameters intended to be accessible by the client.""" |
2 | 2 | ||
3 | import util | ||
4 | |||
3 | class FontOptions(object): | 5 | class FontOptions(object): |
4 | """Class for combining assorted simple font options.""" | 6 | """Class for combining assorted simple font options.""" |
5 | def __init__(self, name, size, color): | 7 | def __init__(self, name, size, color): |
@@ -58,6 +60,8 @@ class GraphFormat(object): | |||
58 | DEF_FOPTS_ARROW = FontOptions("Times", 12, (0.0, 0.0, 0.0)) | 60 | DEF_FOPTS_ARROW = FontOptions("Times", 12, (0.0, 0.0, 0.0)) |
59 | DEF_FOPTS_ARROW_SSCRIPT = FontOptions("Times", 7, (0.0, 0.0, 0.0)) | 61 | DEF_FOPTS_ARROW_SSCRIPT = FontOptions("Times", 7, (0.0, 0.0, 0.0)) |
60 | 62 | ||
63 | DEF_TIME_UNIT = util.MilliSec() | ||
64 | |||
61 | LEFT_SIDE_PAD = 30 | 65 | LEFT_SIDE_PAD = 30 |
62 | WIDTH_PAD = 50 | 66 | WIDTH_PAD = 50 |
63 | HEIGHT_PAD = 150 | 67 | HEIGHT_PAD = 150 |
@@ -85,7 +89,7 @@ class GraphFormat(object): | |||
85 | def __init__(self, time_per_maj=DEF_TIME_PER_MAJ, maj_sep=DEF_MAJ_SEP, | 89 | def __init__(self, time_per_maj=DEF_TIME_PER_MAJ, maj_sep=DEF_MAJ_SEP, |
86 | min_per_maj=DEF_MIN_PER_MAJ, y_item_size=DEF_Y_ITEM_SIZE, bar_fopts=DEF_FOPTS_BAR, | 90 | min_per_maj=DEF_MIN_PER_MAJ, y_item_size=DEF_Y_ITEM_SIZE, bar_fopts=DEF_FOPTS_BAR, |
87 | item_fopts=DEF_FOPTS_ITEM, show_min=False, majfopts=DEF_FOPTS_MAJ, | 91 | item_fopts=DEF_FOPTS_ITEM, show_min=False, majfopts=DEF_FOPTS_MAJ, |
88 | minfopts=DEF_FOPTS_MIN): | 92 | minfopts=DEF_FOPTS_MIN, unit=DEF_TIME_UNIT): |
89 | self.time_per_maj = time_per_maj | 93 | self.time_per_maj = time_per_maj |
90 | self.maj_sep = maj_sep | 94 | self.maj_sep = maj_sep |
91 | self.min_per_maj = min_per_maj | 95 | self.min_per_maj = min_per_maj |
@@ -95,3 +99,4 @@ class GraphFormat(object): | |||
95 | self.show_min = show_min | 99 | self.show_min = show_min |
96 | self.majfopts = majfopts | 100 | self.majfopts = majfopts |
97 | self.minfopts = minfopts | 101 | self.minfopts = minfopts |
102 | self.unit = unit | ||
diff --git a/unit_trace/viz/graph.py b/unit_trace/viz/graph.py index fa0df99..73c4ce4 100644 --- a/unit_trace/viz/graph.py +++ b/unit_trace/viz/graph.py | |||
@@ -60,25 +60,25 @@ class Graph(object): | |||
60 | 60 | ||
61 | #if surface.ctx is None: | 61 | #if surface.ctx is None: |
62 | # surface.renew(width, height) | 62 | # surface.renew(width, height) |
63 | 63 | ||
64 | # item clist might end before | 64 | # item clist might end before |
65 | full_item_clist = [] | 65 | full_item_clist = [] |
66 | full_bar_plist = [] | 66 | full_bar_plist = [] |
67 | 67 | ||
68 | for i in range(0, len(self.y_item_list)): | 68 | for i in range(0, len(self.y_item_list)): |
69 | full_item_clist.append(item_clist[i % len(item_clist)]) | 69 | full_item_clist.append(item_clist[i % len(item_clist)]) |
70 | 70 | ||
71 | for i in range(0, len(self.top_item_list)): | 71 | for i in range(0, len(self.top_item_list)): |
72 | full_bar_plist.append(bar_plist[i % (len(bar_plist) - 1)]) | 72 | full_bar_plist.append(bar_plist[i % (len(bar_plist) - 1)]) |
73 | 73 | ||
74 | # last bar pattern is a ``special pattern'' so put it at | 74 | # last bar pattern is a ``special pattern'' so put it at |
75 | # the end (and nowhere else) | 75 | # the end (and nowhere else) |
76 | full_bar_plist.append(bar_plist[-1]) | 76 | full_bar_plist.append(bar_plist[-1]) |
77 | 77 | ||
78 | self.canvas = CanvasType(width, height, full_item_clist, full_bar_plist, surface) | 78 | self.canvas = CanvasType(width, height, full_item_clist, full_bar_plist, surface) |
79 | 79 | ||
80 | def get_intersecting_regions(self, real_x, real_y, width, height): | 80 | def get_intersecting_regions(self, x, y, width, height): |
81 | return self.canvas.get_intersecting_regions(real_x, real_y, width, height) | 81 | return self.canvas.get_intersecting_regions(x, y, width, height) |
82 | 82 | ||
83 | def get_width(self): | 83 | def get_width(self): |
84 | return self.width | 84 | return self.width |
@@ -103,29 +103,55 @@ class Graph(object): | |||
103 | 103 | ||
104 | def get_item_color(self, no): | 104 | def get_item_color(self, no): |
105 | return self.canvas.get_item_color(no) | 105 | return self.canvas.get_item_color(no) |
106 | 106 | ||
107 | def get_bar_pattern(self, no): | 107 | def get_bar_pattern(self, no): |
108 | return self.canvas.get_bar_pattern(no) | 108 | return self.canvas.get_bar_pattern(no) |
109 | 109 | ||
110 | def set_bar_pattern(self, no, pattern): | 110 | def set_bar_pattern(self, no, pattern): |
111 | self.canvas.set_bar_pattern(no, pattern) | 111 | self.canvas.set_bar_pattern(no, pattern) |
112 | 112 | ||
113 | def update_view(self, x, y, width, height, scale, ctx): | 113 | def real_to_virt(self, x, y, width, height): |
114 | """Updates the view's window location, scale factor, and Cairo context (i.e. where | 114 | """Convert real coordinates to virtual coordinates.""" |
115 | virt_x, virt_y = self.canvas.surface.get_virt_coor(x, y) | ||
116 | virt_width, virt_height = self.canvas.surface.get_virt_dim(width, height) | ||
117 | return (virt_x, virt_y, virt_width, virt_height) | ||
118 | |||
119 | def real_to_virt_list(self, coors): | ||
120 | l = [] | ||
121 | for coor in coors: | ||
122 | l.append(self.real_to_virt(coor[0], coor[1], coor[2], coor[3])) | ||
123 | return l | ||
124 | |||
125 | def virt_to_real(self, x, y, width, height): | ||
126 | real_x, real_y = self.canvas.surface.get_real_coor(x, y) | ||
127 | real_width, real_height = self.canvas.surface.get_real_dim(width, height) | ||
128 | return (real_x, real_y, real_width, real_height) | ||
129 | |||
130 | def virt_to_real_list(self, coors): | ||
131 | l = [] | ||
132 | for coor in coors: | ||
133 | l.append(self.virt_to_real(coor[0], coor[1], coor[2], coor[3])) | ||
134 | return l | ||
135 | |||
136 | def update_view(self, x, y, width, height, scale): | ||
137 | """Updates the view's window location and scale factor, and Cairo context (i.e. where | ||
115 | the view currently ``is'').""" | 138 | the view currently ``is'').""" |
116 | self.canvas.surface.pan(x, y, width, height) | 139 | self.canvas.surface.pan(x, y, width, height) |
117 | self.canvas.set_scale(scale) | 140 | self.canvas.set_scale(scale) |
141 | |||
142 | def load_ctx(self, ctx): | ||
143 | """Borrows a context to draw on.""" | ||
118 | self.canvas.surface.change_ctx(ctx) | 144 | self.canvas.surface.change_ctx(ctx) |
119 | 145 | ||
120 | def set_tmp_surface(self, surface): | 146 | def set_tmp_surface(self, surface): |
121 | """Changes to a temporary surface, instead of using the default surface (the | 147 | """Changes to a temporary surface, instead of using the default surface (the |
122 | surface that writes to the screen.""" | 148 | surface that writes to the screen.""" |
123 | self.canvas.set_tmp_surface(surface) | 149 | self.canvas.set_tmp_surface(surface) |
124 | 150 | ||
125 | def revert_surface(self): | 151 | def revert_surface(self): |
126 | """Changes back to the default surface.""" | 152 | """Changes back to the default surface.""" |
127 | self.canvas.revert_surface() | 153 | self.canvas.revert_surface() |
128 | 154 | ||
129 | def _recomp_min_max(self, start_time, end_time, start_item, end_item): | 155 | def _recomp_min_max(self, start_time, end_time, start_item, end_item): |
130 | if self.min_time is None or start_time < self.min_time: | 156 | if self.min_time is None or start_time < self.min_time: |
131 | self.min_time = start_time | 157 | self.min_time = start_time |
@@ -177,15 +203,12 @@ class Graph(object): | |||
177 | def ycoor_to_item_no(self, y): | 203 | def ycoor_to_item_no(self, y): |
178 | return int((y - self.origin[1] + self._get_y_axis_height()) // self.attrs.y_item_size) | 204 | return int((y - self.origin[1] + self._get_y_axis_height()) // self.attrs.y_item_size) |
179 | 205 | ||
180 | def get_offset_params(self, real_x, real_y, width, height): | 206 | def get_offset_params(self, x, y, width, height): |
181 | x_start, y_start = self.canvas.surface.get_virt_coor_unscaled(real_x, real_y) | 207 | start_time = self.xcoor_to_time(x) |
182 | x_end, y_end = self.canvas.surface.get_virt_coor_unscaled(real_x + width, real_y + height) | 208 | end_time = self.xcoor_to_time(x + width) |
183 | 209 | ||
184 | start_time = self.xcoor_to_time(x_start) | 210 | start_item = self.ycoor_to_item_no(y) |
185 | end_time = self.xcoor_to_time(x_end) | 211 | end_item = 1 + self.ycoor_to_item_no(y + height) |
186 | |||
187 | start_item = self.ycoor_to_item_no(y_start) | ||
188 | end_item = 1 + self.ycoor_to_item_no(y_end) | ||
189 | 212 | ||
190 | return (start_time, end_time, start_item, end_item) | 213 | return (start_time, end_time, start_item, end_item) |
191 | 214 | ||
@@ -199,7 +222,7 @@ class Graph(object): | |||
199 | self.canvas.whiteout() | 222 | self.canvas.whiteout() |
200 | start, end = sched.get_time_bounds() | 223 | start, end = sched.get_time_bounds() |
201 | self.draw_skeleton(start, end, 0, len(self.y_item_list)) | 224 | self.draw_skeleton(start, end, 0, len(self.y_item_list)) |
202 | 225 | ||
203 | slots = {} | 226 | slots = {} |
204 | all_events = {} | 227 | all_events = {} |
205 | sched.get_time_slot_array().get_slots(slots, | 228 | sched.get_time_slot_array().get_slots(slots, |
@@ -210,7 +233,7 @@ class Graph(object): | |||
210 | if event.get_layer() not in all_events: | 233 | if event.get_layer() not in all_events: |
211 | all_events[event.get_layer()] = {} | 234 | all_events[event.get_layer()] = {} |
212 | all_events[event.get_layer()][event] = None | 235 | all_events[event.get_layer()][event] = None |
213 | 236 | ||
214 | self.render_events(all_events) | 237 | self.render_events(all_events) |
215 | 238 | ||
216 | def get_events_to_render(self, sched, regions, selectable=False): | 239 | def get_events_to_render(self, sched, regions, selectable=False): |
@@ -221,15 +244,15 @@ class Graph(object): | |||
221 | x, y, width, height = region | 244 | x, y, width, height = region |
222 | start_time, end_time, start_item, end_item = self.get_offset_params(x, y, width, height) | 245 | start_time, end_time, start_item, end_item = self.get_offset_params(x, y, width, height) |
223 | self._recomp_min_max(start_time, end_time, start_item, end_item) | 246 | self._recomp_min_max(start_time, end_time, start_item, end_item) |
224 | 247 | ||
225 | sched.get_time_slot_array().get_slots(slots, | 248 | sched.get_time_slot_array().get_slots(slots, |
226 | start_time, end_time, start_item, end_item, | 249 | start_time, end_time, start_item, end_item, |
227 | self.list_type) | 250 | self.list_type) |
228 | 251 | ||
229 | events_to_render = {} | 252 | events_to_render = {} |
230 | for layer in Canvas.LAYERS: | 253 | for layer in Canvas.LAYERS: |
231 | events_to_render[layer] = {} | 254 | events_to_render[layer] = {} |
232 | 255 | ||
233 | for event in sched.get_time_slot_array().get_events(slots, | 256 | for event in sched.get_time_slot_array().get_events(slots, |
234 | self.list_type, schedule.EVENT_LIST): | 257 | self.list_type, schedule.EVENT_LIST): |
235 | events_to_render[event.get_layer()][event] = None | 258 | events_to_render[event.get_layer()][event] = None |
@@ -246,7 +269,7 @@ class Graph(object): | |||
246 | self.draw_skeleton(self.min_time, self.max_time, | 269 | self.draw_skeleton(self.min_time, self.max_time, |
247 | self.min_item, self.max_item) | 270 | self.min_item, self.max_item) |
248 | self.render_events(events, selectable) | 271 | self.render_events(events, selectable) |
249 | 272 | ||
250 | def render_events(self, events, selectable=False): | 273 | def render_events(self, events, selectable=False): |
251 | for layer in Canvas.LAYERS: | 274 | for layer in Canvas.LAYERS: |
252 | prev_events = {} | 275 | prev_events = {} |
@@ -278,7 +301,7 @@ class Graph(object): | |||
278 | self.canvas.draw_grid(self.origin[0], self.origin[1], self._get_y_axis_height(), | 301 | self.canvas.draw_grid(self.origin[0], self.origin[1], self._get_y_axis_height(), |
279 | start_tick, end_tick, start_item, end_item, self.attrs.maj_sep, self.attrs.y_item_size, \ | 302 | start_tick, end_tick, start_item, end_item, self.attrs.maj_sep, self.attrs.y_item_size, \ |
280 | self.attrs.min_per_maj, True) | 303 | self.attrs.min_per_maj, True) |
281 | 304 | ||
282 | def draw_x_axis_with_labels_at_time(self, start_time, end_time): | 305 | def draw_x_axis_with_labels_at_time(self, start_time, end_time): |
283 | start_tick = max(0, self._get_bottom_tick(start_time)) | 306 | start_tick = max(0, self._get_bottom_tick(start_time)) |
284 | end_tick = min(self.num_maj - 1, self._get_top_tick(end_time)) | 307 | end_tick = min(self.num_maj - 1, self._get_top_tick(end_time)) |
@@ -288,33 +311,33 @@ class Graph(object): | |||
288 | self.canvas.draw_x_axis_labels(self.origin[0], self.origin[1], start_tick, \ | 311 | self.canvas.draw_x_axis_labels(self.origin[0], self.origin[1], start_tick, \ |
289 | end_tick, self.attrs.maj_sep, self.attrs.min_per_maj, | 312 | end_tick, self.attrs.maj_sep, self.attrs.min_per_maj, |
290 | self.start_time + start_tick * self.attrs.time_per_maj, | 313 | self.start_time + start_tick * self.attrs.time_per_maj, |
291 | self.attrs.time_per_maj, False) | 314 | self.attrs.time_per_maj, False, self.attrs.unit) |
292 | 315 | ||
293 | def draw_y_axis(self): | 316 | def draw_y_axis(self): |
294 | self.canvas.draw_y_axis(self.origin[0], self.origin[1], self._get_y_axis_height()) | 317 | self.canvas.draw_y_axis(self.origin[0], self.origin[1], self._get_y_axis_height()) |
295 | #self.canvas.draw_y_axis_labels(self.origin[0], self.origin[1], self._get_y_axis_height(), \ | 318 | #self.canvas.draw_y_axis_labels(self.origin[0], self.origin[1], self._get_y_axis_height(), \ |
296 | # self.y_item_list, self.attrs.y_item_size) | 319 | # self.y_item_list, self.attrs.y_item_size) |
297 | 320 | ||
298 | def draw_y_axis_item(self, item_no, selected): | 321 | def draw_y_axis_item(self, item_no, selected): |
299 | self.canvas.draw_y_axis_item(self.origin[0], self.origin[1], self._get_y_axis_height(), | 322 | self.canvas.draw_y_axis_item(self.origin[0], self.origin[1], self._get_y_axis_height(), |
300 | self.y_item_list[item_no], item_no, self.attrs.y_item_size, | 323 | self.y_item_list[item_no], item_no, self.attrs.y_item_size, |
301 | selected) | 324 | selected) |
302 | 325 | ||
303 | def add_sel_y_axis_item(self, item_no, event): | 326 | def add_sel_y_axis_item(self, item_no, event): |
304 | self.canvas.add_sel_y_axis_item(self.origin[0], self.origin[1], self._get_y_axis_height(), | 327 | self.canvas.add_sel_y_axis_item(self.origin[0], self.origin[1], self._get_y_axis_height(), |
305 | self.y_item_list[item_no], item_no, self.attrs.y_item_size, | 328 | self.y_item_list[item_no], item_no, self.attrs.y_item_size, |
306 | event) | 329 | event) |
307 | 330 | ||
308 | def draw_top_item(self, item_no, selected): | 331 | def draw_top_item(self, item_no, selected): |
309 | self.canvas.draw_top_item(self.origin[0], self.origin[1], self._get_y_axis_height(), | 332 | self.canvas.draw_top_item(self.origin[0], self.origin[1], self._get_y_axis_height(), |
310 | self.top_item_list[item_no], item_no, self.top_item_list[0:item_no], | 333 | self.top_item_list[item_no], item_no, self.top_item_list[0:item_no], |
311 | selected) | 334 | selected) |
312 | 335 | ||
313 | def add_sel_top_item(self, item_no, event): | 336 | def add_sel_top_item(self, item_no, event): |
314 | self.canvas.add_sel_top_item(self.origin[0], self.origin[1], self._get_y_axis_height(), | 337 | self.canvas.add_sel_top_item(self.origin[0], self.origin[1], self._get_y_axis_height(), |
315 | self.top_item_list[item_no], item_no, self.top_item_list[0:item_no], | 338 | self.top_item_list[item_no], item_no, self.top_item_list[0:item_no], |
316 | event) | 339 | event) |
317 | 340 | ||
318 | def all_one_item_selected(self, selected): | 341 | def all_one_item_selected(self, selected): |
319 | """When the user selects some parts of the graph, it might be the case that all of the | 342 | """When the user selects some parts of the graph, it might be the case that all of the |
320 | parts lie under one ``item'' of the graph. In this case, certain operations are likely | 343 | parts lie under one ``item'' of the graph. In this case, certain operations are likely |
@@ -322,19 +345,19 @@ class Graph(object): | |||
322 | a set of selected items satisfies this criterion. It also gives the item selected if | 345 | a set of selected items satisfies this criterion. It also gives the item selected if |
323 | so.""" | 346 | so.""" |
324 | raise NotImplementedError | 347 | raise NotImplementedError |
325 | 348 | ||
326 | def draw_cpu_item(self, item_no, selected): | 349 | def draw_cpu_item(self, item_no, selected): |
327 | raise NotImplementedError | 350 | raise NotImplementedError |
328 | 351 | ||
329 | def add_sel_cpu_item(self, item_no, event): | 352 | def add_sel_cpu_item(self, item_no, event): |
330 | raise NotImplementedError | 353 | raise NotImplementedError |
331 | 354 | ||
332 | def draw_task_item(self, item_no, selected): | 355 | def draw_task_item(self, item_no, selected): |
333 | raise NotImplementedError | 356 | raise NotImplementedError |
334 | 357 | ||
335 | def add_sel_task_item(self, item_no, event): | 358 | def add_sel_task_item(self, item_no, event): |
336 | raise NotImplementedError | 359 | raise NotImplementedError |
337 | 360 | ||
338 | def draw_suspend_triangle_at_time(self, time, task_no, cpu_no, selected=False): | 361 | def draw_suspend_triangle_at_time(self, time, task_no, cpu_no, selected=False): |
339 | """Draws a suspension symbol for a dcertain task at an instant in time.""" | 362 | """Draws a suspension symbol for a dcertain task at an instant in time.""" |
340 | raise NotImplementedError | 363 | raise NotImplementedError |
@@ -404,7 +427,7 @@ class TaskGraph(Graph): | |||
404 | super(TaskGraph, self).__init__(CanvasType, surface, start_time, end_time, y_item_list, top_item_list, | 427 | super(TaskGraph, self).__init__(CanvasType, surface, start_time, end_time, y_item_list, top_item_list, |
405 | attrs, item_clist, bar_plist) | 428 | attrs, item_clist, bar_plist) |
406 | self.list_type = schedule.TimeSlotArray.TASK_LIST | 429 | self.list_type = schedule.TimeSlotArray.TASK_LIST |
407 | 430 | ||
408 | def all_one_item_selected(self, selected): | 431 | def all_one_item_selected(self, selected): |
409 | cpu = None | 432 | cpu = None |
410 | for layer in selected: | 433 | for layer in selected: |
@@ -414,21 +437,21 @@ class TaskGraph(Graph): | |||
414 | else: | 437 | else: |
415 | if event.get_cpu() is None or event.get_cpu() != cpu: | 438 | if event.get_cpu() is None or event.get_cpu() != cpu: |
416 | return None | 439 | return None |
417 | 440 | ||
418 | return cpu | 441 | return cpu |
419 | 442 | ||
420 | def draw_cpu_item(self, item_no, selected): | 443 | def draw_cpu_item(self, item_no, selected): |
421 | self.draw_top_item(item_no, selected) | 444 | self.draw_top_item(item_no, selected) |
422 | 445 | ||
423 | def add_sel_cpu_item(self, item_no, event): | 446 | def add_sel_cpu_item(self, item_no, event): |
424 | self.add_sel_top_item(item_no, event) | 447 | self.add_sel_top_item(item_no, event) |
425 | 448 | ||
426 | def draw_task_item(self, item_no, selected): | 449 | def draw_task_item(self, item_no, selected): |
427 | self.draw_y_axis_item(item_no, selected) | 450 | self.draw_y_axis_item(item_no, selected) |
428 | 451 | ||
429 | def add_sel_task_item(self, item_no, event): | 452 | def add_sel_task_item(self, item_no, event): |
430 | self.add_sel_y_axis_item(item_no, event) | 453 | self.add_sel_y_axis_item(item_no, event) |
431 | 454 | ||
432 | def draw_suspend_triangle_at_time(self, time, task_no, cpu_no, selected=False): | 455 | def draw_suspend_triangle_at_time(self, time, task_no, cpu_no, selected=False): |
433 | height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR | 456 | height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR |
434 | x = self.get_time_xpos(time) | 457 | x = self.get_time_xpos(time) |
@@ -563,7 +586,7 @@ class CpuGraph(Graph): | |||
563 | super(CpuGraph, self).__init__(CanvasType, surface, start_time, end_time, y_item_list, top_item_list, | 586 | super(CpuGraph, self).__init__(CanvasType, surface, start_time, end_time, y_item_list, top_item_list, |
564 | attrs, item_clist, bar_plist) | 587 | attrs, item_clist, bar_plist) |
565 | self.list_type = schedule.TimeSlotArray.CPU_LIST | 588 | self.list_type = schedule.TimeSlotArray.CPU_LIST |
566 | 589 | ||
567 | def all_one_item_selected(self, selected): | 590 | def all_one_item_selected(self, selected): |
568 | task_no = None | 591 | task_no = None |
569 | for layer in selected: | 592 | for layer in selected: |
@@ -573,21 +596,21 @@ class CpuGraph(Graph): | |||
573 | else: | 596 | else: |
574 | if event.get_task() is None or event.get_task().get_task_no() != task_no: | 597 | if event.get_task() is None or event.get_task().get_task_no() != task_no: |
575 | return None | 598 | return None |
576 | 599 | ||
577 | return task_no | 600 | return task_no |
578 | 601 | ||
579 | def draw_cpu_item(self, item_no, selected): | 602 | def draw_cpu_item(self, item_no, selected): |
580 | self.draw_y_axis_item(item_no, selected) | 603 | self.draw_y_axis_item(item_no, selected) |
581 | 604 | ||
582 | def add_sel_cpu_item(self, item_no, event): | 605 | def add_sel_cpu_item(self, item_no, event): |
583 | self.add_sel_y_axis_item(item_no, event) | 606 | self.add_sel_y_axis_item(item_no, event) |
584 | 607 | ||
585 | def draw_task_item(self, item_no, selected): | 608 | def draw_task_item(self, item_no, selected): |
586 | self.draw_top_item(item_no, selected) | 609 | self.draw_top_item(item_no, selected) |
587 | 610 | ||
588 | def add_sel_task_item(self, item_no, event): | 611 | def add_sel_task_item(self, item_no, event): |
589 | self.add_sel_top_item(item_no, event) | 612 | self.add_sel_top_item(item_no, event) |
590 | 613 | ||
591 | def draw_suspend_triangle_at_time(self, time, task_no, cpu_no, selected=False): | 614 | def draw_suspend_triangle_at_time(self, time, task_no, cpu_no, selected=False): |
592 | height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR | 615 | height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR |
593 | x = self.get_time_xpos(time) | 616 | x = self.get_time_xpos(time) |
diff --git a/unit_trace/viz/schedule.py b/unit_trace/viz/schedule.py index 82b7ea1..ec6398d 100644 --- a/unit_trace/viz/schedule.py +++ b/unit_trace/viz/schedule.py | |||
@@ -22,14 +22,14 @@ class TimeSlotArray(object): | |||
22 | TASK_LIST = 0 | 22 | TASK_LIST = 0 |
23 | CPU_LIST = 1 | 23 | CPU_LIST = 1 |
24 | LIST_TYPES = (TASK_LIST, CPU_LIST) | 24 | LIST_TYPES = (TASK_LIST, CPU_LIST) |
25 | 25 | ||
26 | PRE_ITEM_NO = -1 | 26 | PRE_ITEM_NO = -1 |
27 | POST_ITEM_NO = -2 | 27 | POST_ITEM_NO = -2 |
28 | 28 | ||
29 | PRE_SLOT = 'pre' | 29 | PRE_SLOT = 'pre' |
30 | POST_SLOT = 'post' | 30 | POST_SLOT = 'post' |
31 | ALL_SLOT = 'all' | 31 | ALL_SLOT = 'all' |
32 | 32 | ||
33 | def __init__(self, time_per_maj=None, num_tasks=0, num_cpus=0): | 33 | def __init__(self, time_per_maj=None, num_tasks=0, num_cpus=0): |
34 | if time_per_maj is None: | 34 | if time_per_maj is None: |
35 | self.array = None | 35 | self.array = None |
@@ -40,7 +40,7 @@ class TimeSlotArray(object): | |||
40 | self.array = {} | 40 | self.array = {} |
41 | self.min_slot = None | 41 | self.min_slot = None |
42 | self.max_slot = None | 42 | self.max_slot = None |
43 | 43 | ||
44 | for type in self.list_sizes: | 44 | for type in self.list_sizes: |
45 | num = self.list_sizes[type] | 45 | num = self.list_sizes[type] |
46 | self.array[type] = [] | 46 | self.array[type] = [] |
@@ -53,12 +53,12 @@ class TimeSlotArray(object): | |||
53 | 53 | ||
54 | def get_time_slot(self, time): | 54 | def get_time_slot(self, time): |
55 | return int(time // self.time_per_maj) | 55 | return int(time // self.time_per_maj) |
56 | 56 | ||
57 | def put_event_in_slot(self, list_type, no, klass, slot, event): | 57 | def put_event_in_slot(self, list_type, no, klass, slot, event): |
58 | if slot not in self.array[list_type][no][klass]: | 58 | if slot not in self.array[list_type][no][klass]: |
59 | self.array[list_type][no][klass][slot] = [] | 59 | self.array[list_type][no][klass][slot] = [] |
60 | self.array[list_type][no][klass][slot].append(event) | 60 | self.array[list_type][no][klass][slot].append(event) |
61 | 61 | ||
62 | if self.min_slot is None or slot < self.min_slot: | 62 | if self.min_slot is None or slot < self.min_slot: |
63 | self.min_slot = slot | 63 | self.min_slot = slot |
64 | if self.max_slot is None or slot > self.max_slot: | 64 | if self.max_slot is None or slot > self.max_slot: |
@@ -69,10 +69,10 @@ class TimeSlotArray(object): | |||
69 | cpu = event.get_cpu() | 69 | cpu = event.get_cpu() |
70 | 70 | ||
71 | slot = self.get_time_slot(event.get_time()) | 71 | slot = self.get_time_slot(event.get_time()) |
72 | 72 | ||
73 | for type in item_nos: | 73 | for type in item_nos: |
74 | self.put_event_in_slot(type, item_nos[type], event.__class__, slot, event) | 74 | self.put_event_in_slot(type, item_nos[type], event.__class__, slot, event) |
75 | 75 | ||
76 | def fill_span_event_from_start(self, event, item_nos): | 76 | def fill_span_event_from_start(self, event, item_nos): |
77 | end_slot = None | 77 | end_slot = None |
78 | if event.corresp_end_event is None: | 78 | if event.corresp_end_event is None: |
@@ -104,17 +104,17 @@ class TimeSlotArray(object): | |||
104 | 104 | ||
105 | for type in item_nos: | 105 | for type in item_nos: |
106 | self.put_event_in_slot(type, item_nos[type], event.dummy_class, slot, dummy) | 106 | self.put_event_in_slot(type, item_nos[type], event.dummy_class, slot, dummy) |
107 | 107 | ||
108 | def add_item_dummies(self, sched): | 108 | def add_item_dummies(self, sched): |
109 | start = sched.get_time_bounds()[0] | 109 | start = sched.get_time_bounds()[0] |
110 | 110 | ||
111 | for task in sched.get_tasks().values(): | 111 | for task in sched.get_tasks().values(): |
112 | dummy = TaskDummy(start, task) | 112 | dummy = TaskDummy(start, task) |
113 | self.put_event_in_slot(TimeSlotArray.TASK_LIST, task.get_task_no(), dummy.__class__, | 113 | self.put_event_in_slot(TimeSlotArray.TASK_LIST, task.get_task_no(), dummy.__class__, |
114 | TimeSlotArray.PRE_SLOT, dummy) | 114 | TimeSlotArray.PRE_SLOT, dummy) |
115 | self.put_event_in_slot(TimeSlotArray.CPU_LIST, TimeSlotArray.PRE_ITEM_NO, dummy.__class__, | 115 | self.put_event_in_slot(TimeSlotArray.CPU_LIST, TimeSlotArray.PRE_ITEM_NO, dummy.__class__, |
116 | TimeSlotArray.ALL_SLOT, dummy) | 116 | TimeSlotArray.ALL_SLOT, dummy) |
117 | 117 | ||
118 | for i in range(0, self.list_sizes[TimeSlotArray.CPU_LIST]): | 118 | for i in range(0, self.list_sizes[TimeSlotArray.CPU_LIST]): |
119 | dummy = CPUDummy(start, sched, i) | 119 | dummy = CPUDummy(start, sched, i) |
120 | self.put_event_in_slot(TimeSlotArray.CPU_LIST, i, dummy.__class__, | 120 | self.put_event_in_slot(TimeSlotArray.CPU_LIST, i, dummy.__class__, |
@@ -135,14 +135,14 @@ class TimeSlotArray(object): | |||
135 | raise ValueError('Litmus is not a time machine') | 135 | raise ValueError('Litmus is not a time machine') |
136 | if start_no > end_no: | 136 | if start_no > end_no: |
137 | raise ValueError('start no should be less than end no') | 137 | raise ValueError('start no should be less than end no') |
138 | 138 | ||
139 | if self.min_slot is None: | 139 | if self.min_slot is None: |
140 | # no events ever got added | 140 | # no events ever got added |
141 | return {} | 141 | return {} |
142 | 142 | ||
143 | start_slot = self.get_time_slot(start) | 143 | start_slot = self.get_time_slot(start) |
144 | end_slot = self.get_time_slot(end) + 1 | 144 | end_slot = self.get_time_slot(end) + 1 |
145 | 145 | ||
146 | item_no_list = [] | 146 | item_no_list = [] |
147 | if start_no < 0: | 147 | if start_no < 0: |
148 | item_no_list.append(TimeSlotArray.PRE_ITEM_NO) | 148 | item_no_list.append(TimeSlotArray.PRE_ITEM_NO) |
@@ -151,24 +151,24 @@ class TimeSlotArray(object): | |||
151 | start_no = max(0, start_no) | 151 | start_no = max(0, start_no) |
152 | end_no = min(self.list_sizes[list_type] - 1, end_no) | 152 | end_no = min(self.list_sizes[list_type] - 1, end_no) |
153 | item_no_list += range(start_no, end_no + 1) | 153 | item_no_list += range(start_no, end_no + 1) |
154 | 154 | ||
155 | edge_slots = [TimeSlotArray.ALL_SLOT] | 155 | edge_slots = [TimeSlotArray.ALL_SLOT] |
156 | if start_slot <= self.min_slot: | 156 | if start_slot <= self.min_slot: |
157 | edge_slots.append(TimeSlotArray.PRE_SLOT) | 157 | edge_slots.append(TimeSlotArray.PRE_SLOT) |
158 | if end_slot >= self.max_slot: | 158 | if end_slot >= self.max_slot: |
159 | edge_slots.append(TimeSlotArray.POST_SLOT) | 159 | edge_slots.append(TimeSlotArray.POST_SLOT) |
160 | 160 | ||
161 | for slot in edge_slots: | 161 | for slot in edge_slots: |
162 | slots[slot] = {} | 162 | slots[slot] = {} |
163 | for no in item_no_list: | 163 | for no in item_no_list: |
164 | slots[slot][no] = None | 164 | slots[slot][no] = None |
165 | 165 | ||
166 | for slot in xrange(start_slot, end_slot + 1): | 166 | for slot in xrange(start_slot, end_slot + 1): |
167 | if slot not in slots: | 167 | if slot not in slots: |
168 | slots[slot] = {} | 168 | slots[slot] = {} |
169 | for no in item_no_list: | 169 | for no in item_no_list: |
170 | slots[slot][no] = None | 170 | slots[slot][no] = None |
171 | 171 | ||
172 | class Schedule(object): | 172 | class Schedule(object): |
173 | """The total schedule (task system), consisting of a certain number of | 173 | """The total schedule (task system), consisting of a certain number of |
174 | tasks.""" | 174 | tasks.""" |
@@ -213,8 +213,8 @@ class Schedule(object): | |||
213 | self.time_slot_array = TimeSlotArray() | 213 | self.time_slot_array = TimeSlotArray() |
214 | return | 214 | return |
215 | 215 | ||
216 | self.time_slot_array = TimeSlotArray(self.time_per_maj, \ | 216 | self.time_slot_array = TimeSlotArray(self.time_per_maj, |
217 | len(self.task_list), self.num_cpus) | 217 | len(self.task_list), self.num_cpus) |
218 | 218 | ||
219 | def get_time_slot_array(self): | 219 | def get_time_slot_array(self): |
220 | return self.time_slot_array | 220 | return self.time_slot_array |
@@ -226,6 +226,7 @@ class Schedule(object): | |||
226 | self.start = None | 226 | self.start = None |
227 | self.end = None | 227 | self.end = None |
228 | 228 | ||
229 | self.sort_task_nos_numeric() | ||
229 | self.set_time_params(time_per_maj) | 230 | self.set_time_params(time_per_maj) |
230 | 231 | ||
231 | # we scan the graph task by task, and job by job | 232 | # we scan the graph task by task, and job by job |
@@ -250,10 +251,10 @@ class Schedule(object): | |||
250 | switch_event = switches[event] | 251 | switch_event = switches[event] |
251 | if switch_event is not None: | 252 | if switch_event is not None: |
252 | event.fill_span_event_from_start() | 253 | event.fill_span_event_from_start() |
253 | 254 | ||
254 | # add events that correspond to the tasks and CPUS, at the very beginning | 255 | # add events that correspond to the tasks and CPUS, at the very beginning |
255 | self.time_slot_array.add_item_dummies(self) | 256 | self.time_slot_array.add_item_dummies(self) |
256 | 257 | ||
257 | def add_task(self, task): | 258 | def add_task(self, task): |
258 | if task.name in self.tasks: | 259 | if task.name in self.tasks: |
259 | raise ValueError("task already in list!") | 260 | raise ValueError("task already in list!") |
@@ -263,6 +264,17 @@ class Schedule(object): | |||
263 | task.task_no = self.cur_task_no | 264 | task.task_no = self.cur_task_no |
264 | self.cur_task_no += 1 | 265 | self.cur_task_no += 1 |
265 | 266 | ||
267 | def sort_task_nos_numeric(self): | ||
268 | # sort task numbers by the numeric value of the task names. | ||
269 | nums = [] | ||
270 | |||
271 | for task_name in self.tasks: | ||
272 | nums.append((int(task_name), task_name)) | ||
273 | |||
274 | nums.sort(key=lambda t: t[0]) | ||
275 | for no, task in enumerate(nums): | ||
276 | self.tasks[task[1]].task_no = no | ||
277 | |||
266 | def get_tasks(self): | 278 | def get_tasks(self): |
267 | return self.tasks | 279 | return self.tasks |
268 | 280 | ||
@@ -281,6 +293,11 @@ def deepcopy_selected(selected): | |||
281 | selected_copy[layer] = copy.copy(selected[layer]) | 293 | selected_copy[layer] = copy.copy(selected[layer]) |
282 | return selected_copy | 294 | return selected_copy |
283 | 295 | ||
296 | def _format_time(time, unit): | ||
297 | if time is None: | ||
298 | return '(None)' | ||
299 | return util.format_float(unit.nsec_to_native(time), Event.NUM_DEC_PLACES) \ | ||
300 | + ' ' + str(unit) | ||
284 | class Task(object): | 301 | class Task(object): |
285 | """Represents a task, including the set of jobs that were run under | 302 | """Represents a task, including the set of jobs that were run under |
286 | this task.""" | 303 | this task.""" |
@@ -317,15 +334,75 @@ class Job(object): | |||
317 | self.job_no = job_no | 334 | self.job_no = job_no |
318 | self.events = {} | 335 | self.events = {} |
319 | self.task = None | 336 | self.task = None |
337 | self.start_run_time = None | ||
338 | self.end_run_time = None | ||
339 | self.release_time = None | ||
340 | self.deadline_time = None | ||
320 | for event in event_list: | 341 | for event in event_list: |
321 | self.add_event(event) | 342 | self.add_event(event) |
322 | 343 | ||
344 | def get_name(self): | ||
345 | return 'Job ' + str(self.job_no) + ' for task ' + str(self.get_task().get_task_no()) | ||
346 | |||
347 | def str_long(self, unit): | ||
348 | duration = None | ||
349 | if self.start_run_time is not None and self.end_run_time is not None: | ||
350 | duration = self.end_run_time - self.start_run_time | ||
351 | |||
352 | return_str = 'Job Information\n---------------' + \ | ||
353 | '\nTask Name: ' + str(self.get_task().get_name()) + \ | ||
354 | '\n(Task no., Job no.): ' + str((self.get_task().get_task_no(), \ | ||
355 | self.get_job_no())) + \ | ||
356 | '\nStart Run Time: ' + _format_time(self.start_run_time, unit) | ||
357 | |||
358 | if duration is not None: | ||
359 | return_str += '\nDuration: ' + _format_time(duration, unit) | ||
360 | |||
361 | return_str += '\nEnd Run Time: ' + _format_time(self.end_run_time, unit) \ | ||
362 | + '\nRelease Time: ' + _format_time(self.release_time, unit) \ | ||
363 | + '\nDeadline Time: ' + _format_time(self.deadline_time, unit) | ||
364 | |||
365 | return return_str | ||
366 | |||
367 | def str_run_short(self, unit): | ||
368 | start = self.start_run_time | ||
369 | end = self.end_run_time | ||
370 | duration = None | ||
371 | if start is not None and end is not None: | ||
372 | duration = end - start | ||
373 | |||
374 | return_str = self.get_name() + ': START=' + _format_time(start, unit) | ||
375 | |||
376 | if duration is not None: | ||
377 | return_str += ', DUR=' + _format_time(duration, unit) | ||
378 | |||
379 | return_str += ', END=' + _format_time(end, unit) | ||
380 | |||
381 | return return_str | ||
382 | |||
383 | def str_rd_short(self, unit): | ||
384 | return self.get_name() + ': RELEASE=' + _format_time(self.release_time, unit) \ | ||
385 | + ', DEADLINE=' + _format_time(self.deadline_time, unit) | ||
386 | |||
323 | def add_event(self, event): | 387 | def add_event(self, event): |
324 | if event.time not in self.events: | 388 | if event.time not in self.events: |
325 | self.events[event.time] = [] | 389 | self.events[event.get_time()] = [] |
326 | self.events[event.time].append(event) | 390 | self.events[event.get_time()].append(event) |
327 | event.job = self | 391 | event.job = self |
328 | 392 | ||
393 | # update some statistics | ||
394 | if isinstance(event, ReleaseEvent): | ||
395 | if self.release_time is None or event.get_time() < self.release_time: | ||
396 | self.release_time = event.get_time() | ||
397 | elif isinstance(event, DeadlineEvent): | ||
398 | if self.deadline_time is None or event.get_time() > self.deadline_time: | ||
399 | self.deadline_time = event.get_time() | ||
400 | else: | ||
401 | if self.start_run_time is None or event.get_time() < self.start_run_time: | ||
402 | self.start_run_time = event.get_time() | ||
403 | if self.end_run_time is None or event.get_time() > self.end_run_time: | ||
404 | self.end_run_time = event.get_time() | ||
405 | |||
329 | def get_events(self): | 406 | def get_events(self): |
330 | return self.events | 407 | return self.events |
331 | 408 | ||
@@ -348,9 +425,6 @@ class DummyEvent(object): | |||
348 | self.job = None | 425 | self.job = None |
349 | self.layer = None | 426 | self.layer = None |
350 | 427 | ||
351 | def __str__(self): | ||
352 | return '[Dummy Event]' | ||
353 | |||
354 | def get_time(self): | 428 | def get_time(self): |
355 | return self.time | 429 | return self.time |
356 | 430 | ||
@@ -359,21 +433,21 @@ class DummyEvent(object): | |||
359 | 433 | ||
360 | def get_schedule(self): | 434 | def get_schedule(self): |
361 | return self.get_task().get_schedule() | 435 | return self.get_task().get_schedule() |
362 | 436 | ||
363 | def get_task(self): | 437 | def get_task(self): |
364 | return self.get_job().get_task() | 438 | return self.get_job().get_task() |
365 | 439 | ||
366 | def get_job(self): | 440 | def get_job(self): |
367 | return self.job | 441 | return self.job |
368 | 442 | ||
369 | def get_layer(self): | 443 | def get_layer(self): |
370 | return self.layer | 444 | return self.layer |
371 | 445 | ||
372 | def is_selected(self): | 446 | def is_selected(self): |
373 | """Returns whether the event has been selected by the user. (needed for rendering)""" | 447 | """Returns whether the event has been selected by the user. (needed for rendering)""" |
374 | selected = self.get_schedule().get_selected() | 448 | selected = self.get_schedule().get_selected() |
375 | return self.get_layer() in selected and self in selected[self.get_layer()] | 449 | return self.get_layer() in selected and self in selected[self.get_layer()] |
376 | 450 | ||
377 | def render(self, graph, layer, prev_events, selectable=False): | 451 | def render(self, graph, layer, prev_events, selectable=False): |
378 | """Method that the visualizer calls to tell the event to render itself | 452 | """Method that the visualizer calls to tell the event to render itself |
379 | Obviously only implemented by subclasses (actual event types) | 453 | Obviously only implemented by subclasses (actual event types) |
@@ -396,17 +470,21 @@ class Event(DummyEvent): | |||
396 | def get_name(self): | 470 | def get_name(self): |
397 | raise NotImplementedError | 471 | raise NotImplementedError |
398 | 472 | ||
399 | def __str__(self): | 473 | def str_short(self, unit): |
400 | return self.get_name() + self._common_str() + ', TIME=' + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) | 474 | return self.get_name() + self._common_str() + ', TIME=' + \ |
475 | util.format_float(unit.nsec_to_native(self.get_time()), Event.NUM_DEC_PLACES) + \ | ||
476 | ' ' + str(unit) | ||
401 | 477 | ||
402 | def str_long(self): | 478 | def str_long(self, unit): |
403 | """Prints the event as a string, in ``long'' form.""" | 479 | """Prints the event as a string, in ``long'' form.""" |
404 | return 'Event Type: ' + self.get_name() + \ | 480 | return 'Event Information\n-----------------\n' + \ |
481 | 'Event Type: ' + self.get_name() + \ | ||
405 | '\nTask Name: ' + str(self.get_job().get_task().get_name()) + \ | 482 | '\nTask Name: ' + str(self.get_job().get_task().get_name()) + \ |
406 | '\n(Task no., Job no.): ' + str((self.get_job().get_task().get_task_no(), \ | 483 | '\n(Task no., Job no.): ' + str((self.get_job().get_task().get_task_no(), \ |
407 | self.get_job().get_job_no())) + \ | 484 | self.get_job().get_job_no())) + \ |
408 | '\nCPU: ' + str(self.get_cpu()) + \ | 485 | '\nCPU: ' + str(self.get_cpu()) + \ |
409 | '\nTime: ' + str(self.get_time()) | 486 | '\nTime: ' + _format_time(self.get_time(), unit) + \ |
487 | '\n\n' + self.get_job().str_long(unit) | ||
410 | 488 | ||
411 | def _common_str(self): | 489 | def _common_str(self): |
412 | job = self.get_job() | 490 | job = self.get_job() |
@@ -440,7 +518,7 @@ class Event(DummyEvent): | |||
440 | TimeSlotArray.CPU_LIST : self.get_cpu() } | 518 | TimeSlotArray.CPU_LIST : self.get_cpu() } |
441 | sched.get_time_slot_array().add_event_to_time_slot(self, item_nos) | 519 | sched.get_time_slot_array().add_event_to_time_slot(self, item_nos) |
442 | self.fill_span_event_from_end() | 520 | self.fill_span_event_from_end() |
443 | 521 | ||
444 | def fill_span_event_from_start(self): | 522 | def fill_span_event_from_start(self): |
445 | """This method exists for events that can ``range'' over a period of time | 523 | """This method exists for events that can ``range'' over a period of time |
446 | (e.g. SwitchAway and SwitchTo). In case a start event is not paired with | 524 | (e.g. SwitchAway and SwitchTo). In case a start event is not paired with |
@@ -448,11 +526,11 @@ class Event(DummyEvent): | |||
448 | the way to the beginning or end. Since most events occur only at a specific | 526 | the way to the beginning or end. Since most events occur only at a specific |
449 | time, this is usually a no-op.""" | 527 | time, this is usually a no-op.""" |
450 | pass | 528 | pass |
451 | 529 | ||
452 | def fill_span_event_from_end(self): | 530 | def fill_span_event_from_end(self): |
453 | """The mirror image of the last method.""" | 531 | """The mirror image of the last method.""" |
454 | pass | 532 | pass |
455 | 533 | ||
456 | class SpanEvent(Event): | 534 | class SpanEvent(Event): |
457 | def __init__(self, time, cpu, dummy_class): | 535 | def __init__(self, time, cpu, dummy_class): |
458 | super(SpanEvent, self).__init__(time, cpu) | 536 | super(SpanEvent, self).__init__(time, cpu) |
@@ -461,54 +539,93 @@ class SpanEvent(Event): | |||
461 | class SpanDummy(DummyEvent): | 539 | class SpanDummy(DummyEvent): |
462 | def __init__(self): | 540 | def __init__(self): |
463 | super(SpanDummy, self).__init__(None, None) | 541 | super(SpanDummy, self).__init__(None, None) |
464 | 542 | ||
465 | def get_task(self): | 543 | def get_task(self): |
466 | if self.corresp_start_event is not None: | 544 | if self.corresp_start_event is not None: |
467 | return self.corresp_start_event.get_task() | 545 | return self.corresp_start_event.get_task() |
468 | if self.corresp_end_event is not None: | 546 | if self.corresp_end_event is not None: |
469 | return self.corresp_end_event.get_task() | 547 | return self.corresp_end_event.get_task() |
470 | return None | 548 | return None |
471 | 549 | ||
472 | def get_schedule(self): | 550 | def get_schedule(self): |
473 | if self.corresp_start_event is not None: | 551 | if self.corresp_start_event is not None: |
474 | return self.corresp_start_event.get_schedule() | 552 | return self.corresp_start_event.get_schedule() |
475 | if self.corresp_end_event is not None: | 553 | if self.corresp_end_event is not None: |
476 | return self.corresp_end_event.get_schedule() | 554 | return self.corresp_end_event.get_schedule() |
477 | return None | 555 | return None |
478 | 556 | ||
479 | def get_job(self): | 557 | def get_job(self): |
480 | if self.corresp_start_event is not None: | 558 | if self.corresp_start_event is not None: |
481 | return self.corresp_start_event.get_job() | 559 | return self.corresp_start_event.get_job() |
482 | if self.corresp_end_event is not None: | 560 | if self.corresp_end_event is not None: |
483 | return self.corresp_end_event.get_job() | 561 | return self.corresp_end_event.get_job() |
484 | return None | 562 | return None |
485 | 563 | ||
486 | def get_cpu(self): | 564 | def get_cpu(self): |
487 | if self.corresp_start_event is not None: | 565 | if self.corresp_start_event is not None: |
488 | return self.corresp_start_event.get_cpu() | 566 | return self.corresp_start_event.get_cpu() |
489 | if self.corresp_end_event is not None: | 567 | if self.corresp_end_event is not None: |
490 | return self.corresp_end_event.get_cpu() | 568 | return self.corresp_end_event.get_cpu() |
491 | return None | 569 | return None |
492 | 570 | ||
493 | def get_time(self): | 571 | def get_time(self): |
494 | if self.corresp_start_event is not None: | 572 | if self.corresp_start_event is not None: |
495 | return self.corresp_start_event.get_time() | 573 | return self.corresp_start_event.get_time() |
496 | if self.corresp_end_event is not None: | 574 | if self.corresp_end_event is not None: |
497 | return self.corresp_end_event.get_time() | 575 | return self.corresp_end_event.get_time() |
498 | return None | 576 | return None |
499 | 577 | ||
500 | class StartSpanEvent(SpanEvent): | 578 | class StartSpanEvent(SpanEvent): |
579 | def str_short(self, unit): | ||
580 | if self.corresp_end_event is None: | ||
581 | return super(StartSpanEvent, self).str_short(unit) | ||
582 | start = self.get_time() | ||
583 | end = self.corresp_end_event.get_time() | ||
584 | duration = end - start | ||
585 | return self.get_name() + self._common_str() + ', START=' \ | ||
586 | + _format_time(start, unit) \ | ||
587 | + ', DUR=' + _format_time(duration, unit) \ | ||
588 | + ', END=' + _format_time(end, unit) | ||
589 | |||
590 | def str_long(self, unit): | ||
591 | if self.corresp_end_event is None: | ||
592 | return super(StartSpanEvent, self).str_long(unit) | ||
593 | else: | ||
594 | start = self.get_time() | ||
595 | end = self.corresp_end_event.get_time() | ||
596 | duration = end - start | ||
597 | return 'Event Type: ' + self.get_name() + \ | ||
598 | '\nTask Name: ' + str(self.get_job().get_task().get_name()) + \ | ||
599 | '\n(Task no., Job no.): ' + str((self.get_job().get_task().get_task_no(), \ | ||
600 | self.get_job().get_job_no())) + \ | ||
601 | '\nCPU: ' + str(self.get_cpu()) + \ | ||
602 | '\nStart: ' + _format_time(start, unit) + \ | ||
603 | '\nDuration: ' + _format_time(duration, unit) + \ | ||
604 | '\nEnd: ' + _format_time(end, unit) + \ | ||
605 | '\n\n' + self.get_job().str_long(unit) | ||
606 | |||
501 | def fill_span_event_from_start(self): | 607 | def fill_span_event_from_start(self): |
502 | item_nos = { TimeSlotArray.TASK_LIST : self.get_task().get_task_no(), | 608 | item_nos = { TimeSlotArray.TASK_LIST : self.get_task().get_task_no(), |
503 | TimeSlotArray.CPU_LIST : self.get_cpu() } | 609 | TimeSlotArray.CPU_LIST : self.get_cpu() } |
504 | self.get_schedule().get_time_slot_array().fill_span_event_from_start(self, item_nos) | 610 | self.get_schedule().get_time_slot_array().fill_span_event_from_start(self, item_nos) |
505 | 611 | ||
506 | class EndSpanEvent(SpanEvent): | 612 | class EndSpanEvent(SpanEvent): |
613 | def str_short(self, unit): | ||
614 | if self.corresp_start_event is None: | ||
615 | return super(EndSpanEvent, self).str_short(unit) | ||
616 | return self.corresp_start_event.str_short(unit) | ||
617 | |||
618 | def str_long(self, unit): | ||
619 | if self.corresp_start_event is None: | ||
620 | return super(EndSpanEvent, self).str_long(unit) | ||
621 | else: | ||
622 | return self.corresp_start_event.str_long(unit) | ||
623 | |||
507 | def fill_span_event_from_end(self): | 624 | def fill_span_event_from_end(self): |
508 | item_nos = { TimeSlotArray.TASK_LIST : self.get_task().get_task_no(), | 625 | item_nos = { TimeSlotArray.TASK_LIST : self.get_task().get_task_no(), |
509 | TimeSlotArray.CPU_LIST : self.get_cpu() } | 626 | TimeSlotArray.CPU_LIST : self.get_cpu() } |
510 | self.get_schedule().get_time_slot_array().fill_span_event_from_end(self, item_nos) | 627 | self.get_schedule().get_time_slot_array().fill_span_event_from_end(self, item_nos) |
511 | 628 | ||
512 | class SuspendEvent(Event): | 629 | class SuspendEvent(Event): |
513 | def __init__(self, time, cpu): | 630 | def __init__(self, time, cpu): |
514 | super(SuspendEvent, self).__init__(time, cpu) | 631 | super(SuspendEvent, self).__init__(time, cpu) |
@@ -592,25 +709,6 @@ class SwitchToEvent(StartSpanEvent): | |||
592 | else: | 709 | else: |
593 | return 'Scheduled' | 710 | return 'Scheduled' |
594 | 711 | ||
595 | def __str__(self): | ||
596 | if self.corresp_end_event is None: | ||
597 | return super(SwitchToEvent, self).__str__() | ||
598 | return self.get_name() + self._common_str() + ', START=' \ | ||
599 | + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) \ | ||
600 | + ', END=' + util.format_float(self.corresp_end_event.get_time(), Event.NUM_DEC_PLACES) | ||
601 | |||
602 | def str_long(self): | ||
603 | if self.corresp_end_event is None: | ||
604 | return super(SwitchToEvent, self).str_long() | ||
605 | else : | ||
606 | return 'Event Type: ' + self.get_name() + \ | ||
607 | '\nTask Name: ' + str(self.get_job().get_task().get_name()) + \ | ||
608 | '\n(Task no., Job no.): ' + str((self.get_job().get_task().get_task_no(), \ | ||
609 | self.get_job().get_job_no())) + \ | ||
610 | '\nCPU: ' + str(self.get_cpu()) + \ | ||
611 | '\nStart: ' + str(self.get_time()) + \ | ||
612 | '\nEnd: ' + str(self.corresp_end_event.get_time()) | ||
613 | |||
614 | def scan(self, cur_cpu, switches): | 712 | def scan(self, cur_cpu, switches): |
615 | old_cur_cpu = cur_cpu[0] | 713 | old_cur_cpu = cur_cpu[0] |
616 | cur_cpu[0] = self.get_cpu() | 714 | cur_cpu[0] = self.get_cpu() |
@@ -656,17 +754,6 @@ class SwitchAwayEvent(EndSpanEvent): | |||
656 | else: | 754 | else: |
657 | return 'Scheduled' | 755 | return 'Scheduled' |
658 | 756 | ||
659 | def __str__(self): | ||
660 | if self.corresp_start_event is None: | ||
661 | return super(SwitchAwayEvent, self).__str__() | ||
662 | return str(self.corresp_start_event) | ||
663 | |||
664 | def str_long(self): | ||
665 | if self.corresp_start_event is None: | ||
666 | return super(SwitchAwayEvent, self).str_long() | ||
667 | |||
668 | return self.corresp_start_event.str_long() | ||
669 | |||
670 | def scan(self, cur_cpu, switches): | 757 | def scan(self, cur_cpu, switches): |
671 | old_cur_cpu = cur_cpu[0] | 758 | old_cur_cpu = cur_cpu[0] |
672 | 759 | ||
@@ -772,30 +859,11 @@ class InversionStartEvent(StartSpanEvent): | |||
772 | else: | 859 | else: |
773 | return 'Priority Inversion' | 860 | return 'Priority Inversion' |
774 | 861 | ||
775 | def __str__(self): | ||
776 | if self.corresp_end_event is None: | ||
777 | return super(InversionStartEvent, self).__str__() | ||
778 | return self.get_name() + self._common_str() + ', START=' \ | ||
779 | + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) \ | ||
780 | + ', END=' + util.format_float(self.corresp_end_event.get_time(), Event.NUM_DEC_PLACES) | ||
781 | |||
782 | def str_long(self): | ||
783 | if self.corresp_end_event is None: | ||
784 | return super(InversionStartEvent, self).str_long() | ||
785 | else : | ||
786 | return 'Event Type: ' + self.get_name() + \ | ||
787 | '\nTask Name: ' + str(self.get_job().get_task().get_name()) + \ | ||
788 | '\n(Task no., Job no.): ' + str((self.get_job().get_task().get_task_no(), \ | ||
789 | self.get_job().get_job_no())) + \ | ||
790 | '\nCPU: ' + str(self.get_cpu()) + \ | ||
791 | '\nStart: ' + str(self.get_time()) + \ | ||
792 | '\nEnd: ' + str(self.corresp_end_event.get_time()) | ||
793 | |||
794 | def fill_span_event_from_start(self): | 862 | def fill_span_event_from_start(self): |
795 | item_nos = { TimeSlotArray.TASK_LIST : self.get_task().get_task_no(), | 863 | item_nos = { TimeSlotArray.TASK_LIST : self.get_task().get_task_no(), |
796 | TimeSlotArray.CPU_LIST : TimeSlotArray.POST_ITEM_NO } | 864 | TimeSlotArray.CPU_LIST : TimeSlotArray.POST_ITEM_NO } |
797 | self.get_schedule().get_time_slot_array().fill_span_event_from_start(self, item_nos) | 865 | self.get_schedule().get_time_slot_array().fill_span_event_from_start(self, item_nos) |
798 | 866 | ||
799 | def scan(self, cur_cpu, switches): | 867 | def scan(self, cur_cpu, switches): |
800 | switches[InversionStartEvent] = self | 868 | switches[InversionStartEvent] = self |
801 | self.corresp_end_event = None | 869 | self.corresp_end_event = None |
@@ -840,23 +908,11 @@ class InversionEndEvent(EndSpanEvent): | |||
840 | else: | 908 | else: |
841 | return 'Priority Inversion' | 909 | return 'Priority Inversion' |
842 | 910 | ||
843 | def __str__(self): | ||
844 | if self.corresp_start_event is None: | ||
845 | return super(InversionEndEvent, self).__str__() | ||
846 | |||
847 | return str(self.corresp_start_event) | ||
848 | |||
849 | def str_long(self): | ||
850 | if self.corresp_start_event is None: | ||
851 | return super(InversionEndEvent, self).str_long() | ||
852 | |||
853 | return self.corresp_start_event.str_long() | ||
854 | |||
855 | def fill_span_event_from_end(self): | 911 | def fill_span_event_from_end(self): |
856 | item_nos = { TimeSlotArray.TASK_LIST : self.get_task().get_task_no(), | 912 | item_nos = { TimeSlotArray.TASK_LIST : self.get_task().get_task_no(), |
857 | TimeSlotArray.CPU_LIST : TimeSlotArray.POST_ITEM_NO } | 913 | TimeSlotArray.CPU_LIST : TimeSlotArray.POST_ITEM_NO } |
858 | self.get_schedule().get_time_slot_array().fill_span_event_from_end(self, item_nos) | 914 | self.get_schedule().get_time_slot_array().fill_span_event_from_end(self, item_nos) |
859 | 915 | ||
860 | def scan(self, cur_cpu, switches): | 916 | def scan(self, cur_cpu, switches): |
861 | self.corresp_start_event = switches[InversionStartEvent] | 917 | self.corresp_start_event = switches[InversionStartEvent] |
862 | 918 | ||
@@ -932,25 +988,25 @@ class TaskDummy(DummyEvent): | |||
932 | super(TaskDummy, self).__init__(time, None) | 988 | super(TaskDummy, self).__init__(time, None) |
933 | self.task = task | 989 | self.task = task |
934 | self.layer = Canvas.BOTTOM_LAYER | 990 | self.layer = Canvas.BOTTOM_LAYER |
935 | 991 | ||
936 | def get_name(self): | 992 | def get_name(self): |
937 | return 'Task' | 993 | return 'Task' |
938 | 994 | ||
939 | def __str__(self): | 995 | def str_short(self, unit): |
940 | return 'Task ' + self.task.get_name() | 996 | return 'Task ' + self.task.get_name() |
941 | 997 | ||
942 | def str_long(self): | 998 | def str_long(self, unit): |
943 | return 'Task ' + self.task.get_name() | 999 | return 'Task ' + self.task.get_name() |
944 | 1000 | ||
945 | def get_task(self): | 1001 | def get_task(self): |
946 | return self.task | 1002 | return self.task |
947 | 1003 | ||
948 | def render(self, graph, layer, prev_events, selectable=False): | 1004 | def render(self, graph, layer, prev_events, selectable=False): |
949 | if selectable: | 1005 | if selectable: |
950 | graph.add_sel_task_item(self.task.get_task_no(), self) | 1006 | graph.add_sel_task_item(self.task.get_task_no(), self) |
951 | else: | 1007 | else: |
952 | graph.draw_task_item(self.task.get_task_no(), self.is_selected()) | 1008 | graph.draw_task_item(self.task.get_task_no(), self.is_selected()) |
953 | 1009 | ||
954 | class CPUDummy(DummyEvent): | 1010 | class CPUDummy(DummyEvent): |
955 | """A dummy event that represents a certain CPU. Used for rendering the CPU's ``name'' | 1011 | """A dummy event that represents a certain CPU. Used for rendering the CPU's ``name'' |
956 | (i.e. ``CPU #n'')""" | 1012 | (i.e. ``CPU #n'')""" |
@@ -958,29 +1014,29 @@ class CPUDummy(DummyEvent): | |||
958 | super(CPUDummy, self).__init__(time, cpu) | 1014 | super(CPUDummy, self).__init__(time, cpu) |
959 | self.sched = sched | 1015 | self.sched = sched |
960 | self.layer = Canvas.BOTTOM_LAYER | 1016 | self.layer = Canvas.BOTTOM_LAYER |
961 | 1017 | ||
962 | def get_name(self): | 1018 | def get_name(self): |
963 | return 'CPU' | 1019 | return 'CPU' |
964 | 1020 | ||
965 | def __str__(self): | 1021 | def str_short(self, unit): |
966 | return 'CPU #' + str(self.get_cpu()) | 1022 | return 'CPU #' + str(self.get_cpu()) |
967 | 1023 | ||
968 | def str_long(self): | 1024 | def str_long(self, unit): |
969 | return 'CPU #' + str(self.get_cpu()) | 1025 | return 'CPU #' + str(self.get_cpu()) |
970 | 1026 | ||
971 | def get_task(self): | 1027 | def get_task(self): |
972 | return None | 1028 | return None |
973 | 1029 | ||
974 | def get_schedule(self): | 1030 | def get_schedule(self): |
975 | # we don't have a job, so get the schedule from the schedule passed in | 1031 | # we don't have a job, so get the schedule from the schedule passed in |
976 | return self.sched | 1032 | return self.sched |
977 | 1033 | ||
978 | def render(self, graph, layer, prev_events, selectable=False): | 1034 | def render(self, graph, layer, prev_events, selectable=False): |
979 | if selectable: | 1035 | if selectable: |
980 | graph.add_sel_cpu_item(self.cpu, self) | 1036 | graph.add_sel_cpu_item(self.cpu, self) |
981 | else: | 1037 | else: |
982 | graph.draw_cpu_item(self.cpu, self.is_selected()) | 1038 | graph.draw_cpu_item(self.cpu, self.is_selected()) |
983 | 1039 | ||
984 | EVENT_LIST = {SuspendEvent : None, ResumeEvent : None, CompleteEvent : None, | 1040 | EVENT_LIST = {SuspendEvent : None, ResumeEvent : None, CompleteEvent : None, |
985 | SwitchAwayEvent : None, SwitchToEvent : None, ReleaseEvent : None, | 1041 | SwitchAwayEvent : None, SwitchToEvent : None, ReleaseEvent : None, |
986 | DeadlineEvent : None, IsRunningDummy : None, | 1042 | DeadlineEvent : None, IsRunningDummy : None, |
diff --git a/unit_trace/viz/util.py b/unit_trace/viz/util.py index 3111f39..430063b 100644 --- a/unit_trace/viz/util.py +++ b/unit_trace/viz/util.py | |||
@@ -1,9 +1,78 @@ | |||
1 | #!/usr/bin/python | ||
2 | |||
3 | """Miscellanious utility functions that don't fit anywhere.""" | 1 | """Miscellanious utility functions that don't fit anywhere.""" |
4 | 2 | ||
5 | def format_float(num, numplaces): | 3 | def format_float(num, numplaces): |
4 | if num is None: | ||
5 | return '(None)' | ||
6 | |||
6 | if abs(round(num, numplaces) - round(num, 0)) == 0.0: | 7 | if abs(round(num, numplaces) - round(num, 0)) == 0.0: |
7 | return '%.0f' % float(num) | 8 | return '%.0f' % float(num) |
8 | else: | 9 | else: |
9 | return ('%.' + numplaces + 'f') % round(float(num), numplaces) | 10 | return ('%.' + str(numplaces) + 'f') % round(float(num), numplaces) |
11 | |||
12 | class TimeUnit(object): | ||
13 | """Class that represents a certain time unit.""" | ||
14 | def nsec_to_native(self, time): | ||
15 | raise NotImplementedError | ||
16 | |||
17 | def native_to_nsec(self, time): | ||
18 | raise NotImplementedError | ||
19 | |||
20 | class NanoSec(TimeUnit): | ||
21 | def __str__(self): | ||
22 | return 'ns' | ||
23 | |||
24 | def nsec_to_native(self, time): | ||
25 | return time | ||
26 | |||
27 | def native_to_nsec(self, time): | ||
28 | return time | ||
29 | |||
30 | class MicroSec(TimeUnit): | ||
31 | def __str__(self): | ||
32 | return 'us' | ||
33 | |||
34 | def nsec_to_native(self, time): | ||
35 | if time is None: return time | ||
36 | return time / 1000.0 | ||
37 | |||
38 | def native_to_nsec(self, time): | ||
39 | if time is None: return time | ||
40 | return time * 1000.0 | ||
41 | |||
42 | class MilliSec(TimeUnit): | ||
43 | def __str__(self): | ||
44 | return 'ms' | ||
45 | |||
46 | def nsec_to_native(self, time): | ||
47 | if time is None: return time | ||
48 | return time / 1000000.0 | ||
49 | |||
50 | def native_to_nsec(self, time): | ||
51 | if time is None: return time | ||
52 | return time * 1000000.0 | ||
53 | |||
54 | class Sec(TimeUnit): | ||
55 | def __str__(self): | ||
56 | return 's' | ||
57 | |||
58 | def nsec_to_native(self, time): | ||
59 | if time is None: return time | ||
60 | return time / 1000000000.0 | ||
61 | |||
62 | def native_to_nsec(self, time): | ||
63 | if time is None: return time | ||
64 | return time * 1000000000.0 | ||
65 | |||
66 | def parse_unit(expr): | ||
67 | expr = expr.strip().lower() | ||
68 | |||
69 | if expr == 'ns' or expr == 'nsec' or expr == 'nsecs' or expr == 'nanosec' or expr == 'nanosecs': | ||
70 | return NanoSec() | ||
71 | elif expr == 'us' or expr == 'usec' or expr == 'usecs' or expr == 'microsec' or expr == 'microsecs': | ||
72 | return MicroSec() | ||
73 | elif expr == 'ms' or expr == 'msec' or expr == 'msecs' or expr == 'millisec' or expr == 'millisecs': | ||
74 | return MilliSec() | ||
75 | elif expr == 's' or expr == 'sec' or expr == 'secs' or expr == 'second' or expr == 'seconds': | ||
76 | return Sec() | ||
77 | else: | ||
78 | raise ValueError | ||
diff --git a/unit_trace/viz/viewer.py b/unit_trace/viz/viewer.py index 4f7a511..bb1385c 100644 --- a/unit_trace/viz/viewer.py +++ b/unit_trace/viz/viewer.py | |||
@@ -30,7 +30,7 @@ class GraphContextMenu(gtk.Menu): | |||
30 | else: | 30 | else: |
31 | for layer in selected: | 31 | for layer in selected: |
32 | for event in selected[layer]: | 32 | for event in selected[layer]: |
33 | string = str(event) | 33 | string = event.str_short(info_win.get_unit()) |
34 | if len(string) > GraphContextMenu.MAX_STR_LEN - 3: | 34 | if len(string) > GraphContextMenu.MAX_STR_LEN - 3: |
35 | string = string[:GraphContextMenu.MAX_STR_LEN - 3] + '...' | 35 | string = string[:GraphContextMenu.MAX_STR_LEN - 3] + '...' |
36 | item = gtk.MenuItem(string) | 36 | item = gtk.MenuItem(string) |
@@ -68,19 +68,23 @@ class GraphArea(gtk.DrawingArea): | |||
68 | self.set_set_scroll_adjustments_signal('set-scroll-adjustments') | 68 | self.set_set_scroll_adjustments_signal('set-scroll-adjustments') |
69 | 69 | ||
70 | self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK | | 70 | self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK | |
71 | gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.EXPOSURE_MASK) | 71 | gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.EXPOSURE_MASK | gtk.gdk.LEAVE_NOTIFY_MASK) |
72 | 72 | ||
73 | self.band_rect = None | 73 | self.band_rect = None |
74 | self.ctrl_clicked = False | 74 | self.ctrl_clicked = False |
75 | self.cursor_pos = None | ||
75 | self.last_selected = {} | 76 | self.last_selected = {} |
76 | self.dirtied_regions = [] | 77 | self.dirtied_regions = [] |
77 | 78 | ||
79 | self.get_graph().update_view(self.cur_x, self.cur_y, self.width, self.height, self.scale) | ||
80 | |||
78 | self.connect('expose-event', self.expose) | 81 | self.connect('expose-event', self.expose) |
79 | self.connect('size-allocate', self.size_allocate) | 82 | self.connect('size-allocate', self.size_allocate) |
80 | self.connect('set-scroll-adjustments', self.set_scroll_adjustments) | 83 | self.connect('set-scroll-adjustments', self.set_scroll_adjustments) |
81 | self.connect('button-press-event', self.button_press) | 84 | self.connect('button-press-event', self.button_press) |
82 | self.connect('button-release-event', self.button_release) | 85 | self.connect('button-release-event', self.button_release) |
83 | self.connect('motion-notify-event', self.motion_notify) | 86 | self.connect('motion-notify-event', self.motion_notify) |
87 | self.connect('leave-notify-event', self.leave_notify) | ||
84 | 88 | ||
85 | def output_to_file(self, filename, ftype): | 89 | def output_to_file(self, filename, ftype): |
86 | """Outputs the entire graph to a file.""" | 90 | """Outputs the entire graph to a file.""" |
@@ -92,17 +96,17 @@ class GraphArea(gtk.DrawingArea): | |||
92 | surface = SVGSurface() | 96 | surface = SVGSurface() |
93 | else: | 97 | else: |
94 | raise ValueError | 98 | raise ValueError |
95 | 99 | ||
96 | surface.renew(graph.get_width(), graph.get_height()) | 100 | surface.renew(graph.get_width(), graph.get_height()) |
97 | graph.set_tmp_surface(surface) | 101 | graph.set_tmp_surface(surface) |
98 | graph.render_all(self.renderer.get_schedule()) | 102 | graph.render_all(self.renderer.get_schedule()) |
99 | surface.write_out(filename) | 103 | surface.write_out(filename) |
100 | graph.revert_surface() | 104 | graph.revert_surface() |
101 | 105 | ||
102 | def expose(self, widget, expose_event, data=None): | 106 | def expose(self, widget, expose_event, data=None): |
103 | ctx = widget.window.cairo_create() | 107 | ctx = widget.window.cairo_create() |
104 | graph = self.renderer.get_graph() | 108 | graph = self.get_graph() |
105 | graph.update_view(self.cur_x, self.cur_y, self.width, self.height, self.scale, ctx) | 109 | graph.load_ctx(ctx) |
106 | 110 | ||
107 | # If X caused the expose event, we need to update the entire area, not just the | 111 | # If X caused the expose event, we need to update the entire area, not just the |
108 | # changes we might have made. An expose event caused by X needs to take priority | 112 | # changes we might have made. An expose event caused by X needs to take priority |
@@ -113,11 +117,13 @@ class GraphArea(gtk.DrawingArea): | |||
113 | self.dirtied_regions = [(expose_event.area.x, expose_event.area.y, | 117 | self.dirtied_regions = [(expose_event.area.x, expose_event.area.y, |
114 | expose_event.area.width, expose_event.area.height)] | 118 | expose_event.area.width, expose_event.area.height)] |
115 | 119 | ||
116 | graph.render_surface(self.renderer.get_schedule(), self.dirtied_regions) | 120 | virt_dirtied_regions = graph.real_to_virt_list(self.dirtied_regions) |
121 | graph.render_surface(self.renderer.get_schedule(), virt_dirtied_regions) | ||
117 | 122 | ||
118 | # render dragging band rectangle, if there is one | 123 | # render dragging band rectangle, if there is one |
119 | if self.band_rect is not None: | 124 | if self.band_rect is not None: |
120 | x, y, width, height = self.band_rect | 125 | x, y, width, height = graph.virt_to_real(self.band_rect[0], self.band_rect[1], |
126 | self.band_rect[2], self.band_rect[3]) | ||
121 | thickness = GraphFormat.BAND_THICKNESS | 127 | thickness = GraphFormat.BAND_THICKNESS |
122 | color = GraphFormat.BAND_COLOR | 128 | color = GraphFormat.BAND_COLOR |
123 | 129 | ||
@@ -136,21 +142,23 @@ class GraphArea(gtk.DrawingArea): | |||
136 | 142 | ||
137 | def get_schedule(self): | 143 | def get_schedule(self): |
138 | return self.renderer.get_schedule() | 144 | return self.renderer.get_schedule() |
139 | 145 | ||
140 | def zoom_in(self): | 146 | def zoom_in(self): |
141 | scale = self.scale + GraphArea.ZOOM_INCR | 147 | scale = self.scale + GraphArea.ZOOM_INCR |
142 | if scale > GraphArea.MAX_ZOOM_IN: | 148 | if scale > GraphArea.MAX_ZOOM_IN: |
143 | scale = GraphArea.MAX_ZOOM_IN | 149 | scale = GraphArea.MAX_ZOOM_IN |
144 | self.set_scale(scale) | 150 | self.set_scale(scale) |
145 | self.config_scrollbars(self.cur_x, self.cur_y, self.scale) | 151 | self.config_scrollbars(self.cur_x, self.cur_y, self.scale) |
146 | 152 | self._pos_update() | |
153 | |||
147 | def zoom_out(self): | 154 | def zoom_out(self): |
148 | scale = self.scale - GraphArea.ZOOM_INCR | 155 | scale = self.scale - GraphArea.ZOOM_INCR |
149 | if scale < GraphArea.MIN_ZOOM_OUT: | 156 | if scale < GraphArea.MIN_ZOOM_OUT: |
150 | scale = GraphArea.MIN_ZOOM_OUT | 157 | scale = GraphArea.MIN_ZOOM_OUT |
151 | self.set_scale(scale) | 158 | self.set_scale(scale) |
152 | self.config_scrollbars(self.cur_x, self.cur_y, self.scale) | 159 | self.config_scrollbars(self.cur_x, self.cur_y, self.scale) |
153 | 160 | self._pos_update() | |
161 | |||
154 | def set_scale(self, scale): | 162 | def set_scale(self, scale): |
155 | if scale == self.scale: | 163 | if scale == self.scale: |
156 | return | 164 | return |
@@ -165,7 +173,7 @@ class GraphArea(gtk.DrawingArea): | |||
165 | value = max(value, self.horizontal.get_lower()) | 173 | value = max(value, self.horizontal.get_lower()) |
166 | value = min(value, self.horizontal.get_upper() - self.horizontal.get_page_size()) | 174 | value = min(value, self.horizontal.get_upper() - self.horizontal.get_page_size()) |
167 | self.horizontal.set_value(value) | 175 | self.horizontal.set_value(value) |
168 | 176 | ||
169 | def set_vvalue(self, value): | 177 | def set_vvalue(self, value): |
170 | if self.vertical is None: | 178 | if self.vertical is None: |
171 | return | 179 | return |
@@ -173,7 +181,7 @@ class GraphArea(gtk.DrawingArea): | |||
173 | value = max(value, self.vertical.get_lower()) | 181 | value = max(value, self.vertical.get_lower()) |
174 | value = min(value, self.vertical.get_upper() - self.vertical.get_page_size()) | 182 | value = min(value, self.vertical.get_upper() - self.vertical.get_page_size()) |
175 | self.vertical.set_value(value) | 183 | self.vertical.set_value(value) |
176 | 184 | ||
177 | def set_scroll_adjustments(self, widget, horizontal, vertical, data=None): | 185 | def set_scroll_adjustments(self, widget, horizontal, vertical, data=None): |
178 | graph = self.renderer.get_graph() | 186 | graph = self.renderer.get_graph() |
179 | width = graph.get_width() | 187 | width = graph.get_width() |
@@ -192,20 +200,27 @@ class GraphArea(gtk.DrawingArea): | |||
192 | self.cur_x = min(adjustment.value, self.renderer.get_graph().get_width()) | 200 | self.cur_x = min(adjustment.value, self.renderer.get_graph().get_width()) |
193 | self.cur_x = max(adjustment.value, 0.0) | 201 | self.cur_x = max(adjustment.value, 0.0) |
194 | 202 | ||
195 | self.renderer.get_graph().render_surface(self.renderer.get_schedule(), [(0, 0, self.width, self.height)], True) | 203 | self.get_graph().render_surface(self.renderer.get_schedule(), |
204 | [self.get_graph().real_to_virt(0, 0, self.width, self.height)], True) | ||
196 | self._dirty(0, 0, self.width, self.height) | 205 | self._dirty(0, 0, self.width, self.height) |
197 | 206 | ||
207 | self._pos_update() | ||
208 | |||
198 | def vertical_value_changed(self, adjustment): | 209 | def vertical_value_changed(self, adjustment): |
199 | self.cur_y = min(adjustment.value, self.renderer.get_graph().get_height()) | 210 | self.cur_y = min(adjustment.value, self.renderer.get_graph().get_height()) |
200 | self.cur_y = max(adjustment.value, 0.0) | 211 | self.cur_y = max(adjustment.value, 0.0) |
201 | 212 | ||
202 | self.renderer.get_graph().render_surface(self.renderer.get_schedule(), [(0, 0, self.width, self.height)], True) | 213 | self.get_graph().render_surface(self.renderer.get_schedule(), |
214 | [self.get_graph().real_to_virt(0, 0, self.width, self.height)], True) | ||
203 | self._dirty(0, 0, self.width, self.height) | 215 | self._dirty(0, 0, self.width, self.height) |
204 | 216 | ||
217 | self._pos_update() | ||
218 | |||
205 | def size_allocate(self, widget, allocation): | 219 | def size_allocate(self, widget, allocation): |
206 | self.width = allocation.width | 220 | self.width = allocation.width |
207 | self.height = allocation.height | 221 | self.height = allocation.height |
208 | self.config_scrollbars(self.cur_x, self.cur_y, self.scale) | 222 | self.config_scrollbars(self.cur_x, self.cur_y, self.scale) |
223 | self._pos_update() | ||
209 | 224 | ||
210 | def config_scrollbars(self, hvalue, vvalue, scale): | 225 | def config_scrollbars(self, hvalue, vvalue, scale): |
211 | graph = self.renderer.get_graph() | 226 | graph = self.renderer.get_graph() |
@@ -224,11 +239,11 @@ class GraphArea(gtk.DrawingArea): | |||
224 | graph.get_attrs().y_item_size * GraphArea.VERT_PAGE_SCROLL_FACTOR / scale, | 239 | graph.get_attrs().y_item_size * GraphArea.VERT_PAGE_SCROLL_FACTOR / scale, |
225 | self.height / scale) | 240 | self.height / scale) |
226 | self.set_vvalue(vvalue) | 241 | self.set_vvalue(vvalue) |
227 | 242 | ||
228 | def refresh_all(self): | 243 | def refresh_all(self): |
229 | """Refreshes the entire visible screen.""" | 244 | """Refreshes the entire visible screen.""" |
230 | self._dirty(0, 0, self.width, self.height) | 245 | self._dirty(0, 0, self.width, self.height) |
231 | 246 | ||
232 | def refresh_events(self, sender, new, old, replace): | 247 | def refresh_events(self, sender, new, old, replace): |
233 | """Even if the selected areas change on one graph, they change | 248 | """Even if the selected areas change on one graph, they change |
234 | everywhere, and different events might be in different regions on | 249 | everywhere, and different events might be in different regions on |
@@ -240,7 +255,7 @@ class GraphArea(gtk.DrawingArea): | |||
240 | refreshes the drawing of the graph that requested the change.""" | 255 | refreshes the drawing of the graph that requested the change.""" |
241 | if self is not sender: | 256 | if self is not sender: |
242 | self.renderer.get_graph().render_events(new, True) | 257 | self.renderer.get_graph().render_events(new, True) |
243 | 258 | ||
244 | self._tag_events(new) | 259 | self._tag_events(new) |
245 | 260 | ||
246 | if self is sender: | 261 | if self is sender: |
@@ -252,7 +267,7 @@ class GraphArea(gtk.DrawingArea): | |||
252 | else: | 267 | else: |
253 | self.renderer.get_schedule().remove_selected(old) | 268 | self.renderer.get_schedule().remove_selected(old) |
254 | self.renderer.get_schedule().add_selected(new) | 269 | self.renderer.get_schedule().add_selected(new) |
255 | 270 | ||
256 | self.emit('update-sel-changes', self.renderer.get_schedule().get_selected()) | 271 | self.emit('update-sel-changes', self.renderer.get_schedule().get_selected()) |
257 | 272 | ||
258 | def _find_max_layer(self, regions): | 273 | def _find_max_layer(self, regions): |
@@ -267,11 +282,9 @@ class GraphArea(gtk.DrawingArea): | |||
267 | for layer in events: | 282 | for layer in events: |
268 | for event in events[layer]: | 283 | for event in events[layer]: |
269 | if not events[layer][event][self].is_dummy(): | 284 | if not events[layer][event][self].is_dummy(): |
270 | x, y, width, height = events[layer][event][self].get_dimensions() | 285 | dim = events[layer][event][self].get_dimensions() |
271 | self._dirty_inflate((x - self.cur_x) * self.scale, | 286 | x, y, width, height = self.get_graph().virt_to_real(dim[0], dim[1], dim[2], dim[3]) |
272 | (y - self.cur_y) * self.scale, | 287 | self._dirty_inflate(x, y, width, height, |
273 | width * self.scale, | ||
274 | height * self.scale, | ||
275 | GraphFormat.BORDER_THICKNESS * self.scale) | 288 | GraphFormat.BORDER_THICKNESS * self.scale) |
276 | 289 | ||
277 | def _tag_events(self, selected): | 290 | def _tag_events(self, selected): |
@@ -303,15 +316,27 @@ class GraphArea(gtk.DrawingArea): | |||
303 | if event not in coll[event.get_layer()]: | 316 | if event not in coll[event.get_layer()]: |
304 | coll[event.get_layer()][event] = {} | 317 | coll[event.get_layer()][event] = {} |
305 | 318 | ||
319 | def leave_notify(self, widget, leave_event): | ||
320 | self.cursor_pos = None | ||
321 | self.emit('update-event-description', None) | ||
322 | |||
306 | def motion_notify(self, widget, motion_event, data=None): | 323 | def motion_notify(self, widget, motion_event, data=None): |
307 | msg = None | 324 | self.cursor_pos = (motion_event.x, motion_event.y) |
325 | self._pos_update() | ||
326 | |||
327 | def _pos_update(self): | ||
328 | self.get_graph().update_view(self.cur_x, self.cur_y, self.width, self.height, self.scale) | ||
329 | |||
330 | if self.cursor_pos is None: | ||
331 | return | ||
308 | 332 | ||
309 | graph = self.renderer.get_graph() | 333 | graph = self.renderer.get_graph() |
310 | 334 | ||
311 | graph.render_surface(self.renderer.get_schedule(), [(motion_event.x, motion_event.y, | 335 | graph.render_surface(self.renderer.get_schedule(), |
312 | 0, 0)], True) | 336 | [graph.real_to_virt(self.cursor_pos[0], self.cursor_pos[1], 0, 0)], True) |
313 | 337 | ||
314 | just_selected = graph.get_intersecting_regions(motion_event.x, motion_event.y, 0, 0) | 338 | cur_x_v, cur_y_v, w, h = graph.real_to_virt(self.cursor_pos[0], self.cursor_pos[1], 0, 0) |
339 | just_selected = graph.get_intersecting_regions(cur_x_v, cur_y_v, 0, 0) | ||
315 | was_selected = self.renderer.get_schedule().get_selected() | 340 | was_selected = self.renderer.get_schedule().get_selected() |
316 | if not just_selected: | 341 | if not just_selected: |
317 | msg = '' | 342 | msg = '' |
@@ -323,10 +348,11 @@ class GraphArea(gtk.DrawingArea): | |||
323 | the_event = event | 348 | the_event = event |
324 | break | 349 | break |
325 | 350 | ||
326 | msg = str(the_event) | 351 | msg = the_event.str_short(graph.get_attrs().unit) |
327 | 352 | ||
328 | self.emit('update-event-description', the_event, msg) | 353 | self.emit('update-event-description', the_event) |
329 | 354 | ||
355 | virt_x, virt_y, w, h = graph.real_to_virt(self.cursor_pos[0], self.cursor_pos[1], 0, 0) | ||
330 | if self.band_rect is not None: | 356 | if self.band_rect is not None: |
331 | remove_selected = {} | 357 | remove_selected = {} |
332 | add_selected = {} | 358 | add_selected = {} |
@@ -334,14 +360,12 @@ class GraphArea(gtk.DrawingArea): | |||
334 | # dragging a rectangle | 360 | # dragging a rectangle |
335 | x = self.band_rect[0] | 361 | x = self.band_rect[0] |
336 | y = self.band_rect[1] | 362 | y = self.band_rect[1] |
337 | width = motion_event.x - self.band_rect[0] | 363 | width = virt_x - self.band_rect[0] |
338 | height = motion_event.y - self.band_rect[1] | 364 | height = virt_y - self.band_rect[1] |
339 | old_x, old_y, old_width, old_height = self.band_rect | 365 | old_x, old_y, old_width, old_height = self.band_rect |
340 | 366 | ||
341 | x_p, y_p, width_p, height_p = self._positivify(x, y, width, height) | 367 | x_p, y_p, width_p, height_p = self._positivify_int(x, y, width, height) |
342 | old_x_p, old_y_p, old_width_p, old_height_p = self._positivify(old_x, old_y, old_width, old_height) | 368 | old_x_p, old_y_p, old_width_p, old_height_p = self._positivify_int(old_x, old_y, old_width, old_height) |
343 | x_p, y_p, width_p, height_p = int(x_p), int(y_p), int(width_p), int(height_p) | ||
344 | old_x_p, old_y_p, old_width_p, old_height_p = int(old_x_p), int(old_y_p), int(old_width_p), int(old_height_p) | ||
345 | 369 | ||
346 | new_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(x_p, y_p, width_p, height_p)) | 370 | new_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(x_p, y_p, width_p, height_p)) |
347 | old_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(old_x_p, old_y_p, old_width_p, old_height_p)) | 371 | old_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(old_x_p, old_y_p, old_width_p, old_height_p)) |
@@ -354,7 +378,7 @@ class GraphArea(gtk.DrawingArea): | |||
354 | dirty_list = [] | 378 | dirty_list = [] |
355 | for rect in remove_reg.get_rectangles(): | 379 | for rect in remove_reg.get_rectangles(): |
356 | dirty_list.append((rect.x, rect.y, rect.width, rect.height)) | 380 | dirty_list.append((rect.x, rect.y, rect.width, rect.height)) |
357 | graph.render_surface(self.renderer.get_schedule(), dirty_list, True) | 381 | graph.render_surface(self.renderer.get_schedule(), graph.real_to_virt_list(dirty_list), True) |
358 | for rect in dirty_list: | 382 | for rect in dirty_list: |
359 | rx, ry, rwidth, rheight = rect | 383 | rx, ry, rwidth, rheight = rect |
360 | i_regs = graph.get_intersecting_regions(rx, ry, rwidth, rheight) | 384 | i_regs = graph.get_intersecting_regions(rx, ry, rwidth, rheight) |
@@ -370,6 +394,7 @@ class GraphArea(gtk.DrawingArea): | |||
370 | for rect in add_reg.get_rectangles(): | 394 | for rect in add_reg.get_rectangles(): |
371 | dirty_list.append((rect.x, rect.y, rect.width, rect.height)) | 395 | dirty_list.append((rect.x, rect.y, rect.width, rect.height)) |
372 | graph.render_surface(self.renderer.get_schedule(), dirty_list, True) | 396 | graph.render_surface(self.renderer.get_schedule(), dirty_list, True) |
397 | |||
373 | for rect in dirty_list: | 398 | for rect in dirty_list: |
374 | rx, ry, rwidth, rheight = rect | 399 | rx, ry, rwidth, rheight = rect |
375 | i_regs = graph.get_intersecting_regions(rx, ry, rwidth, rheight) | 400 | i_regs = graph.get_intersecting_regions(rx, ry, rwidth, rheight) |
@@ -380,8 +405,10 @@ class GraphArea(gtk.DrawingArea): | |||
380 | self.band_rect = x, y, width, height | 405 | self.band_rect = x, y, width, height |
381 | self.emit('request-refresh-events', self, add_selected, remove_selected, False) | 406 | self.emit('request-refresh-events', self, add_selected, remove_selected, False) |
382 | 407 | ||
383 | self._dirty_rect_border(old_x, old_y, old_width, old_height, GraphFormat.BAND_THICKNESS) | 408 | x_r, y_r, width_r, height_r = graph.virt_to_real(x, y, width, height) |
384 | self._dirty_rect_border(x, y, width, height, GraphFormat.BAND_THICKNESS) | 409 | old_x_r, old_y_r, old_width_r, old_height_r = graph.virt_to_real(old_x, old_y, old_width, old_height) |
410 | self._dirty_rect_border(old_x_r, old_y_r, old_width_r, old_height_r, GraphFormat.BAND_THICKNESS * self.scale) | ||
411 | self._dirty_rect_border(x_r, y_r, width_r, height_r, GraphFormat.BAND_THICKNESS * self.scale) | ||
385 | 412 | ||
386 | def button_press(self, widget, button_event, data=None): | 413 | def button_press(self, widget, button_event, data=None): |
387 | graph = self.renderer.get_graph() | 414 | graph = self.renderer.get_graph() |
@@ -391,10 +418,11 @@ class GraphArea(gtk.DrawingArea): | |||
391 | if button_event.button == 1: | 418 | if button_event.button == 1: |
392 | self.left_button_start_coor = (button_event.x, button_event.y) | 419 | self.left_button_start_coor = (button_event.x, button_event.y) |
393 | graph.render_surface(self.renderer.get_schedule(), \ | 420 | graph.render_surface(self.renderer.get_schedule(), \ |
394 | [(button_event.x, button_event.y, 0, 0)], True) | 421 | [graph.real_to_virt(button_event.x, button_event.y, 0, 0)], True) |
395 | 422 | ||
396 | just_selected = graph.get_intersecting_regions(button_event.x, button_event.y, 0, 0) | 423 | b_x_r, b_y_r, w, h = graph.real_to_virt(button_event.x, button_event.y, 0, 0) |
397 | 424 | just_selected = graph.get_intersecting_regions(b_x_r, b_y_r, 0, 0) | |
425 | |||
398 | max_layer = self._find_max_layer(just_selected) | 426 | max_layer = self._find_max_layer(just_selected) |
399 | 427 | ||
400 | was_selected = self.renderer.get_schedule().get_selected() | 428 | was_selected = self.renderer.get_schedule().get_selected() |
@@ -418,7 +446,7 @@ class GraphArea(gtk.DrawingArea): | |||
418 | and event in was_selected[max_layer]): | 446 | and event in was_selected[max_layer]): |
419 | self._select_event(new_now_selected, event) | 447 | self._select_event(new_now_selected, event) |
420 | break # only pick one event when just clicking | 448 | break # only pick one event when just clicking |
421 | 449 | ||
422 | self.emit('request-refresh-events', self, new_now_selected, was_selected, True) | 450 | self.emit('request-refresh-events', self, new_now_selected, was_selected, True) |
423 | else: | 451 | else: |
424 | remove_selected = {} | 452 | remove_selected = {} |
@@ -435,7 +463,7 @@ class GraphArea(gtk.DrawingArea): | |||
435 | self.emit('request-refresh-events', self, add_selected, remove_selected, False) | 463 | self.emit('request-refresh-events', self, add_selected, remove_selected, False) |
436 | 464 | ||
437 | if self.band_rect is None: | 465 | if self.band_rect is None: |
438 | self.band_rect = (button_event.x, button_event.y, 0, 0) | 466 | self.band_rect = (b_x_r, b_y_r, 0, 0) |
439 | 467 | ||
440 | elif button_event.button == 3: | 468 | elif button_event.button == 3: |
441 | self._release_band() | 469 | self._release_band() |
@@ -456,7 +484,8 @@ class GraphArea(gtk.DrawingArea): | |||
456 | def _release_band(self): | 484 | def _release_band(self): |
457 | if self.band_rect is not None: | 485 | if self.band_rect is not None: |
458 | x, y, width, height = self.band_rect | 486 | x, y, width, height = self.band_rect |
459 | self._dirty_rect_border(x, y, width, height, GraphFormat.BAND_THICKNESS) | 487 | x_r, y_r, width_r, height_r = self.get_graph().virt_to_real(x, y, width, height) |
488 | self._dirty_rect_border(x_r, y_r, width_r, height_r, GraphFormat.BAND_THICKNESS * self.scale) | ||
460 | self.band_rect = None | 489 | self.band_rect = None |
461 | 490 | ||
462 | def _dirty(self, x, y, width, height): | 491 | def _dirty(self, x, y, width, height): |
@@ -498,6 +527,10 @@ class GraphArea(gtk.DrawingArea): | |||
498 | 527 | ||
499 | return x, y, width, height | 528 | return x, y, width, height |
500 | 529 | ||
530 | def _positivify_int(self, x, y, width, height): | ||
531 | p = self._positivify(x, y, width, height) | ||
532 | return (int(p[0]), int(p[1]), int(p[2]), int(p[3])) | ||
533 | |||
501 | class GraphWindow(gtk.ScrolledWindow): | 534 | class GraphWindow(gtk.ScrolledWindow): |
502 | def __init__(self, renderer): | 535 | def __init__(self, renderer): |
503 | super(GraphWindow, self).__init__(None, None) | 536 | super(GraphWindow, self).__init__(None, None) |
@@ -596,7 +629,7 @@ class MainWindow(gtk.Window): | |||
596 | 629 | ||
597 | def __init__(self): | 630 | def __init__(self): |
598 | super(MainWindow, self).__init__(gtk.WINDOW_TOPLEVEL) | 631 | super(MainWindow, self).__init__(gtk.WINDOW_TOPLEVEL) |
599 | 632 | ||
600 | self.add_events(gtk.gdk.BUTTON_PRESS_MASK) | 633 | self.add_events(gtk.gdk.BUTTON_PRESS_MASK) |
601 | 634 | ||
602 | self.connect('delete_event', self.delete_event) | 635 | self.connect('delete_event', self.delete_event) |
@@ -608,11 +641,11 @@ class MainWindow(gtk.Window): | |||
608 | 641 | ||
609 | agr = gtk.AccelGroup() | 642 | agr = gtk.AccelGroup() |
610 | self.add_accel_group(agr) | 643 | self.add_accel_group(agr) |
611 | 644 | ||
612 | # list of file items that are clickable if and only if at | 645 | # list of file items that are clickable if and only if at |
613 | # least one graph is viewable | 646 | # least one graph is viewable |
614 | self.sensitive_menu_items = [] | 647 | self.sensitive_menu_items = [] |
615 | 648 | ||
616 | save_item = gtk.ImageMenuItem('_Save Graph to File') | 649 | save_item = gtk.ImageMenuItem('_Save Graph to File') |
617 | img = gtk.Image() | 650 | img = gtk.Image() |
618 | img.set_from_stock(gtk.STOCK_SAVE_AS, gtk.ICON_SIZE_MENU) | 651 | img.set_from_stock(gtk.STOCK_SAVE_AS, gtk.ICON_SIZE_MENU) |
@@ -623,7 +656,7 @@ class MainWindow(gtk.Window): | |||
623 | save_item.connect('activate', self.save_item_activate) | 656 | save_item.connect('activate', self.save_item_activate) |
624 | save_item.set_sensitive(False) | 657 | save_item.set_sensitive(False) |
625 | self.sensitive_menu_items.append(save_item) | 658 | self.sensitive_menu_items.append(save_item) |
626 | 659 | ||
627 | quit_item = gtk.ImageMenuItem(gtk.STOCK_QUIT, agr) | 660 | quit_item = gtk.ImageMenuItem(gtk.STOCK_QUIT, agr) |
628 | key, mod = gtk.accelerator_parse('<Ctrl>Q') | 661 | key, mod = gtk.accelerator_parse('<Ctrl>Q') |
629 | quit_item.add_accelerator('activate', agr, key, mod, | 662 | quit_item.add_accelerator('activate', agr, key, mod, |
@@ -636,8 +669,8 @@ class MainWindow(gtk.Window): | |||
636 | file_item = gtk.MenuItem('_File', True) | 669 | file_item = gtk.MenuItem('_File', True) |
637 | file_item.set_submenu(file_menu) | 670 | file_item.set_submenu(file_menu) |
638 | 671 | ||
639 | move_item = gtk.ImageMenuItem('_Move to Time') | 672 | move_item = gtk.ImageMenuItem('_Jump to Time') |
640 | key, mod = gtk.accelerator_parse('<Ctrl>M') | 673 | key, mod = gtk.accelerator_parse('<Ctrl>J') |
641 | img = gtk.Image() | 674 | img = gtk.Image() |
642 | img.set_from_stock(gtk.STOCK_JUMP_TO, gtk.ICON_SIZE_MENU) | 675 | img.set_from_stock(gtk.STOCK_JUMP_TO, gtk.ICON_SIZE_MENU) |
643 | move_item.set_image(img) | 676 | move_item.set_image(img) |
@@ -645,7 +678,7 @@ class MainWindow(gtk.Window): | |||
645 | gtk.ACCEL_VISIBLE) | 678 | gtk.ACCEL_VISIBLE) |
646 | move_item.set_sensitive(False) | 679 | move_item.set_sensitive(False) |
647 | self.sensitive_menu_items.append(move_item) | 680 | self.sensitive_menu_items.append(move_item) |
648 | 681 | ||
649 | move_item.connect('activate', self.move_to_time_activate) | 682 | move_item.connect('activate', self.move_to_time_activate) |
650 | 683 | ||
651 | self.colors_item = gtk.ImageMenuItem('Co_lors') | 684 | self.colors_item = gtk.ImageMenuItem('Co_lors') |
@@ -656,14 +689,14 @@ class MainWindow(gtk.Window): | |||
656 | self.colors_item.add_accelerator('activate', agr, key, mod, | 689 | self.colors_item.add_accelerator('activate', agr, key, mod, |
657 | gtk.ACCEL_VISIBLE) | 690 | gtk.ACCEL_VISIBLE) |
658 | self.colors_item.set_sensitive(False) | 691 | self.colors_item.set_sensitive(False) |
659 | 692 | ||
660 | self.colors_item.connect('activate', self.colors_activate) | 693 | self.colors_item.connect('activate', self.colors_activate) |
661 | 694 | ||
662 | edit_menu.append(self.colors_item) | 695 | edit_menu.append(self.colors_item) |
663 | 696 | ||
664 | edit_item = gtk.MenuItem('_Edit', True) | 697 | edit_item = gtk.MenuItem('_Edit', True) |
665 | edit_item.set_submenu(edit_menu) | 698 | edit_item.set_submenu(edit_menu) |
666 | 699 | ||
667 | zoom_in_item = gtk.ImageMenuItem(gtk.STOCK_ZOOM_IN, agr) | 700 | zoom_in_item = gtk.ImageMenuItem(gtk.STOCK_ZOOM_IN, agr) |
668 | key, mod = gtk.accelerator_parse('<Ctrl>plus') | 701 | key, mod = gtk.accelerator_parse('<Ctrl>plus') |
669 | zoom_in_item.add_accelerator('activate', agr, key, mod, | 702 | zoom_in_item.add_accelerator('activate', agr, key, mod, |
@@ -701,20 +734,23 @@ class MainWindow(gtk.Window): | |||
701 | self.notebook.last_page = -1 | 734 | self.notebook.last_page = -1 |
702 | self.notebook.connect('switch-page', self.switch_page) | 735 | self.notebook.connect('switch-page', self.switch_page) |
703 | 736 | ||
704 | self.desc_label = gtk.Label('') | 737 | self.event_label = gtk.Label('') |
705 | self.desc_label.set_alignment(0.0, 0.0) | 738 | self.job_run_label = gtk.Label('') |
739 | self.job_rd_label = gtk.Label('') | ||
706 | 740 | ||
707 | self.vbox.pack_start(menu_bar, False, False, 0) | 741 | self.vbox.pack_start(menu_bar, False, False, 0) |
708 | self.vbox.pack_start(self.notebook, True, True, 0) | 742 | self.vbox.pack_start(self.notebook, True, True, 0) |
709 | self.vbox.pack_start(self.desc_label, False, False, 0) | 743 | self.vbox.pack_start(self.event_label, False, False, 0) |
710 | 744 | self.vbox.pack_start(self.job_run_label, False, False, 0) | |
745 | self.vbox.pack_start(self.job_rd_label, False, False, 0) | ||
711 | self.add(self.vbox) | 746 | self.add(self.vbox) |
712 | 747 | ||
713 | self.info_win = InfoWindow() | 748 | self.info_win = InfoWindow() |
749 | self.text_input_dialog = TextInputDialog('Move to Time', 'how is babby formed?', self) | ||
714 | self.color_dialog = gtk.ColorSelectionDialog('Choose Item Color') | 750 | self.color_dialog = gtk.ColorSelectionDialog('Choose Item Color') |
715 | 751 | ||
716 | self.cur_item_no = None | 752 | self.cur_item_no = None |
717 | 753 | ||
718 | self.set_size_request(MainWindow.WINDOW_WIDTH_REQ, MainWindow.WINDOW_HEIGHT_REQ) | 754 | self.set_size_request(MainWindow.WINDOW_WIDTH_REQ, MainWindow.WINDOW_HEIGHT_REQ) |
719 | 755 | ||
720 | self.set_title('Unit-Trace Visualizer') | 756 | self.set_title('Unit-Trace Visualizer') |
@@ -736,7 +772,7 @@ class MainWindow(gtk.Window): | |||
736 | self.connect_widgets(gwindow) | 772 | self.connect_widgets(gwindow) |
737 | gwindow.show() | 773 | gwindow.show() |
738 | self.notebook.append_page(gwindow, gtk.Label(title)) | 774 | self.notebook.append_page(gwindow, gtk.Label(title)) |
739 | 775 | ||
740 | if self.notebook.get_n_pages() > 0: | 776 | if self.notebook.get_n_pages() > 0: |
741 | self.notebook.get_nth_page(0).grab_focus() | 777 | self.notebook.get_nth_page(0).grab_focus() |
742 | 778 | ||
@@ -750,14 +786,14 @@ class MainWindow(gtk.Window): | |||
750 | def update_sel_changes(self, widget, selected): | 786 | def update_sel_changes(self, widget, selected): |
751 | """Modify the GUI appropriately to reflect a change in selection.""" | 787 | """Modify the GUI appropriately to reflect a change in selection.""" |
752 | self.selected = selected | 788 | self.selected = selected |
753 | 789 | ||
754 | self.cur_item_no = self.notebook.get_nth_page(self.notebook.last_page).get_graph_area() \ | 790 | self.cur_item_no = self.notebook.get_nth_page(self.notebook.last_page).get_graph_area() \ |
755 | .get_graph().all_one_item_selected(selected) | 791 | .get_graph().all_one_item_selected(selected) |
756 | if self.cur_item_no is not None: | 792 | if self.cur_item_no is not None: |
757 | self.colors_item.set_sensitive(True) | 793 | self.colors_item.set_sensitive(True) |
758 | else: | 794 | else: |
759 | self.colors_item.set_sensitive(False) | 795 | self.colors_item.set_sensitive(False) |
760 | 796 | ||
761 | def switch_page(self, widget, page, page_num): | 797 | def switch_page(self, widget, page, page_num): |
762 | if self.notebook.get_nth_page(self.notebook.last_page) is not None: | 798 | if self.notebook.get_nth_page(self.notebook.last_page) is not None: |
763 | old_value = self.notebook.get_nth_page(self.notebook.last_page).get_hadjustment().get_value() | 799 | old_value = self.notebook.get_nth_page(self.notebook.last_page).get_hadjustment().get_value() |
@@ -765,11 +801,28 @@ class MainWindow(gtk.Window): | |||
765 | new_ofs = self.notebook.get_nth_page(page_num).get_graph_area().get_graph().get_origin()[0] | 801 | new_ofs = self.notebook.get_nth_page(page_num).get_graph_area().get_graph().get_origin()[0] |
766 | new_value = old_value - old_ofs + new_ofs | 802 | new_value = old_value - old_ofs + new_ofs |
767 | self.notebook.get_nth_page(page_num).get_hadjustment().set_value(new_value) | 803 | self.notebook.get_nth_page(page_num).get_hadjustment().set_value(new_value) |
804 | unit = self.notebook.get_nth_page(page_num).get_graph_area().get_graph().get_attrs().unit | ||
805 | self.info_win.set_unit(unit) | ||
768 | 806 | ||
769 | self.notebook.last_page = page_num | 807 | self.notebook.last_page = page_num |
770 | 808 | ||
771 | def update_event_description(self, widget, event, msg): | 809 | def update_event_description(self, widget, event): |
772 | self.desc_label.set_text(msg) | 810 | if event is None: |
811 | self.event_label.set_text('') | ||
812 | self.job_run_label.set_text('') | ||
813 | self.job_rd_label.set_text('') | ||
814 | return | ||
815 | |||
816 | unit = self.notebook.get_nth_page(self.notebook.last_page).get_graph_area() \ | ||
817 | .get_graph().get_attrs().unit | ||
818 | self.event_label.set_text(event.str_short(unit)) | ||
819 | job = event.get_job() | ||
820 | if job is None: | ||
821 | self.job_run_label.set_text('') | ||
822 | self.job_rd_label.set_text('') | ||
823 | else: | ||
824 | self.job_run_label.set_text(job.str_run_short(unit)) | ||
825 | self.job_rd_label.set_text(job.str_rd_short(unit)) | ||
773 | 826 | ||
774 | def request_context_menu(self, widget, gdk_event, selected): | 827 | def request_context_menu(self, widget, gdk_event, selected): |
775 | button = 0 | 828 | button = 0 |
@@ -789,23 +842,34 @@ class MainWindow(gtk.Window): | |||
789 | if self.notebook.get_nth_page(i).get_graph_area() is sender: | 842 | if self.notebook.get_nth_page(i).get_graph_area() is sender: |
790 | self.notebook.get_nth_page(i).get_graph_area().refresh_events(sender, old, new, replace) | 843 | self.notebook.get_nth_page(i).get_graph_area().refresh_events(sender, old, new, replace) |
791 | break | 844 | break |
792 | 845 | ||
793 | def move_to_time_activate(self, widget): | 846 | def move_to_time_activate(self, widget): |
794 | dialog = TextInputDialog('Move to Time', 'What time to move to?', self) | 847 | unit = self.notebook.get_nth_page(self.notebook.last_page).get_graph_area() \ |
848 | .get_graph().get_attrs().unit | ||
849 | |||
850 | self.text_input_dialog.set_prompt('What time to move to (default ' + str(unit) + ') ?') | ||
795 | 851 | ||
796 | err = True | 852 | err = True |
797 | while err: | 853 | while err: |
798 | ret = dialog.run() | 854 | ret = self.text_input_dialog.run() |
799 | 855 | ||
800 | if ret == gtk.RESPONSE_ACCEPT: | 856 | if ret == gtk.RESPONSE_ACCEPT: |
801 | err, time = None, None | 857 | err, time = None, None |
802 | try: | 858 | try: |
803 | time = float(dialog.get_input()) | 859 | input_tokens = self.text_input_dialog.get_input().split() |
804 | start, end = self.notebook.get_nth_page(0).get_graph_area().get_schedule().get_time_bounds() | 860 | if len(input_tokens) > 2: |
861 | raise ValueError | ||
862 | |||
863 | if len(input_tokens) > 1: | ||
864 | unit = util.parse_unit(input_tokens[1]) | ||
865 | |||
866 | time = unit.native_to_nsec(float(input_tokens[0])) | ||
867 | start, end = self.notebook.get_nth_page(self.notebook.last_page).get_graph_area() \ | ||
868 | .get_schedule().get_time_bounds() | ||
805 | if time < start or time > end: | 869 | if time < start or time > end: |
806 | err = 'Time out of range!' | 870 | err = 'Time out of range!' |
807 | except ValueError: | 871 | except ValueError: |
808 | err = 'Must input a number!' | 872 | err = 'Must input a number (with an optional unit)!' |
809 | 873 | ||
810 | if not err: | 874 | if not err: |
811 | for i in xrange(0, self.notebook.get_n_pages()): | 875 | for i in xrange(0, self.notebook.get_n_pages()): |
@@ -820,12 +884,12 @@ class MainWindow(gtk.Window): | |||
820 | err) | 884 | err) |
821 | err_dialog.set_title('Input Error') | 885 | err_dialog.set_title('Input Error') |
822 | err_dialog.run() | 886 | err_dialog.run() |
823 | err_dialog.destroy() | 887 | err_dialog.hide() |
824 | 888 | ||
825 | else: | 889 | else: |
826 | break | 890 | break |
827 | 891 | ||
828 | dialog.destroy() | 892 | self.text_input_dialog.hide() |
829 | 893 | ||
830 | def zoom_in_item_activate(self, widget): | 894 | def zoom_in_item_activate(self, widget): |
831 | for i in range(0, self.notebook.get_n_pages()): | 895 | for i in range(0, self.notebook.get_n_pages()): |
@@ -838,13 +902,13 @@ class MainWindow(gtk.Window): | |||
838 | def colors_activate(self, widget): | 902 | def colors_activate(self, widget): |
839 | garea = self.notebook.get_nth_page(self.notebook.last_page).get_graph_area() | 903 | garea = self.notebook.get_nth_page(self.notebook.last_page).get_graph_area() |
840 | graph = garea.get_renderer().get_graph() | 904 | graph = garea.get_renderer().get_graph() |
841 | 905 | ||
842 | 906 | ||
843 | pattern = graph.get_bar_pattern(self.cur_item_no) | 907 | pattern = graph.get_bar_pattern(self.cur_item_no) |
844 | 908 | ||
845 | if len(pattern.get_color_list()) > 1: | 909 | if len(pattern.get_color_list()) > 1: |
846 | print 'striped patterns not yet supported' | 910 | print 'striped patterns not yet supported' |
847 | 911 | ||
848 | r, g, b = pattern.get_color_list()[0] | 912 | r, g, b = pattern.get_color_list()[0] |
849 | colorsel = self.color_dialog.get_color_selection() | 913 | colorsel = self.color_dialog.get_color_selection() |
850 | cur_color = gtk.gdk.Color(int(r * 65535), int(g * 65535), int(b * 65535)) | 914 | cur_color = gtk.gdk.Color(int(r * 65535), int(g * 65535), int(b * 65535)) |
@@ -857,34 +921,34 @@ class MainWindow(gtk.Window): | |||
857 | r, g, b = color.red / 65535.0, color.green / 65535.0, color.blue / 65535.0 | 921 | r, g, b = color.red / 65535.0, color.green / 65535.0, color.blue / 65535.0 |
858 | graph.set_bar_pattern(self.cur_item_no, Pattern([(r, g, b)])) | 922 | graph.set_bar_pattern(self.cur_item_no, Pattern([(r, g, b)])) |
859 | garea.refresh_all() | 923 | garea.refresh_all() |
860 | 924 | ||
861 | self.color_dialog.hide() | 925 | self.color_dialog.hide() |
862 | 926 | ||
863 | def save_item_activate(self, widget): | 927 | def save_item_activate(self, widget): |
864 | ACCEPTED_TYPES = ('png', 'svg') | 928 | ACCEPTED_TYPES = ('png', 'svg') |
865 | 929 | ||
866 | dialog = gtk.FileChooserDialog('Save to Graphics File', self, | 930 | dialog = gtk.FileChooserDialog('Save to Graphics File', self, |
867 | gtk.FILE_CHOOSER_ACTION_SAVE, | 931 | gtk.FILE_CHOOSER_ACTION_SAVE, |
868 | (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, | 932 | (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, |
869 | gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT)) | 933 | gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT)) |
870 | 934 | ||
871 | png_filter = gtk.FileFilter() | 935 | png_filter = gtk.FileFilter() |
872 | png_filter.add_mime_type('image/png') | 936 | png_filter.add_mime_type('image/png') |
873 | png_filter.set_name('PNG image (*.png)') | 937 | png_filter.set_name('PNG image (*.png)') |
874 | dialog.add_filter(png_filter) | 938 | dialog.add_filter(png_filter) |
875 | 939 | ||
876 | svg_filter = gtk.FileFilter() | 940 | svg_filter = gtk.FileFilter() |
877 | svg_filter.add_mime_type('image/svg+xml') | 941 | svg_filter.add_mime_type('image/svg+xml') |
878 | svg_filter.set_name('SVG vector image (*.svg)') | 942 | svg_filter.set_name('SVG vector image (*.svg)') |
879 | dialog.add_filter(svg_filter) | 943 | dialog.add_filter(svg_filter) |
880 | 944 | ||
881 | filename = None | 945 | filename = None |
882 | ftype = None | 946 | ftype = None |
883 | while True: | 947 | while True: |
884 | if dialog.run() == gtk.RESPONSE_ACCEPT: | 948 | if dialog.run() == gtk.RESPONSE_ACCEPT: |
885 | filename = dialog.get_filename() | 949 | filename = dialog.get_filename() |
886 | # parse filename for extension | 950 | # parse filename for extension |
887 | 951 | ||
888 | parts = filename.split('.') | 952 | parts = filename.split('.') |
889 | ftype = None | 953 | ftype = None |
890 | if len(parts) == 1: | 954 | if len(parts) == 1: |
@@ -910,7 +974,7 @@ class MainWindow(gtk.Window): | |||
910 | filename = None | 974 | filename = None |
911 | ftype = None | 975 | ftype = None |
912 | break | 976 | break |
913 | 977 | ||
914 | if filename is not None: | 978 | if filename is not None: |
915 | cursor = gtk.gdk.Cursor(gtk.gdk.WATCH) | 979 | cursor = gtk.gdk.Cursor(gtk.gdk.WATCH) |
916 | dialog.get_window().set_cursor(cursor) | 980 | dialog.get_window().set_cursor(cursor) |
@@ -919,9 +983,9 @@ class MainWindow(gtk.Window): | |||
919 | dialog.get_window().set_cursor(None) | 983 | dialog.get_window().set_cursor(None) |
920 | dialog.destroy() | 984 | dialog.destroy() |
921 | gobject.idle_add(idle_callback, dialog) # needed to get the cursor to display | 985 | gobject.idle_add(idle_callback, dialog) # needed to get the cursor to display |
922 | else: | 986 | else: |
923 | dialog.destroy() | 987 | dialog.destroy() |
924 | 988 | ||
925 | def quit_item_activate(self, widget): | 989 | def quit_item_activate(self, widget): |
926 | self.destroy() | 990 | self.destroy() |
927 | 991 | ||
diff --git a/unit_trace/viz/windows.py b/unit_trace/viz/windows.py index 4e5af5c..cd75398 100644 --- a/unit_trace/viz/windows.py +++ b/unit_trace/viz/windows.py | |||
@@ -8,20 +8,20 @@ class TextInputDialog(gtk.Dialog): | |||
8 | WINDOW_WIDTH_REQ = 250 | 8 | WINDOW_WIDTH_REQ = 250 |
9 | WINDOW_HEIGHT_REQ = 100 | 9 | WINDOW_HEIGHT_REQ = 100 |
10 | 10 | ||
11 | def __init__(self, title, label, parent_window=None): | 11 | def __init__(self, title, prompt, parent_window=None): |
12 | super(TextInputDialog, self).__init__(title, parent_window, | 12 | super(TextInputDialog, self).__init__(title, parent_window, |
13 | gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, | 13 | gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, |
14 | (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, | 14 | (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, |
15 | gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) | 15 | gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) |
16 | label_widget = gtk.Label(label) | 16 | self.label_widget = gtk.Label(prompt) |
17 | label_widget.set_alignment(0.0, 0.0) | 17 | self.label_widget.set_alignment(0.0, 0.0) |
18 | self.text_input = gtk.Entry() | 18 | self.text_input = gtk.Entry() |
19 | label_widget.show() | 19 | self.label_widget.show() |
20 | self.text_input.show() | 20 | self.text_input.show() |
21 | 21 | ||
22 | vbox = self.get_content_area() | 22 | vbox = self.get_content_area() |
23 | 23 | ||
24 | vbox.pack_start(label_widget, False, False, 0) | 24 | vbox.pack_start(self.label_widget, False, False, 0) |
25 | vbox.pack_start(self.text_input, False, False, 0) | 25 | vbox.pack_start(self.text_input, False, False, 0) |
26 | vbox.show() | 26 | vbox.show() |
27 | 27 | ||
@@ -29,6 +29,9 @@ class TextInputDialog(gtk.Dialog): | |||
29 | 29 | ||
30 | self.set_size_request(TextInputDialog.WINDOW_WIDTH_REQ, TextInputDialog.WINDOW_HEIGHT_REQ) | 30 | self.set_size_request(TextInputDialog.WINDOW_WIDTH_REQ, TextInputDialog.WINDOW_HEIGHT_REQ) |
31 | 31 | ||
32 | def set_prompt(self, text): | ||
33 | self.label_widget.set_text(text) | ||
34 | |||
32 | def get_input(self): | 35 | def get_input(self): |
33 | return self.text_input.get_text() | 36 | return self.text_input.get_text() |
34 | 37 | ||
@@ -57,9 +60,18 @@ class InfoWindow(gtk.Window): | |||
57 | 60 | ||
58 | self.add(self.vbox) | 61 | self.add(self.vbox) |
59 | 62 | ||
63 | self.unit = None | ||
64 | |||
60 | self.set_default_size(InfoWindow.WINDOW_WIDTH_REQ, InfoWindow.WINDOW_HEIGHT_REQ) | 65 | self.set_default_size(InfoWindow.WINDOW_WIDTH_REQ, InfoWindow.WINDOW_HEIGHT_REQ) |
61 | self.set_title('Event Details') | 66 | self.set_title('Event Details') |
62 | 67 | ||
63 | def set_event(self, event): | 68 | def set_event(self, event): |
64 | self.text_view.get_buffer().set_text(event.str_long()) | 69 | self.text_view.get_buffer().set_text(event.str_long(self.unit)) |
65 | self.frm.set_label('Details for ' + event.get_name()) | 70 | self.frm.set_label('Details for ' + event.get_name()) |
71 | |||
72 | def set_unit(self, unit): | ||
73 | self.unit = unit | ||
74 | |||
75 | def get_unit(self): | ||
76 | return self.unit | ||
77 | |||