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 | ||
