diff options
author | Gary Bressler <garybressler@nc.rr.com> | 2010-03-19 12:55:12 -0400 |
---|---|---|
committer | Gary Bressler <garybressler@nc.rr.com> | 2010-03-19 12:55:12 -0400 |
commit | ee7ae82ae6df1378203957b7fa77066153e13c36 (patch) | |
tree | ceb3b420a5ce80b07e9a23bbef0ec9639dbb384f | |
parent | e6dbe1605d8ae52ee65a08e929ed1810bd07d45c (diff) |
Fixed some graphical glitches, along with streamlining the access to the visualizer.
-rwxr-xr-x | unit-trace | 6 | ||||
-rwxr-xr-x | unit_trace/visualizer.py | 35 | ||||
-rw-r--r-- | unit_trace/viz/__init__.py | 5 | ||||
-rw-r--r-- | unit_trace/viz/draw.py | 223 | ||||
-rw-r--r-- | unit_trace/viz/format.py | 3 | ||||
-rw-r--r-- | unit_trace/viz/schedule.py | 89 | ||||
-rw-r--r-- | unit_trace/viz/viewer.py | 182 |
7 files changed, 338 insertions, 205 deletions
@@ -17,7 +17,7 @@ from unit_trace import sanitizer | |||
17 | from unit_trace import gedf_test | 17 | from unit_trace import gedf_test |
18 | from unit_trace import stats | 18 | from unit_trace import stats |
19 | from unit_trace import stdout_printer | 19 | from unit_trace import stdout_printer |
20 | from unit_trace import visualizer | 20 | from unit_trace import viz |
21 | from unit_trace import progress | 21 | from unit_trace import progress |
22 | from unit_trace import skipper | 22 | from unit_trace import skipper |
23 | from unit_trace import maxer | 23 | from unit_trace import maxer |
@@ -107,8 +107,8 @@ if options.stdout is True and options.visualize is True: | |||
107 | import itertools | 107 | import itertools |
108 | stream1, stream2 = itertools.tee(stream,2) | 108 | stream1, stream2 = itertools.tee(stream,2) |
109 | stdout_printer.stdout_printer(stream1) | 109 | stdout_printer.stdout_printer(stream1) |
110 | visualizer.visualizer(stream2) | 110 | viz.visualizer.visualizer(stream2) |
111 | elif options.stdout is True: | 111 | elif options.stdout is True: |
112 | stdout_printer.stdout_printer(stream) | 112 | stdout_printer.stdout_printer(stream) |
113 | elif options.visualize is True: | 113 | elif options.visualize is True: |
114 | visualizer.visualizer(stream) | 114 | viz.visualizer.visualizer(stream) |
diff --git a/unit_trace/visualizer.py b/unit_trace/visualizer.py deleted file mode 100755 index af2dc6a..0000000 --- a/unit_trace/visualizer.py +++ /dev/null | |||
@@ -1,35 +0,0 @@ | |||
1 | #!/usr/bin/python | ||
2 | |||
3 | """Runs the visualizer.""" | ||
4 | |||
5 | import viz | ||
6 | import gtk | ||
7 | import trace_reader | ||
8 | |||
9 | TIME_PER_MAJ = 10000000 | ||
10 | |||
11 | def request_renderer_change(widget, file_list, params): | ||
12 | try: | ||
13 | stream = trace_reader.trace_reader(file_list) | ||
14 | #stream = reader.sanitizer.sanitizer(stream) | ||
15 | #stream = reader.gedf_test.gedf_test(stream) | ||
16 | sched = viz.convert.convert_trace_to_schedule(stream) | ||
17 | except trace_reader.InvalidRecordError, e: | ||
18 | dialog = gtk.MessageDialog(widget, gtk.DIALOG_DESTROY_WITH_PARENT, | ||
19 | gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, str(e)) | ||
20 | dialog.run() | ||
21 | dialog.destroy() | ||
22 | return | ||
23 | |||
24 | sched.scan(TIME_PER_MAJ) | ||
25 | |||
26 | task_renderer = viz.renderer.Renderer(sched) | ||
27 | task_renderer.prepare_task_graph(attrs=viz.format.GraphFormat(time_per_maj=TIME_PER_MAJ)) | ||
28 | cpu_renderer = viz.renderer.Renderer(sched) | ||
29 | cpu_renderer.prepare_cpu_graph(attrs=viz.format.GraphFormat(time_per_maj=TIME_PER_MAJ)) | ||
30 | widget.set_renderers({'Tasks' : task_renderer, 'CPUs' : cpu_renderer}) | ||
31 | |||
32 | if __name__ == '__main__': | ||
33 | window = viz.viewer.MainWindow(request_renderer_change) | ||
34 | gtk.main() | ||
35 | |||
diff --git a/unit_trace/viz/__init__.py b/unit_trace/viz/__init__.py index ab141e1..11505b9 100644 --- a/unit_trace/viz/__init__.py +++ b/unit_trace/viz/__init__.py | |||
@@ -1,3 +1,4 @@ | |||
1 | import visualizer | ||
1 | import viewer | 2 | import viewer |
2 | import renderer | 3 | import renderer |
3 | import format | 4 | import format |
@@ -11,5 +12,5 @@ gobject.signal_new('update-event-description', viewer.GraphArea, gobject.SIGNAL_ | |||
11 | None, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)) | 12 | None, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)) |
12 | gobject.signal_new('request-context-menu', viewer.GraphArea, gobject.SIGNAL_RUN_FIRST, | 13 | gobject.signal_new('request-context-menu', viewer.GraphArea, gobject.SIGNAL_RUN_FIRST, |
13 | None, (gtk.gdk.Event, gobject.TYPE_PYOBJECT)) | 14 | None, (gtk.gdk.Event, gobject.TYPE_PYOBJECT)) |
14 | gobject.signal_new('request-renderer-change', viewer.MainWindow, gobject.SIGNAL_RUN_FIRST, | 15 | #gobject.signal_new('request-renderer-change', viewer.MainWindow, gobject.SIGNAL_RUN_FIRST, |
15 | None, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)) | 16 | # None, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)) |
diff --git a/unit_trace/viz/draw.py b/unit_trace/viz/draw.py index c30ffe7..e28ed24 100644 --- a/unit_trace/viz/draw.py +++ b/unit_trace/viz/draw.py | |||
@@ -3,6 +3,7 @@ | |||
3 | import math | 3 | import math |
4 | import cairo | 4 | import cairo |
5 | import os | 5 | import os |
6 | import copy | ||
6 | 7 | ||
7 | import util | 8 | import util |
8 | import schedule | 9 | import schedule |
@@ -15,7 +16,7 @@ def snap(pos): | |||
15 | line of width 1 on integer coordinates, it will come out blurry unless we shift it, | 16 | line of width 1 on integer coordinates, it will come out blurry unless we shift it, |
16 | since the line will get distributed over two pixels. We actually apply this to all | 17 | since the line will get distributed over two pixels. We actually apply this to all |
17 | coordinates to make sure everything is aligned.""" | 18 | coordinates to make sure everything is aligned.""" |
18 | return pos - 0.5 | 19 | return pos |
19 | 20 | ||
20 | class Surface(object): | 21 | class Surface(object): |
21 | def __init__(self, fname='temp', ctx=None): | 22 | def __init__(self, fname='temp', ctx=None): |
@@ -166,39 +167,40 @@ class Canvas(object): | |||
166 | def scaled(self, *coors): | 167 | def scaled(self, *coors): |
167 | return [coor * self.scale for coor in coors] | 168 | return [coor * self.scale for coor in coors] |
168 | 169 | ||
169 | def draw_rect(self, x, y, width, height, color, thickness): | 170 | def draw_rect(self, x, y, width, height, color, thickness, snap=True): |
170 | """Draws a rectangle somewhere (border only).""" | 171 | """Draws a rectangle somewhere (border only).""" |
171 | raise NotImplementedError | 172 | raise NotImplementedError |
172 | 173 | ||
173 | def fill_rect(self, x, y, width, height, color): | 174 | def fill_rect(self, x, y, width, height, color, snap=True): |
174 | """Draws a filled rectangle somewhere. ``color'' is a 3-tuple.""" | 175 | """Draws a filled rectangle somewhere. ``color'' is a 3-tuple.""" |
175 | raise NotImplementedError | 176 | raise NotImplementedError |
176 | 177 | ||
177 | def fill_rect_fade(self, x, y, width, height, lcolor, rcolor): | 178 | def fill_rect_fade(self, x, y, width, height, lcolor, rcolor, snap=True): |
178 | """Draws a rectangle somewhere, filled in with the fade.""" | 179 | """Draws a rectangle somewhere, filled in with the fade.""" |
179 | raise NotImplementedError | 180 | raise NotImplementedError |
180 | 181 | ||
181 | def draw_line(self, p0, p1, color, thickness): | 182 | def draw_line(self, p0, p1, color, thickness, snap=True): |
182 | """Draws a line from p0 to p1 with a certain color and thickness.""" | 183 | """Draws a line from p0 to p1 with a certain color and thickness.""" |
183 | raise NotImplementedError | 184 | raise NotImplementedError |
184 | 185 | ||
185 | def draw_polyline(self, coor_list, color, thickness): | 186 | def draw_polyline(self, coor_list, color, thickness, snap=True): |
186 | """Draws a polyline, where coor_list = [(x_0, y_0), (x_1, y_1), ... (x_m, y_m)] | 187 | """Draws a polyline, where coor_list = [(x_0, y_0), (x_1, y_1), ... (x_m, y_m)] |
187 | specifies a polyline from (x_0, y_0) to (x_1, y_1), etc.""" | 188 | specifies a polyline from (x_0, y_0) to (x_1, y_1), etc.""" |
188 | raise NotImplementedError | 189 | raise NotImplementedError |
189 | 190 | ||
190 | def fill_polyline(self, coor_list, color, thickness): | 191 | def fill_polyline(self, coor_list, color, thickness, snap=True): |
191 | """Draws a polyline (probably a polygon) and fills it.""" | 192 | """Draws a polyline (probably a polygon) and fills it.""" |
192 | raise NotImplementedError | 193 | raise NotImplementedError |
193 | 194 | ||
194 | def draw_label(self, text, x, y, fopts=GraphFormat.DEF_FOPTS_LABEL, halign=AlignMode.LEFT, valign=AlignMode.BOTTOM): | 195 | def draw_label(self, text, x, y, fopts=GraphFormat.DEF_FOPTS_LABEL, |
196 | halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, snap=True): | ||
195 | """Draws text at a position with a certain alignment.""" | 197 | """Draws text at a position with a certain alignment.""" |
196 | raise NotImplementedError | 198 | raise NotImplementedError |
197 | 199 | ||
198 | def draw_label_with_sscripts(self, text, supscript, subscript, x, y, \ | 200 | def draw_label_with_sscripts(self, text, supscript, subscript, x, y, \ |
199 | textfopts=GraphFormat.DEF_FOPTS_LABEL, | 201 | textfopts=GraphFormat.DEF_FOPTS_LABEL, |
200 | sscriptfopts=GraphFormat.DEF_FOPTS_LABEL_SSCRIPT, \ | 202 | sscriptfopts=GraphFormat.DEF_FOPTS_LABEL_SSCRIPT, \ |
201 | halign=AlignMode.LEFT, valign=AlignMode.BOTTOM): | 203 | halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, snap=True): |
202 | """Draws text at a position with a certain alignment, along with optionally a superscript and | 204 | """Draws text at a position with a certain alignment, along with optionally a superscript and |
203 | subscript (which are None if either is not used.)""" | 205 | subscript (which are None if either is not used.)""" |
204 | raise NotImplementedError | 206 | raise NotImplementedError |
@@ -288,7 +290,7 @@ class Canvas(object): | |||
288 | ``start_tick'' and ``end_tick'' give the major ticks to start and end at for drawing vertical lines. | 290 | ``start_tick'' and ``end_tick'' give the major ticks to start and end at for drawing vertical lines. |
289 | ``start_item'' and ``end_item'' give the item boundaries to start and end drawing horizontal lines.""" | 291 | ``start_item'' and ``end_item'' give the item boundaries to start and end drawing horizontal lines.""" |
290 | if start_tick > end_tick or start_item > end_item: | 292 | if start_tick > end_tick or start_item > end_item: |
291 | raise ValueError('start must be less than end') | 293 | return |
292 | 294 | ||
293 | line_width = (end_tick - start_tick) * maj_sep | 295 | line_width = (end_tick - start_tick) * maj_sep |
294 | line_height = (end_item - start_item) * item_size | 296 | line_height = (end_item - start_item) * item_size |
@@ -490,7 +492,10 @@ class Canvas(object): | |||
490 | 492 | ||
491 | def add_sel_region(self, region): | 493 | def add_sel_region(self, region): |
492 | self.selectable_regions[region.get_event()] = region | 494 | self.selectable_regions[region.get_event()] = region |
493 | 495 | ||
496 | def get_sel_region(self, event): | ||
497 | return self.selectable_regions[event] | ||
498 | |||
494 | def get_selected_regions(self, real_x, real_y, width, height): | 499 | def get_selected_regions(self, real_x, real_y, width, height): |
495 | x = real_x + self.surface.virt_x | 500 | x = real_x + self.surface.virt_x |
496 | y = real_y + self.surface.virt_y | 501 | y = real_y + self.surface.virt_y |
@@ -506,7 +511,7 @@ class Canvas(object): | |||
506 | def whiteout(self, real_x, real_y, width, height): | 511 | def whiteout(self, real_x, real_y, width, height): |
507 | """Overwrites the surface completely white, but technically doesn't delete anything""" | 512 | """Overwrites the surface completely white, but technically doesn't delete anything""" |
508 | self.fill_rect(self.surface.virt_x + real_x, self.surface.virt_y + real_y, width, | 513 | self.fill_rect(self.surface.virt_x + real_x, self.surface.virt_y + real_y, width, |
509 | height, (1.0, 1.0, 1.0)) | 514 | height, (1.0, 1.0, 1.0), False) |
510 | 515 | ||
511 | def get_item_color(self, n): | 516 | def get_item_color(self, n): |
512 | """Gets the nth color in the item color list, which are the colors used to draw the items | 517 | """Gets the nth color in the item color list, which are the colors used to draw the items |
@@ -552,65 +557,84 @@ class CairoCanvas(Canvas): | |||
552 | """Gets the Surface that we are drawing on in its current state.""" | 557 | """Gets the Surface that we are drawing on in its current state.""" |
553 | return self.surface | 558 | return self.surface |
554 | 559 | ||
555 | def _rect_common(self, x, y, width, height, color, thickness): | 560 | def _rect_common(self, x, y, width, height, color, thickness, do_snap=True): |
556 | x, y, width, height = self.scaled(x, y, width, height) | 561 | x, y, width, height = self.scaled(x, y, width, height) |
557 | x, y = self.surface.get_real_coor(x, y) | 562 | x, y = self.surface.get_real_coor(x, y) |
558 | self.surface.ctx.rectangle(snap(x), snap(y), width, height) | 563 | if do_snap: |
564 | self.surface.ctx.rectangle(snap(x), snap(y), width, height) | ||
565 | else: | ||
566 | self.surface.ctx.rectangle(x, y, width, height) | ||
567 | |||
559 | self.surface.ctx.set_line_width(thickness * self.scale) | 568 | self.surface.ctx.set_line_width(thickness * self.scale) |
560 | self.surface.ctx.set_source_rgb(color[0], color[1], color[2]) | 569 | self.surface.ctx.set_source_rgb(color[0], color[1], color[2]) |
561 | 570 | ||
562 | def draw_rect(self, x, y, width, height, color, thickness): | 571 | def draw_rect(self, x, y, width, height, color, thickness, do_snap=True): |
563 | self._rect_common(x, y, width, height, color, thickness) | 572 | self._rect_common(x, y, width, height, color, thickness, do_snap) |
564 | self.surface.ctx.stroke() | 573 | self.surface.ctx.stroke() |
565 | 574 | ||
566 | def fill_rect(self, x, y, width, height, color): | 575 | def fill_rect(self, x, y, width, height, color, do_snap=True): |
567 | self._rect_common(x, y, width, height, color, 1) | 576 | self._rect_common(x, y, width, height, color, 1, do_snap) |
568 | self.surface.ctx.fill() | 577 | self.surface.ctx.fill() |
569 | 578 | ||
570 | def fill_rect_fade(self, x, y, width, height, lcolor, rcolor): | 579 | def fill_rect_fade(self, x, y, width, height, lcolor, rcolor, do_snap=True): |
571 | """Draws a rectangle somewhere, filled in with the fade.""" | 580 | """Draws a rectangle somewhere, filled in with the fade.""" |
572 | x, y, width, height = self.scaled(x, y, width, height) | 581 | x, y, width, height = self.scaled(x, y, width, height) |
573 | x, y = self.surface.get_real_coor(x, y) | 582 | x, y = self.surface.get_real_coor(x, y) |
574 | 583 | ||
575 | linear = cairo.LinearGradient(snap(x), snap(y), \ | 584 | if do_snap: |
585 | linear = cairo.LinearGradient(snap(x), snap(y), \ | ||
576 | snap(x + width), snap(y + height)) | 586 | snap(x + width), snap(y + height)) |
587 | else: | ||
588 | linear = cairo.LinearGradient(x, y, \ | ||
589 | x + width, y + height) | ||
577 | linear.add_color_stop_rgb(0.0, lcolor[0], lcolor[1], lcolor[2]) | 590 | linear.add_color_stop_rgb(0.0, lcolor[0], lcolor[1], lcolor[2]) |
578 | linear.add_color_stop_rgb(1.0, rcolor[0], rcolor[1], rcolor[2]) | 591 | linear.add_color_stop_rgb(1.0, rcolor[0], rcolor[1], rcolor[2]) |
579 | self.surface.ctx.set_source(linear) | 592 | self.surface.ctx.set_source(linear) |
580 | self.surface.ctx.rectangle(snap(x), snap(y), width, height) | 593 | if do_snap: |
594 | self.surface.ctx.rectangle(snap(x), snap(y), width, height) | ||
595 | else: | ||
596 | self.surface.ctx.rectangle(snap(x), snap(y), width, height) | ||
581 | self.surface.ctx.fill() | 597 | self.surface.ctx.fill() |
582 | 598 | ||
583 | def draw_line(self, p0, p1, color, thickness): | 599 | def draw_line(self, p0, p1, color, thickness, do_snap=True): |
584 | """Draws a line from p0 to p1 with a certain color and thickness.""" | 600 | """Draws a line from p0 to p1 with a certain color and thickness.""" |
585 | p0 = self.scaled(p0[0], p0[1]) | 601 | p0 = self.scaled(p0[0], p0[1]) |
586 | p0 = self.surface.get_real_coor(p0[0], p0[1]) | 602 | p0 = self.surface.get_real_coor(p0[0], p0[1]) |
587 | p1 = self.scaled(p1[0], p1[1]) | 603 | p1 = self.scaled(p1[0], p1[1]) |
588 | p1 = self.surface.get_real_coor(p1[0], p1[1]) | 604 | p1 = self.surface.get_real_coor(p1[0], p1[1]) |
605 | if do_snap: | ||
606 | p0 = (snap(p0[0]), snap(p0[1])) | ||
607 | p1 = (snap(p1[0]), snap(p1[1])) | ||
608 | |||
589 | self.surface.ctx.move_to(p0[0], p0[1]) | 609 | self.surface.ctx.move_to(p0[0], p0[1]) |
590 | self.surface.ctx.line_to(p1[0], p1[1]) | 610 | self.surface.ctx.line_to(p1[0], p1[1]) |
591 | self.surface.ctx.set_source_rgb(color[0], color[1], color[2]) | 611 | self.surface.ctx.set_source_rgb(color[0], color[1], color[2]) |
592 | self.surface.ctx.set_line_width(thickness * self.scale) | 612 | self.surface.ctx.set_line_width(thickness * self.scale) |
593 | self.surface.ctx.stroke() | 613 | self.surface.ctx.stroke() |
594 | 614 | ||
595 | def _polyline_common(self, coor_list, color, thickness): | 615 | def _polyline_common(self, coor_list, color, thickness, do_snap=True): |
596 | real_coor_list = [self.surface.get_real_coor(coor[0], coor[1]) for coor in coor_list] | 616 | real_coor_list = [self.surface.get_real_coor(coor[0], coor[1]) for coor in coor_list] |
597 | self.surface.ctx.move_to(real_coor_list[0][0], real_coor_list[0][1]) | 617 | self.surface.ctx.move_to(real_coor_list[0][0], real_coor_list[0][1]) |
618 | if do_snap: | ||
619 | for i in range(0, len(real_coor_list)): | ||
620 | real_coor_list[i] = (snap(real_coor_list[i][0]), snap(real_coor_list[i][1])) | ||
621 | |||
598 | for coor in real_coor_list[1:]: | 622 | for coor in real_coor_list[1:]: |
599 | self.surface.ctx.line_to(coor[0], coor[1]) | 623 | self.surface.ctx.line_to(coor[0], coor[1]) |
600 | 624 | ||
601 | self.surface.ctx.set_line_width(thickness) | 625 | self.surface.ctx.set_line_width(thickness) |
602 | self.surface.ctx.set_source_rgb(color[0], color[1], color[2]) | 626 | self.surface.ctx.set_source_rgb(color[0], color[1], color[2]) |
603 | 627 | ||
604 | def draw_polyline(self, coor_list, color, thickness): | 628 | def draw_polyline(self, coor_list, color, thickness, do_snap=True): |
605 | self._polyline_common(coor_list, color, thickness) | 629 | self._polyline_common(coor_list, color, thickness, do_snap) |
606 | self.surface.ctx.stroke() | 630 | self.surface.ctx.stroke() |
607 | 631 | ||
608 | def fill_polyline(self, coor_list, color, thickness): | 632 | def fill_polyline(self, coor_list, color, thickness, do_snap=True): |
609 | self._polyline_common(coor_list, color, thickness) | 633 | self._polyline_common(coor_list, color, thickness, do_snap) |
610 | self.surface.ctx.fill() | 634 | self.surface.ctx.fill() |
611 | 635 | ||
612 | def _draw_label_common(self, text, x, y, fopts, x_bearing_factor, \ | 636 | def _draw_label_common(self, text, x, y, fopts, x_bearing_factor, \ |
613 | f_descent_factor, width_factor, f_height_factor): | 637 | f_descent_factor, width_factor, f_height_factor, do_snap=True): |
614 | """Helper function for drawing a label with some alignment. Instead of taking in an alignment, | 638 | """Helper function for drawing a label with some alignment. Instead of taking in an alignment, |
615 | it takes in the scale factor for the font extent parameters, which give the raw data of how much to adjust | 639 | it takes in the scale factor for the font extent parameters, which give the raw data of how much to adjust |
616 | the x and y parameters. Only should be used internally.""" | 640 | the x and y parameters. Only should be used internally.""" |
@@ -633,11 +657,14 @@ class CairoCanvas(Canvas): | |||
633 | 657 | ||
634 | self.surface.ctx.set_source_rgb(fopts.color[0], fopts.color[1], fopts.color[2]) | 658 | self.surface.ctx.set_source_rgb(fopts.color[0], fopts.color[1], fopts.color[2]) |
635 | 659 | ||
636 | self.surface.ctx.move_to(snap(actual_x), snap(actual_y)) | 660 | if do_snap: |
637 | 661 | self.surface.ctx.move_to(snap(actual_x), snap(actual_y)) | |
662 | else: | ||
663 | self.surface.ctx.move_to(actual_x, actual_y) | ||
664 | |||
638 | self.surface.ctx.show_text(text) | 665 | self.surface.ctx.show_text(text) |
639 | 666 | ||
640 | def draw_label(self, text, x, y, fopts=GraphFormat.DEF_FOPTS_LABEL, halign=AlignMode.LEFT, valign=AlignMode.BOTTOM): | 667 | def draw_label(self, text, x, y, fopts=GraphFormat.DEF_FOPTS_LABEL, halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, do_snap=True): |
641 | """Draws a label with the given parameters, with the given horizontal and vertical justification. One can override | 668 | """Draws a label with the given parameters, with the given horizontal and vertical justification. One can override |
642 | the color from ``fopts'' by passing something in to ``pattern'', which overrides the color with an arbitrary | 669 | the color from ``fopts'' by passing something in to ``pattern'', which overrides the color with an arbitrary |
643 | pattern.""" | 670 | pattern.""" |
@@ -653,11 +680,11 @@ class CairoCanvas(Canvas): | |||
653 | f_descent_factor, f_height_factor = valign_factors[valign] | 680 | f_descent_factor, f_height_factor = valign_factors[valign] |
654 | 681 | ||
655 | self._draw_label_common(text, x, y, fopts, x_bearing_factor, \ | 682 | self._draw_label_common(text, x, y, fopts, x_bearing_factor, \ |
656 | f_descent_factor, width_factor, f_height_factor) | 683 | f_descent_factor, width_factor, f_height_factor, do_snap) |
657 | 684 | ||
658 | def draw_label_with_sscripts(self, text, supscript, subscript, x, y, \ | 685 | def draw_label_with_sscripts(self, text, supscript, subscript, x, y, \ |
659 | textfopts=GraphFormat.DEF_FOPTS_LABEL, sscriptfopts=GraphFormat.DEF_FOPTS_LABEL_SSCRIPT, \ | 686 | textfopts=GraphFormat.DEF_FOPTS_LABEL, sscriptfopts=GraphFormat.DEF_FOPTS_LABEL_SSCRIPT, \ |
660 | halign=AlignMode.LEFT, valign=AlignMode.BOTTOM): | 687 | halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, do_snap=True): |
661 | """Draws a label, but also optionally allows a superscript and subscript to be rendered.""" | 688 | """Draws a label, but also optionally allows a superscript and subscript to be rendered.""" |
662 | self.draw_label(text, x, y, textfopts, halign, valign) | 689 | self.draw_label(text, x, y, textfopts, halign, valign) |
663 | 690 | ||
@@ -672,14 +699,14 @@ class CairoCanvas(Canvas): | |||
672 | xtmp = x + x_advance | 699 | xtmp = x + x_advance |
673 | ytmp = y | 700 | ytmp = y |
674 | ytmp = y - f_height / 4.0 | 701 | ytmp = y - f_height / 4.0 |
675 | self.draw_label(supscript, xtmp, ytmp, sscriptfopts, halign, valign) | 702 | self.draw_label(supscript, xtmp, ytmp, sscriptfopts, halign, valign, do_snap) |
676 | if subscript is not None: | 703 | if subscript is not None: |
677 | f_height = fe[2] | 704 | f_height = fe[2] |
678 | x_advance = te[4] | 705 | x_advance = te[4] |
679 | xtmp = x + x_advance | 706 | xtmp = x + x_advance |
680 | ytmp = y | 707 | ytmp = y |
681 | ytmp = y + f_height / 4.0 | 708 | ytmp = y + f_height / 4.0 |
682 | self.draw_label(subscript, xtmp, ytmp, sscriptfopts, halign, valign) | 709 | self.draw_label(subscript, xtmp, ytmp, sscriptfopts, halign, valign, do_snap) |
683 | 710 | ||
684 | # represents a selectable region of the graph | 711 | # represents a selectable region of the graph |
685 | class SelectableRegion(object): | 712 | class SelectableRegion(object): |
@@ -769,11 +796,27 @@ class Graph(object): | |||
769 | def get_attrs(self): | 796 | def get_attrs(self): |
770 | return self.attrs | 797 | return self.attrs |
771 | 798 | ||
799 | def add_sel_region(self, region): | ||
800 | self.canvas.add_sel_region(region) | ||
801 | |||
802 | def get_sel_region(self, event): | ||
803 | return self.canvas.get_sel_region(event) | ||
804 | |||
772 | def update_view(self, x, y, width, height, ctx): | 805 | def update_view(self, x, y, width, height, ctx): |
773 | """Proxy into the surface's pan.""" | 806 | """Proxy into the surface's pan.""" |
774 | self.canvas.surface.pan(x, y, width, height) | 807 | self.canvas.surface.pan(x, y, width, height) |
775 | self.canvas.surface.change_ctx(ctx) | 808 | self.canvas.surface.change_ctx(ctx) |
776 | 809 | ||
810 | def _recomp_min_max(self, start_time, end_time, start_item, end_item): | ||
811 | if self.min_time is None or start_time < self.min_time: | ||
812 | self.min_time = start_time | ||
813 | if self.max_time is None or end_time > self.max_time: | ||
814 | self.max_time = end_time | ||
815 | if self.min_item is None or start_item < self.min_item: | ||
816 | self.min_item = start_item | ||
817 | if self.max_item is None or end_item > self.max_item: | ||
818 | self.max_item = end_item | ||
819 | |||
777 | def _get_time_xpos(self, time): | 820 | def _get_time_xpos(self, time): |
778 | """get x so that x is at instant ``time'' on the graph""" | 821 | """get x so that x is at instant ``time'' on the graph""" |
779 | return self.origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + 1.0 * (time - self.start_time) / self.attrs.time_per_maj * self.attrs.maj_sep | 822 | return self.origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + 1.0 * (time - self.start_time) / self.attrs.time_per_maj * self.attrs.maj_sep |
@@ -820,7 +863,7 @@ class Graph(object): | |||
820 | end_time = self.xcoor_to_time(self.canvas.surface.virt_x + real_x + width) | 863 | end_time = self.xcoor_to_time(self.canvas.surface.virt_x + real_x + width) |
821 | 864 | ||
822 | start_item = self.ycoor_to_item_no(self.canvas.surface.virt_y + real_y) | 865 | start_item = self.ycoor_to_item_no(self.canvas.surface.virt_y + real_y) |
823 | end_item = 1 + self.ycoor_to_item_no(self.canvas.surface.virt_y + real_y + height) | 866 | end_item = 2 + self.ycoor_to_item_no(self.canvas.surface.virt_y + real_y + height) |
824 | 867 | ||
825 | return (start_time, end_time, start_item, end_item) | 868 | return (start_time, end_time, start_item, end_item) |
826 | 869 | ||
@@ -829,7 +872,7 @@ class Graph(object): | |||
829 | self.draw_x_axis_with_labels_at_time(start_time, end_time) | 872 | self.draw_x_axis_with_labels_at_time(start_time, end_time) |
830 | self.draw_y_axis_with_labels() | 873 | self.draw_y_axis_with_labels() |
831 | 874 | ||
832 | def render_surface(self, sched, real_x, real_y, width, height, selectable=False): | 875 | def render_surface(self, sched, regions, selectable=False): |
833 | raise NotImplementedError | 876 | raise NotImplementedError |
834 | 877 | ||
835 | def render_all(self, schedule): | 878 | def render_all(self, schedule): |
@@ -946,22 +989,40 @@ class Graph(object): | |||
946 | raise NotImplementedError | 989 | raise NotImplementedError |
947 | 990 | ||
948 | class TaskGraph(Graph): | 991 | class TaskGraph(Graph): |
949 | def render_surface(self, sched, real_x, real_y, width, height, selectable=False): | 992 | def render_surface(self, sched, regions, selectable=False): |
950 | if not selectable: | 993 | events_to_render = {} |
951 | self.canvas.whiteout(real_x, real_y, width, height) | 994 | for layer in Canvas.LAYERS: |
952 | else: | 995 | events_to_render[layer] = {} |
953 | self.canvas.clear_selectable_regions(real_x, real_y, width, height) | 996 | |
997 | for region in regions: | ||
998 | x, y, width, height = region | ||
999 | if not selectable: | ||
1000 | self.canvas.whiteout(x, y, width, height) | ||
1001 | else: | ||
1002 | self.canvas.clear_selectable_regions(x, y, width, height) | ||
954 | 1003 | ||
955 | start_time, end_time, start_item, end_item = self.get_offset_params(real_x, real_y, width, height) | 1004 | self.min_time, self.max_time, self.min_item, self.max_item = None, None, None, None |
1005 | for region in regions: | ||
1006 | x, y, width, height = region | ||
1007 | start_time, end_time, start_item, end_item = self.get_offset_params(x, y, width, height) | ||
1008 | self._recomp_min_max(start_time, end_time, start_item, end_item) | ||
1009 | |||
1010 | for event in sched.get_time_slot_array().iter_over_period( | ||
1011 | start_time, end_time, start_item, end_item, | ||
1012 | schedule.TimeSlotArray.TASK_LIST, schedule.EVENT_LIST): | ||
1013 | events_to_render[event.get_layer()][event] = None | ||
956 | 1014 | ||
957 | if not selectable: | 1015 | if not selectable: |
958 | self.draw_skeleton(start_time, end_time, start_item, end_item) | 1016 | self.draw_skeleton(self.min_time, self.max_time, |
1017 | self.min_item, self.max_item) | ||
959 | 1018 | ||
1019 | #if not selectable: | ||
1020 | # for layer in events_to_render: | ||
1021 | # print 'task render on layer', layer, ':', [str(e) for e in events_to_render[layer].keys()] | ||
1022 | |||
960 | for layer in Canvas.LAYERS: | 1023 | for layer in Canvas.LAYERS: |
961 | prev_events = {} | 1024 | prev_events = {} |
962 | for event in sched.get_time_slot_array().iter_over_period( | 1025 | for event in events_to_render[layer]: |
963 | start_time, end_time, start_item, end_item, | ||
964 | schedule.TimeSlotArray.TASK_LIST, schedule.EVENT_LIST): | ||
965 | event.render(self, layer, prev_events, selectable) | 1026 | event.render(self, layer, prev_events, selectable) |
966 | 1027 | ||
967 | def draw_suspend_triangle_at_time(self, time, task_no, cpu_no, selected=False): | 1028 | def draw_suspend_triangle_at_time(self, time, task_no, cpu_no, selected=False): |
@@ -1093,42 +1154,54 @@ class TaskGraph(Graph): | |||
1093 | self.canvas.add_sel_mini_bar(x, y, width, height, event) | 1154 | self.canvas.add_sel_mini_bar(x, y, width, height, event) |
1094 | 1155 | ||
1095 | class CpuGraph(Graph): | 1156 | class CpuGraph(Graph): |
1096 | def render_surface(self, sched, real_x, real_y, width, height, selectable=False): | 1157 | def render_surface(self, sched, regions, selectable=False): |
1097 | if not selectable: | 1158 | BOTTOM_EVENTS = [schedule.ReleaseEvent, schedule.DeadlineEvent, schedule.InversionStartEvent, |
1098 | self.canvas.whiteout(real_x, real_y, width, height) | 1159 | schedule.InversionEndEvent, schedule.InversionDummy] |
1099 | else: | 1160 | TOP_EVENTS = [schedule.SuspendEvent, schedule.ResumeEvent, schedule.CompleteEvent, |
1100 | self.canvas.clear_selectable_regions(real_x, real_y, width, height) | 1161 | schedule.SwitchAwayEvent, schedule.SwitchToEvent, schedule.IsRunningDummy] |
1101 | 1162 | ||
1102 | start_time, end_time, start_item, end_item = self.get_offset_params(real_x, real_y, width, height) | 1163 | events_to_render = {} |
1103 | 1164 | for layer in Canvas.LAYERS: | |
1104 | if not selectable: | 1165 | events_to_render[layer] = {} |
1105 | self.draw_skeleton(start_time, end_time, start_item, end_item) | 1166 | |
1106 | 1167 | self.min_time, self.max_time, self.min_item, self.max_item = None, None, None, None | |
1107 | event_list = dict(schedule.EVENT_LIST) | 1168 | for region in regions: |
1169 | x, y, width, height = region | ||
1170 | if not selectable: | ||
1171 | #self.canvas.whiteout(x, y, width, height) | ||
1172 | self.canvas.whiteout(0, 0, self.canvas.surface.width, self.canvas.surface.height) | ||
1173 | else: | ||
1174 | self.canvas.clear_selectable_regions(x, y, width, height) | ||
1108 | 1175 | ||
1109 | bottom_events = [schedule.ReleaseEvent, schedule.DeadlineEvent, schedule.InversionStartEvent, | 1176 | for region in regions: |
1110 | schedule.InversionEndEvent, schedule.InversionDummy] | 1177 | x, y, width, height = region |
1178 | start_time, end_time, start_item, end_item = self.get_offset_params(x, y, width, height) | ||
1179 | self._recomp_min_max(start_time, end_time, start_item, end_item) | ||
1111 | 1180 | ||
1112 | for event in bottom_events: | ||
1113 | del event_list[event] | ||
1114 | |||
1115 | for layer in Canvas.LAYERS: | ||
1116 | prev_events = {} | ||
1117 | for event in sched.get_time_slot_array().iter_over_period( | 1181 | for event in sched.get_time_slot_array().iter_over_period( |
1118 | start_time, end_time, start_item, end_item, | 1182 | start_time, end_time, start_item, end_item, |
1119 | schedule.TimeSlotArray.CPU_LIST, schedule.EVENT_LIST): | 1183 | schedule.TimeSlotArray.CPU_LIST, |
1120 | event.render(self, layer, prev_events, selectable) | 1184 | TOP_EVENTS): |
1185 | events_to_render[event.get_layer()][event] = None | ||
1121 | 1186 | ||
1122 | if end_item >= len(self.y_item_list): | 1187 | if end_item >= len(self.y_item_list): |
1123 | # we are far down enough that we should render the releases and deadlines and inversions, | 1188 | # we are far down enough that we should render the releases and deadlines and inversions, |
1124 | # which appear near the x-axis | 1189 | # which appear near the x-axis |
1125 | for layer in Canvas.LAYERS: | 1190 | x, y, width, height = region |
1126 | prev_events = {} | ||
1127 | for event in sched.get_time_slot_array().iter_over_period( | 1191 | for event in sched.get_time_slot_array().iter_over_period( |
1128 | start_time, end_time, 0, sched.get_num_cpus(), | 1192 | start_time, end_time, 0, sched.get_num_cpus(), |
1129 | schedule.TimeSlotArray.CPU_LIST, | 1193 | schedule.TimeSlotArray.CPU_LIST, |
1130 | bottom_events): | 1194 | BOTTOM_EVENTS): |
1131 | event.render(self, layer, prev_events, selectable) | 1195 | events_to_render[event.get_layer()][event] = None |
1196 | |||
1197 | if not selectable: | ||
1198 | self.draw_skeleton(self.min_time, self.max_time, | ||
1199 | self.min_item, self.max_item) | ||
1200 | |||
1201 | for layer in Canvas.LAYERS: | ||
1202 | prev_events = {} | ||
1203 | for event in events_to_render[layer]: | ||
1204 | event.render(self, layer, prev_events, selectable) | ||
1132 | 1205 | ||
1133 | def render(self, schedule, start_time=None, end_time=None): | 1206 | def render(self, schedule, start_time=None, end_time=None): |
1134 | if end_time < start_time: | 1207 | if end_time < start_time: |
diff --git a/unit_trace/viz/format.py b/unit_trace/viz/format.py index 6469467..33e0b03 100644 --- a/unit_trace/viz/format.py +++ b/unit_trace/viz/format.py | |||
@@ -31,6 +31,9 @@ class GraphFormat(object): | |||
31 | GRID_THICKNESS = 1 | 31 | GRID_THICKNESS = 1 |
32 | AXIS_THICKNESS = 1 | 32 | AXIS_THICKNESS = 1 |
33 | 33 | ||
34 | BAND_THICKNESS = 1.5 | ||
35 | BAND_COLOR = (0.85, 0.0, 0.0) | ||
36 | |||
34 | X_AXIS_MEASURE_OFS = 30 | 37 | X_AXIS_MEASURE_OFS = 30 |
35 | X_AXIS_LABEL_GAP = 10 | 38 | X_AXIS_LABEL_GAP = 10 |
36 | Y_AXIS_ITEM_GAP = 10 | 39 | Y_AXIS_ITEM_GAP = 10 |
diff --git a/unit_trace/viz/schedule.py b/unit_trace/viz/schedule.py index ce062a3..4277f1a 100644 --- a/unit_trace/viz/schedule.py +++ b/unit_trace/viz/schedule.py | |||
@@ -65,6 +65,7 @@ class TimeSlotArray(object): | |||
65 | for slot in range(start_slot + 1, end_slot): | 65 | for slot in range(start_slot + 1, end_slot): |
66 | dummy = span_events[span_event](task_no, cpu) | 66 | dummy = span_events[span_event](task_no, cpu) |
67 | dummy.corresp_start_event = event.corresp_start_event | 67 | dummy.corresp_start_event = event.corresp_start_event |
68 | dummy.corresp_end_event = event | ||
68 | 69 | ||
69 | self._put_event_in_slot(TimeSlotArray.TASK_LIST, task_no, dummy.__class__, slot, dummy) | 70 | self._put_event_in_slot(TimeSlotArray.TASK_LIST, task_no, dummy.__class__, slot, dummy) |
70 | self._put_event_in_slot(TimeSlotArray.CPU_LIST, cpu, dummy.__class__, slot, dummy) | 71 | self._put_event_in_slot(TimeSlotArray.CPU_LIST, cpu, dummy.__class__, slot, dummy) |
@@ -151,7 +152,7 @@ class Schedule(object): | |||
151 | for event_time in sorted(job.get_events().keys()): | 152 | for event_time in sorted(job.get_events().keys()): |
152 | # could have multiple events at the same time (unlikely but possible) | 153 | # could have multiple events at the same time (unlikely but possible) |
153 | for event in job.get_events()[event_time]: | 154 | for event in job.get_events()[event_time]: |
154 | print 'task, job, event:', task.name, job.job_no, event.__class__.__name__ | 155 | #print 'task, job, event:', task.name, job.job_no, event.__class__.__name__ |
155 | event.scan(cur_cpu, switches) | 156 | event.scan(cur_cpu, switches) |
156 | 157 | ||
157 | def add_task(self, task): | 158 | def add_task(self, task): |
@@ -395,7 +396,7 @@ class SwitchAwayEvent(Event): | |||
395 | def __str__(self): | 396 | def __str__(self): |
396 | if self.corresp_start_event is None: | 397 | if self.corresp_start_event is None: |
397 | return 'Switch Away (w/o Switch To)' + self._common_str() + 'TIME=' \ | 398 | return 'Switch Away (w/o Switch To)' + self._common_str() + 'TIME=' \ |
398 | + self.get_time() | 399 | + str(self.get_time()) |
399 | return str(self.corresp_start_event) | 400 | return str(self.corresp_start_event) |
400 | 401 | ||
401 | def scan(self, cur_cpu, switches): | 402 | def scan(self, cur_cpu, switches): |
@@ -422,9 +423,25 @@ class SwitchAwayEvent(Event): | |||
422 | super(SwitchAwayEvent, self).scan(cur_cpu, switches) | 423 | super(SwitchAwayEvent, self).scan(cur_cpu, switches) |
423 | 424 | ||
424 | def render(self, graph, layer, prev_events, selectable=False): | 425 | def render(self, graph, layer, prev_events, selectable=False): |
425 | if self.corresp_start_event is None or self.corresp_start_event in prev_events: | 426 | if self.corresp_start_event is None: |
426 | return # erroneous switch away or already rendered | 427 | # We never found a corresponding start event. In that case, we can assume it lies |
427 | self.corresp_start_event.render(graph, layer, prev_events, selectable) | 428 | # in some part of the trace that was never read in. So draw a bar starting from |
429 | # the very beginning. | ||
430 | if layer == self.layer: | ||
431 | prev_events[self] = None | ||
432 | cpu = self.get_cpu() | ||
433 | task_no = self.get_job().get_task().get_task_no() | ||
434 | if selectable: | ||
435 | start = self.get_job().get_task().get_schedule().start | ||
436 | graph.add_sel_bar_at_time(start, self.get_time(), | ||
437 | task_no, cpu, self) | ||
438 | else: | ||
439 | graph.draw_bar_at_time(start, self.get_time(), | ||
440 | task_no, cpu, self.get_job().get_job_no(), self.is_selected()) | ||
441 | else: | ||
442 | if self.corresp_start_event in prev_events: | ||
443 | return # already rendered the bar | ||
444 | self.corresp_start_event.render(graph, layer, prev_events, selectable) | ||
428 | 445 | ||
429 | class SwitchToEvent(Event): | 446 | class SwitchToEvent(Event): |
430 | def __init__(self, time, cpu): | 447 | def __init__(self, time, cpu): |
@@ -434,7 +451,7 @@ class SwitchToEvent(Event): | |||
434 | def __str__(self): | 451 | def __str__(self): |
435 | if self.corresp_end_event is None: | 452 | if self.corresp_end_event is None: |
436 | return 'Switch To (w/o Switch Away)' + self._common_str() + ', TIME=' \ | 453 | return 'Switch To (w/o Switch Away)' + self._common_str() + ', TIME=' \ |
437 | + self.get_time() | 454 | + str(self.get_time()) |
438 | return 'Scheduled' + self._common_str() + ', START=' \ | 455 | return 'Scheduled' + self._common_str() + ', START=' \ |
439 | + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) \ | 456 | + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) \ |
440 | + ', END=' + util.format_float(self.corresp_end_event.get_time(), Event.NUM_DEC_PLACES) | 457 | + ', END=' + util.format_float(self.corresp_end_event.get_time(), Event.NUM_DEC_PLACES) |
@@ -454,15 +471,21 @@ class SwitchToEvent(Event): | |||
454 | def render(self, graph, layer, prev_events, selectable=False): | 471 | def render(self, graph, layer, prev_events, selectable=False): |
455 | if self.corresp_end_event is None: | 472 | if self.corresp_end_event is None: |
456 | return # fatally erroneous switch to | 473 | return # fatally erroneous switch to |
457 | if layer == Canvas.BOTTOM_LAYER: | 474 | if layer == self.layer: |
475 | end_time = None | ||
476 | if self.corresp_end_event is None: | ||
477 | end_time = self.get_job().get_task().get_schedule().end | ||
478 | else: | ||
479 | end_time = self.corresp_end_event.get_time() | ||
480 | |||
458 | prev_events[self] = None | 481 | prev_events[self] = None |
459 | cpu = self.get_cpu() | 482 | cpu = self.get_cpu() |
460 | task_no = self.get_job().get_task().get_task_no() | 483 | task_no = self.get_job().get_task().get_task_no() |
461 | if selectable: | 484 | if selectable: |
462 | graph.add_sel_bar_at_time(self.get_time(), self.corresp_end_event.get_time(), | 485 | graph.add_sel_bar_at_time(self.get_time(), end_time, |
463 | task_no, cpu, self) | 486 | task_no, cpu, self) |
464 | else: | 487 | else: |
465 | graph.draw_bar_at_time(self.get_time(), self.corresp_end_event.get_time(), | 488 | graph.draw_bar_at_time(self.get_time(), end_time, |
466 | task_no, cpu, self.get_job().get_job_no(), self.is_selected()) | 489 | task_no, cpu, self.get_job().get_job_no(), self.is_selected()) |
467 | 490 | ||
468 | class ReleaseEvent(Event): | 491 | class ReleaseEvent(Event): |
@@ -530,15 +553,21 @@ class InversionStartEvent(ErrorEvent): | |||
530 | super(InversionStartEvent, self).scan(cur_cpu, switches) | 553 | super(InversionStartEvent, self).scan(cur_cpu, switches) |
531 | 554 | ||
532 | def render(self, graph, layer, prev_events, selectable=False): | 555 | def render(self, graph, layer, prev_events, selectable=False): |
533 | if layer == Canvas.BOTTOM_LAYER: | 556 | end_time = None |
557 | if self.corresp_end_event is None: | ||
558 | end_time = self.get_job().get_task().get_schedule().end | ||
559 | else: | ||
560 | end_time = self.corresp_end_event.get_time() | ||
561 | |||
562 | if layer == self.layer: | ||
534 | prev_events[self] = None | 563 | prev_events[self] = None |
535 | cpu = self.get_cpu() | 564 | cpu = self.get_cpu() |
536 | task_no = self.get_job().get_task().get_task_no() | 565 | task_no = self.get_job().get_task().get_task_no() |
537 | if selectable: | 566 | if selectable: |
538 | graph.add_sel_mini_bar_at_time(self.get_time(), self.corresp_end_event.get_time(), | 567 | graph.add_sel_mini_bar_at_time(self.get_time(), end_time, |
539 | task_no, cpu, self) | 568 | task_no, cpu, self) |
540 | else: | 569 | else: |
541 | graph.draw_mini_bar_at_time(self.get_time(), self.corresp_end_event.get_time(), | 570 | graph.draw_mini_bar_at_time(self.get_time(), end_time, |
542 | task_no, cpu, self.get_job().get_job_no(), self.is_selected()) | 571 | task_no, cpu, self.get_job().get_job_no(), self.is_selected()) |
543 | 572 | ||
544 | 573 | ||
@@ -570,19 +599,45 @@ class InversionEndEvent(ErrorEvent): | |||
570 | super(InversionEndEvent, self).scan(cur_cpu, switches) | 599 | super(InversionEndEvent, self).scan(cur_cpu, switches) |
571 | 600 | ||
572 | def render(self, graph, layer, prev_events, selectable=False): | 601 | def render(self, graph, layer, prev_events, selectable=False): |
573 | if self.corresp_start_event is None or self.corresp_start_event in prev_events: | 602 | if self.corresp_start_event is None: |
574 | return # erroneous inversion end or already rendered | 603 | # We never found a corresponding start event. In that case, we can assume it lies |
575 | self.corresp_start_event.render(graph, layer, prev_events, selectable) | 604 | # in some part of the trace that was never read in. So draw a bar starting from |
605 | # the very beginning. | ||
606 | if layer == self.layer: | ||
607 | prev_events[self] = None | ||
608 | cpu = self.get_cpu() | ||
609 | task_no = self.get_job().get_task().get_task_no() | ||
610 | if selectable: | ||
611 | start = self.get_job().get_task().get_schedule().start | ||
612 | graph.add_sel_mini_bar_at_time(start, self.get_time(), | ||
613 | task_no, cpu, self) | ||
614 | else: | ||
615 | graph.draw_mini_bar_at_time(start, self.get_time(), | ||
616 | task_no, cpu, self.get_job().get_job_no(), self.is_selected()) | ||
617 | else: | ||
618 | if self.corresp_start_event in prev_events: | ||
619 | return # already rendered the bar | ||
620 | self.corresp_start_event.render(graph, layer, prev_events, selectable) | ||
576 | 621 | ||
577 | class InversionDummy(DummyEvent): | 622 | class InversionDummy(DummyEvent): |
623 | def __init__(self, time, cpu): | ||
624 | super(InversionDummy, self).__init__(time, Event.NO_CPU) | ||
625 | self.layer = Canvas.BOTTOM_LAYER | ||
626 | |||
578 | def render(self, graph, layer, prev_events, selectable=False): | 627 | def render(self, graph, layer, prev_events, selectable=False): |
579 | if self.corresp_start_event in prev_events: | 628 | if self.corresp_start_event is not None and self.corresp_start_event in prev_events: |
580 | return # we have already been rendered | 629 | return # we have already been rendered |
630 | if self.corresp_end_event is not None and self.corresp_end_event in prev_events: | ||
631 | return | ||
581 | self.corresp_start_event.render(graph, layer, prev_events, selectable) | 632 | self.corresp_start_event.render(graph, layer, prev_events, selectable) |
582 | 633 | ||
583 | class IsRunningDummy(DummyEvent): | 634 | class IsRunningDummy(DummyEvent): |
635 | def __init__(self, time, cpu): | ||
636 | super(IsRunningDummy, self).__init__(time, Event.NO_CPU) | ||
637 | self.layer = Canvas.BOTTOM_LAYER | ||
638 | |||
584 | def render(self, graph, layer, prev_events, selectable=False): | 639 | def render(self, graph, layer, prev_events, selectable=False): |
585 | if self.corresp_start_event in prev_events: | 640 | if self.corresp_start_event is None or self.corresp_start_event in prev_events: |
586 | return # we have already been rendered | 641 | return # we have already been rendered |
587 | self.corresp_start_event.render(graph, layer, prev_events, selectable) | 642 | self.corresp_start_event.render(graph, layer, prev_events, selectable) |
588 | 643 | ||
diff --git a/unit_trace/viz/viewer.py b/unit_trace/viz/viewer.py index 120c7e7..236a467 100644 --- a/unit_trace/viz/viewer.py +++ b/unit_trace/viz/viewer.py | |||
@@ -9,6 +9,7 @@ from renderer import * | |||
9 | import pygtk | 9 | import pygtk |
10 | import gtk | 10 | import gtk |
11 | import gobject | 11 | import gobject |
12 | import copy | ||
12 | 13 | ||
13 | class GraphContextMenu(gtk.Menu): | 14 | class GraphContextMenu(gtk.Menu): |
14 | MAX_STR_LEN = 80 | 15 | MAX_STR_LEN = 80 |
@@ -30,8 +31,7 @@ class GraphArea(gtk.DrawingArea): | |||
30 | VERT_PAGE_SCROLL_FACTOR = 3.0 | 31 | VERT_PAGE_SCROLL_FACTOR = 3.0 |
31 | VERT_STEP_SCROLL_FACTOR = 0.5 | 32 | VERT_STEP_SCROLL_FACTOR = 0.5 |
32 | 33 | ||
33 | SELECT_THICKNESS = 1.5 | 34 | REFRESH_INFLATION_FACTOR = 20.0 |
34 | SELECT_COLOR = (0.85, 0.0, 0.0) | ||
35 | 35 | ||
36 | def __init__(self, renderer): | 36 | def __init__(self, renderer): |
37 | super(GraphArea, self).__init__() | 37 | super(GraphArea, self).__init__() |
@@ -46,11 +46,11 @@ class GraphArea(gtk.DrawingArea): | |||
46 | self.set_set_scroll_adjustments_signal('set-scroll-adjustments') | 46 | self.set_set_scroll_adjustments_signal('set-scroll-adjustments') |
47 | 47 | ||
48 | self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK | | 48 | self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK | |
49 | gtk.gdk.BUTTON_RELEASE_MASK) | 49 | gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.EXPOSURE_MASK) |
50 | 50 | ||
51 | self.left_button_start_coor = None | 51 | self.band_rect = None |
52 | self.select_rect = None | ||
53 | self.ctrl_clicked = False | 52 | self.ctrl_clicked = False |
53 | self.dirtied_regions = [] | ||
54 | 54 | ||
55 | self.connect('expose-event', self.expose) | 55 | self.connect('expose-event', self.expose) |
56 | self.connect('size-allocate', self.size_allocate) | 56 | self.connect('size-allocate', self.size_allocate) |
@@ -59,23 +59,34 @@ class GraphArea(gtk.DrawingArea): | |||
59 | self.connect('button-release-event', self.button_release) | 59 | self.connect('button-release-event', self.button_release) |
60 | self.connect('motion-notify-event', self.motion_notify) | 60 | self.connect('motion-notify-event', self.motion_notify) |
61 | 61 | ||
62 | def expose(self, widget, event, data=None): | 62 | def expose(self, widget, expose_event, data=None): |
63 | ctx = widget.window.cairo_create() | 63 | ctx = widget.window.cairo_create() |
64 | graph = self.renderer.get_graph() | 64 | graph = self.renderer.get_graph() |
65 | graph.update_view(self.cur_x, self.cur_y, self.width, self.height, ctx) | 65 | graph.update_view(self.cur_x, self.cur_y, self.width, self.height, ctx) |
66 | graph.render_surface(self.renderer.get_schedule(), 0, 0, self.width, self.height) | ||
67 | 66 | ||
68 | # render dragging rectangle, if there is one | 67 | # We ourselves didn't update dirtied_regions, so this means that X or the |
69 | if self.select_rect is not None: | 68 | # window manager must have caused the expose event. So just update the |
70 | x, y, width, height = self.select_rect | 69 | # expose_event's bounding area. |
71 | thickness = GraphArea.SELECT_THICKNESS | 70 | if not self.dirtied_regions or expose_event.send_event: |
72 | color = GraphArea.SELECT_COLOR | 71 | print 'forced expose' |
72 | self.dirtied_regions = [(expose_event.area.x, expose_event.area.y, | ||
73 | expose_event.area.width, expose_event.area.height)] | ||
74 | |||
75 | graph.render_surface(self.renderer.get_schedule(), self.dirtied_regions) | ||
76 | |||
77 | # render dragging band rectangle, if there is one | ||
78 | if self.band_rect is not None: | ||
79 | x, y, width, height = self.band_rect | ||
80 | thickness = GraphFormat.BAND_THICKNESS | ||
81 | color = GraphFormat.BAND_COLOR | ||
73 | 82 | ||
74 | ctx.rectangle(x, y, width, height) | 83 | ctx.rectangle(x, y, width, height) |
75 | ctx.set_line_width(thickness) | 84 | ctx.set_line_width(thickness) |
76 | ctx.set_source_rgb(color[0], color[1], color[2]) | 85 | ctx.set_source_rgb(color[0], color[1], color[2]) |
77 | ctx.stroke() | 86 | ctx.stroke() |
78 | 87 | ||
88 | self.dirtied_regions = [] | ||
89 | |||
79 | def get_renderer(self): | 90 | def get_renderer(self): |
80 | return self.renderer | 91 | return self.renderer |
81 | 92 | ||
@@ -100,15 +111,13 @@ class GraphArea(gtk.DrawingArea): | |||
100 | self.cur_x = min(adjustment.value, self.renderer.get_graph().get_width()) | 111 | self.cur_x = min(adjustment.value, self.renderer.get_graph().get_width()) |
101 | self.cur_x = max(adjustment.value, 0.0) | 112 | self.cur_x = max(adjustment.value, 0.0) |
102 | 113 | ||
103 | rect = gtk.gdk.Rectangle(0, 0, self.width, self.height) | 114 | self._dirty(0, 0, self.width, self.height) |
104 | self.window.invalidate_rect(rect, True) | ||
105 | 115 | ||
106 | def vertical_value_changed(self, adjustment): | 116 | def vertical_value_changed(self, adjustment): |
107 | self.cur_y = min(adjustment.value, self.renderer.get_graph().get_height()) | 117 | self.cur_y = min(adjustment.value, self.renderer.get_graph().get_height()) |
108 | self.cur_y = max(adjustment.value, 0.0) | 118 | self.cur_y = max(adjustment.value, 0.0) |
109 | 119 | ||
110 | rect = gtk.gdk.Rectangle(0, 0, self.width, self.height) | 120 | self._dirty(0, 0, self.width, self.height) |
111 | self.window.invalidate_rect(rect, True) | ||
112 | 121 | ||
113 | def size_allocate(self, widget, allocation): | 122 | def size_allocate(self, widget, allocation): |
114 | self.width = allocation.width | 123 | self.width = allocation.width |
@@ -133,13 +142,25 @@ class GraphArea(gtk.DrawingArea): | |||
133 | if event.get_layer() > max_layer: | 142 | if event.get_layer() > max_layer: |
134 | max_layer = event.get_layer() | 143 | max_layer = event.get_layer() |
135 | return max_layer | 144 | return max_layer |
136 | 145 | ||
146 | def _update_event_changes(self, list1, list2): | ||
147 | # if an event changed selected status, update the bounding area | ||
148 | for event in list1: | ||
149 | if event not in list2: | ||
150 | x, y, width, height = list1[event].get_dimensions() | ||
151 | self._dirty_inflate(x - self.cur_x, y - self.cur_y, width, height, GraphFormat.BORDER_THICKNESS) | ||
152 | for event in list2: | ||
153 | if event not in list1: | ||
154 | x, y, width, height = list2[event].get_dimensions() | ||
155 | self._dirty_inflate(x - self.cur_x, y - self.cur_y, width, height, GraphFormat.BORDER_THICKNESS) | ||
156 | |||
137 | def motion_notify(self, widget, motion_event, data=None): | 157 | def motion_notify(self, widget, motion_event, data=None): |
138 | msg = None | 158 | msg = None |
139 | 159 | ||
140 | graph = self.renderer.get_graph() | 160 | graph = self.renderer.get_graph() |
141 | 161 | ||
142 | graph.render_surface(self.renderer.get_schedule(), motion_event.x, motion_event.y, 0, 0, True) | 162 | graph.render_surface(self.renderer.get_schedule(), [(motion_event.x, motion_event.y, |
163 | 0, 0)], True) | ||
143 | just_selected = graph.get_selected_regions(motion_event.x, motion_event.y, 0, 0) | 164 | just_selected = graph.get_selected_regions(motion_event.x, motion_event.y, 0, 0) |
144 | if not just_selected: | 165 | if not just_selected: |
145 | msg = '' | 166 | msg = '' |
@@ -156,25 +177,31 @@ class GraphArea(gtk.DrawingArea): | |||
156 | 177 | ||
157 | self.emit('update-event-description', the_event, msg) | 178 | self.emit('update-event-description', the_event, msg) |
158 | 179 | ||
159 | if self.left_button_start_coor is not None: | 180 | if self.band_rect is not None: |
160 | selected = {} | 181 | selected = {} |
182 | was_selected = self.renderer.get_schedule().get_selected() | ||
161 | if self.ctrl_clicked: | 183 | if self.ctrl_clicked: |
162 | selected = dict(self.last_selected) | 184 | selected = copy.copy(was_selected) |
163 | 185 | ||
164 | # dragging a rectangle | 186 | # dragging a rectangle |
165 | x = min(self.left_button_start_coor[0], motion_event.x) | 187 | x = self.band_rect[0] |
166 | y = min(self.left_button_start_coor[1], motion_event.y) | 188 | y = self.band_rect[1] |
167 | width = abs(self.left_button_start_coor[0] - motion_event.x) | 189 | width = motion_event.x - self.band_rect[0] |
168 | height = abs(self.left_button_start_coor[1] - motion_event.y) | 190 | height = motion_event.y - self.band_rect[1] |
169 | 191 | ||
170 | graph.render_surface(self.renderer.get_schedule(), x, y, width, height, True) | 192 | x_p, y_p, width_p, height_p = self._positivify(x, y, width, height) |
171 | selected.update(graph.get_selected_regions(x, y, width, height)) | 193 | graph.render_surface(self.renderer.get_schedule(), [(x_p, y_p, width_p, height_p)], True) |
194 | selected.update(graph.get_selected_regions(x_p, y_p, width_p, height_p)) | ||
172 | self.renderer.get_schedule().set_selected(selected) | 195 | self.renderer.get_schedule().set_selected(selected) |
173 | 196 | ||
174 | self.select_rect = (x, y, width, height) | 197 | old_x, old_y, old_width, old_height = self.band_rect |
198 | self.band_rect = (x, y, width, height) | ||
175 | 199 | ||
176 | self._dirty() | 200 | self._dirty_rect_border(old_x, old_y, old_width, old_height, GraphFormat.BAND_THICKNESS) |
201 | self._dirty_rect_border(x, y, width, height, GraphFormat.BAND_THICKNESS) | ||
177 | 202 | ||
203 | self._update_event_changes(was_selected, selected) | ||
204 | |||
178 | def button_press(self, widget, button_event, data=None): | 205 | def button_press(self, widget, button_event, data=None): |
179 | graph = self.renderer.get_graph() | 206 | graph = self.renderer.get_graph() |
180 | 207 | ||
@@ -183,15 +210,16 @@ class GraphArea(gtk.DrawingArea): | |||
183 | if button_event.button == 1: | 210 | if button_event.button == 1: |
184 | self.left_button_start_coor = (button_event.x, button_event.y) | 211 | self.left_button_start_coor = (button_event.x, button_event.y) |
185 | graph.render_surface(self.renderer.get_schedule(), \ | 212 | graph.render_surface(self.renderer.get_schedule(), \ |
186 | button_event.x, button_event.y, 0, 0, True) | 213 | [(button_event.x, button_event.y, 0, 0)], True) |
187 | 214 | ||
188 | just_selected = graph.get_selected_regions(button_event.x, button_event.y, 0, 0) | 215 | just_selected = graph.get_selected_regions(button_event.x, button_event.y, 0, 0) |
189 | 216 | ||
190 | max_layer = self._find_max_layer(just_selected) | 217 | max_layer = self._find_max_layer(just_selected) |
191 | 218 | ||
192 | new_now_selected = None | 219 | new_now_selected = None |
220 | was_selected = self.renderer.get_schedule().get_selected() | ||
193 | if self.ctrl_clicked: | 221 | if self.ctrl_clicked: |
194 | new_now_selected = self.renderer.get_schedule().get_selected() | 222 | new_now_selected = copy.copy(was_selected) |
195 | else: | 223 | else: |
196 | new_now_selected = {} | 224 | new_now_selected = {} |
197 | 225 | ||
@@ -206,45 +234,75 @@ class GraphArea(gtk.DrawingArea): | |||
206 | if (len(now_selected) == 1 or self.ctrl_clicked) and event in now_selected: | 234 | if (len(now_selected) == 1 or self.ctrl_clicked) and event in now_selected: |
207 | val = not event.is_selected | 235 | val = not event.is_selected |
208 | if val: | 236 | if val: |
209 | new_now_selected[event] = None | 237 | new_now_selected[event] = graph.get_sel_region(event) |
210 | elif event in new_now_selected: | 238 | elif event in new_now_selected: |
211 | del new_now_selected[event] | 239 | del new_now_selected[event] |
212 | break # only pick one event when just clicking | 240 | break # only pick one event when just clicking |
213 | 241 | ||
214 | self.renderer.get_schedule().set_selected(new_now_selected) | 242 | self.renderer.get_schedule().set_selected(new_now_selected) |
215 | self.last_selected = dict(new_now_selected) | 243 | #self.last_selected = new_now_selected |
216 | 244 | ||
217 | self._dirty() | 245 | self._update_event_changes(new_now_selected, was_selected) |
246 | |||
247 | if self.band_rect is None: | ||
248 | self.band_rect = (button_event.x, button_event.y, 0, 0) | ||
249 | |||
218 | elif button_event.button == 3: | 250 | elif button_event.button == 3: |
219 | self._release_band() | 251 | self._release_band() |
220 | self.emit('request-context-menu', button_event, self.renderer.get_schedule().get_selected()) | 252 | self.emit('request-context-menu', button_event, self.renderer.get_schedule().get_selected()) |
221 | 253 | ||
222 | def button_release(self, widget, button_event, data=None): | 254 | def button_release(self, widget, button_event, data=None): |
223 | self.ctrl_clicked = False | 255 | self.ctrl_clicked = False |
224 | self.last_selected = dict(self.renderer.get_schedule().get_selected()) | 256 | #self.last_selected = copy.copy(self.renderer.get_schedule().get_selected()) |
225 | 257 | ||
226 | if button_event.button == 1: | 258 | if button_event.button == 1: |
227 | self._release_band() | 259 | self._release_band() |
228 | 260 | ||
229 | def _release_band(self): | 261 | def _release_band(self): |
230 | old_select_rect = self.select_rect | 262 | if self.band_rect is not None: |
231 | self.left_button_start_coor = None | 263 | x, y, width, height = self.band_rect |
232 | self.select_rect = None | 264 | self._dirty_rect_border(x, y, width, height, GraphFormat.BAND_THICKNESS) |
233 | if old_select_rect is not None: | 265 | self.band_rect = None |
234 | self._dirty() | ||
235 | 266 | ||
236 | def _dirty(self, x=None, y=None, width=None, height=None): | 267 | def _dirty(self, x, y, width, height): |
237 | if x is None: | 268 | x = max(int(math.floor(x)), 0) |
238 | x = 0 | 269 | y = max(int(math.floor(y)), 0) |
239 | if y is None: | 270 | width = min(int(math.ceil(width)), self.width) |
240 | y = 0 | 271 | height = min(int(math.ceil(height)), self.height) |
241 | if width is None: | 272 | |
242 | width = self.width | 273 | self.dirtied_regions.append((x, y, width, height)) |
243 | if height is None: | 274 | |
244 | height = self.height | ||
245 | rect = gtk.gdk.Rectangle(x, y, width, height) | 275 | rect = gtk.gdk.Rectangle(x, y, width, height) |
246 | self.window.invalidate_rect(rect, True) | 276 | self.window.invalidate_rect(rect, True) |
247 | 277 | ||
278 | def _dirty_inflate(self, x, y, width, height, thickness): | ||
279 | t = thickness * GraphArea.REFRESH_INFLATION_FACTOR | ||
280 | x -= t / 2.0 | ||
281 | y -= t / 2.0 | ||
282 | width += t | ||
283 | height += t | ||
284 | self._dirty(x, y, width, height) | ||
285 | |||
286 | def _dirty_rect_border(self, x, y, width, height, thickness): | ||
287 | # support rectangles with negative width and height (i.e. -width = width, but going leftwards | ||
288 | # instead of rightwards) | ||
289 | x, y, width, height = self._positivify(x, y, width, height) | ||
290 | |||
291 | self._dirty_inflate(x, y, width, 0, thickness) | ||
292 | self._dirty_inflate(x, y, 0, height, thickness) | ||
293 | self._dirty_inflate(x, y + height, width, 0, thickness) | ||
294 | self._dirty_inflate(x + width, y, 0, height, thickness) | ||
295 | |||
296 | def _positivify(self, x, y, width, height): | ||
297 | if width < 0: | ||
298 | x += width | ||
299 | width = -width | ||
300 | if height < 0: | ||
301 | y += height | ||
302 | height = -height | ||
303 | |||
304 | return x, y, width, height | ||
305 | |||
248 | class GraphWindow(gtk.ScrolledWindow): | 306 | class GraphWindow(gtk.ScrolledWindow): |
249 | def __init__(self, renderer): | 307 | def __init__(self, renderer): |
250 | super(GraphWindow, self).__init__(None, None) | 308 | super(GraphWindow, self).__init__(None, None) |
@@ -310,20 +368,13 @@ class MainWindow(gtk.Window): | |||
310 | WINDOW_WIDTH_REQ = 500 | 368 | WINDOW_WIDTH_REQ = 500 |
311 | WINDOW_HEIGHT_REQ = 300 | 369 | WINDOW_HEIGHT_REQ = 300 |
312 | 370 | ||
313 | def __init__(self, request_renderer_change): | 371 | def __init__(self): |
314 | super(MainWindow, self).__init__(gtk.WINDOW_TOPLEVEL) | 372 | super(MainWindow, self).__init__(gtk.WINDOW_TOPLEVEL) |
315 | 373 | ||
316 | self.connect('delete_event', self.delete_event) | 374 | self.connect('delete_event', self.delete_event) |
317 | self.connect('destroy', self.die) | 375 | self.connect('destroy', self.die) |
318 | |||
319 | self.connect('request-renderer-change', request_renderer_change) | ||
320 | 376 | ||
321 | self.file_menu = gtk.Menu() | 377 | self.file_menu = gtk.Menu() |
322 | self.open_item = gtk.MenuItem('_Open', True) | ||
323 | self.open_item.connect('activate', self.open_item_activate) | ||
324 | self.file_menu.append(self.open_item) | ||
325 | self.open_item.show() | ||
326 | |||
327 | self.quit_item = gtk.MenuItem('_Quit', True) | 378 | self.quit_item = gtk.MenuItem('_Quit', True) |
328 | self.quit_item.connect('activate', self.quit_item_activate) | 379 | self.quit_item.connect('activate', self.quit_item_activate) |
329 | self.quit_item.show() | 380 | self.quit_item.show() |
@@ -397,21 +448,6 @@ class MainWindow(gtk.Window): | |||
397 | menu = GraphContextMenu(selected) | 448 | menu = GraphContextMenu(selected) |
398 | menu.popup(None, None, None, button, time) | 449 | menu.popup(None, None, None, button, time) |
399 | 450 | ||
400 | def open_item_activate(self, widget): | ||
401 | dialog = gtk.FileChooserDialog('Open File', self, | ||
402 | gtk.FILE_CHOOSER_ACTION_OPEN, | ||
403 | (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, | ||
404 | gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT)) | ||
405 | dialog.set_select_multiple(True) | ||
406 | |||
407 | if dialog.run() == gtk.RESPONSE_ACCEPT: | ||
408 | filenames = dialog.get_filenames() | ||
409 | dialog.destroy() | ||
410 | self.emit('request-renderer-change', filenames, None) | ||
411 | else: | ||
412 | dialog.destroy() | ||
413 | |||
414 | |||
415 | def quit_item_activate(self, widget): | 451 | def quit_item_activate(self, widget): |
416 | self.destroy() | 452 | self.destroy() |
417 | 453 | ||