From 883f9dfe38ab081025220aafbf47f722b540d003 Mon Sep 17 00:00:00 2001 From: Gary Bressler Date: Tue, 9 Mar 2010 13:33:54 -0500 Subject: Preliminary, fairly workable version of unit_trace, including the visualizer. --- viz/viewer.py | 253 ++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 212 insertions(+), 41 deletions(-) (limited to 'viz/viewer.py') diff --git a/viz/viewer.py b/viz/viewer.py index a695473..a7871a7 100644 --- a/viz/viewer.py +++ b/viz/viewer.py @@ -3,20 +3,36 @@ """GUI stuff.""" from schedule import * + from renderer import * import pygtk import gtk import gobject + +class GraphContextMenu(gtk.Menu): + MAX_STR_LEN = 80 + def __init__(self, selected): + super(GraphContextMenu, self).__init__() + + for event in selected: + string = str(event) + if len(string) > GraphContextMenu.MAX_STR_LEN - 3: + string = string[:GraphContextMenu.MAX_STR_LEN - 3] + '...' + item = gtk.MenuItem(string) + self.append(item) + item.show() + class GraphArea(gtk.DrawingArea): - DAREA_WIDTH_REQ = 500 - DAREA_HEIGHT_REQ = 300 HORIZ_PAGE_SCROLL_FACTOR = 4.8 HORIZ_STEP_SCROLL_FACTOR = 0.8 VERT_PAGE_SCROLL_FACTOR = 3.0 VERT_STEP_SCROLL_FACTOR = 0.5 + SELECT_THICKNESS = 1.5 + SELECT_COLOR = (0.85, 0.0, 0.0) + def __init__(self, renderer): super(GraphArea, self).__init__() @@ -27,25 +43,38 @@ class GraphArea(gtk.DrawingArea): self.width = 0 self.height = 0 - self.now_selected = [] - self.set_set_scroll_adjustments_signal('set-scroll-adjustments') - self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK) + self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK | + gtk.gdk.BUTTON_RELEASE_MASK) + + self.left_button_start_coor = None + self.select_rect = None + self.ctrl_clicked = False self.connect('expose-event', self.expose) self.connect('size-allocate', self.size_allocate) self.connect('set-scroll-adjustments', self.set_scroll_adjustments) self.connect('button-press-event', self.button_press) + self.connect('button-release-event', self.button_release) self.connect('motion-notify-event', self.motion_notify) - self.set_size_request(GraphArea.DAREA_WIDTH_REQ, GraphArea.DAREA_HEIGHT_REQ) - def expose(self, widget, event, data=None): ctx = widget.window.cairo_create() graph = self.renderer.get_graph() graph.update_view(self.cur_x, self.cur_y, self.width, self.height, ctx) - graph.render_surface(self.renderer.get_schedule()) + graph.render_surface(self.renderer.get_schedule(), 0, 0, self.width, self.height) + + # render dragging rectangle, if there is one + if self.select_rect is not None: + x, y, width, height = self.select_rect + thickness = GraphArea.SELECT_THICKNESS + color = GraphArea.SELECT_COLOR + + ctx.rectangle(x, y, width, height) + ctx.set_line_width(thickness) + ctx.set_source_rgb(color[0], color[1], color[2]) + ctx.stroke() def set_scroll_adjustments(self, widget, horizontal, vertical, data=None): graph = self.renderer.get_graph() @@ -55,9 +84,11 @@ class GraphArea(gtk.DrawingArea): self.horizontal = horizontal self.vertical = vertical self.config_scrollbars(self.cur_x, self.cur_y) - - self.horizontal.connect('value-changed', self.horizontal_value_changed) - self.vertical.connect('value-changed', self.vertical_value_changed) + + if self.horizontal is not None: + self.horizontal.connect('value-changed', self.horizontal_value_changed) + if self.vertical is not None: + self.vertical.connect('value-changed', self.vertical_value_changed) def horizontal_value_changed(self, adjustment): self.cur_x = min(adjustment.value, self.renderer.get_graph().get_width()) @@ -85,7 +116,7 @@ class GraphArea(gtk.DrawingArea): if self.horizontal is not None: self.horizontal.set_all(hvalue, 0.0, width, graph.get_attrs().maj_sep * GraphArea.HORIZ_STEP_SCROLL_FACTOR, - graph.get_attrs().maj_sep * GraphArea.HORIZ_PAGE_SCROLL_FACTOR, self.width) + graph.get_attrs().maj_sep * GraphArea.HORIZ_PAGE_SCROLL_FACTOR, self.width) if self.vertical is not None: self.vertical.set_all(vvalue, 0.0, height, graph.get_attrs().y_item_size * GraphArea.VERT_STEP_SCROLL_FACTOR, graph.get_attrs().y_item_size * GraphArea.VERT_PAGE_SCROLL_FACTOR, self.height) @@ -101,7 +132,9 @@ class GraphArea(gtk.DrawingArea): msg = None graph = self.renderer.get_graph() - just_selected = graph.get_selected_regions(motion_event.x, motion_event.y) + + graph.render_surface(self.renderer.get_schedule(), motion_event.x, motion_event.y, 0, 0, True) + just_selected = graph.get_selected_regions(motion_event.x, motion_event.y, 0, 0) if not just_selected: msg = '' the_event = None @@ -116,36 +149,96 @@ class GraphArea(gtk.DrawingArea): msg = str(the_event) self.emit('update-event-description', the_event, msg) + + if self.left_button_start_coor is not None: + selected = {} + if self.ctrl_clicked: + selected = dict(self.last_selected) + + # dragging a rectangle + x = min(self.left_button_start_coor[0], motion_event.x) + y = min(self.left_button_start_coor[1], motion_event.y) + width = abs(self.left_button_start_coor[0] - motion_event.x) + height = abs(self.left_button_start_coor[1] - motion_event.y) + + graph.render_surface(self.renderer.get_schedule(), x, y, width, height, True) + selected.update(graph.get_selected_regions(x, y, width, height)) + self.renderer.get_schedule().set_selected(selected) + + self.select_rect = (x, y, width, height) + self._dirty() + def button_press(self, widget, button_event, data=None): graph = self.renderer.get_graph() + self.ctrl_clicked = button_event.state & gtk.gdk.CONTROL_MASK + if button_event.button == 1: - just_selected = graph.get_selected_regions(button_event.x, button_event.y) + self.left_button_start_coor = (button_event.x, button_event.y) + graph.render_surface(self.renderer.get_schedule(), \ + button_event.x, button_event.y, 0, 0, True) + + just_selected = graph.get_selected_regions(button_event.x, button_event.y, 0, 0) max_layer = self._find_max_layer(just_selected) + new_now_selected = None + if self.ctrl_clicked: + new_now_selected = self.renderer.get_schedule().get_selected() + else: + new_now_selected = {} + # only select those events which were in the top layer (it's # not intuitive to click something and then have something # below it get selected). Also, clicking something that - # is selected deselects it - new_now_selected = {} + # is selected deselects it, if it's the only thing selected for event in just_selected: if event.get_layer() == max_layer: - if not event.is_selected(): + val = True + now_selected = self.renderer.get_schedule().get_selected() + if (len(now_selected) == 1 or self.ctrl_clicked) and event in now_selected: + val = not event.is_selected + if val: new_now_selected[event] = None - event.set_selected(not event.is_selected()) - break + elif event in new_now_selected: + del new_now_selected[event] + break # only pick one event when just clicking - for event in self.now_selected: - if event not in new_now_selected: - event.set_selected(False) - - self.now_selected = new_now_selected + self.renderer.get_schedule().set_selected(new_now_selected) + self.last_selected = dict(new_now_selected) - rect = gtk.gdk.Rectangle(0, 0, self.width, self.height) - self.window.invalidate_rect(rect, True) - + self._dirty() + elif button_event.button == 3: + self._release_band() + self.emit('request-context-menu', button_event, self.renderer.get_schedule().get_selected()) + + def button_release(self, widget, button_event, data=None): + self.ctrl_clicked = False + self.last_selected = dict(self.renderer.get_schedule().get_selected()) + + if button_event.button == 1: + self._release_band() + + def _release_band(self): + old_select_rect = self.select_rect + self.left_button_start_coor = None + self.select_rect = None + if old_select_rect is not None: + self._dirty() + + def _dirty(self, x=None, y=None, width=None, height=None): + if x is None: + x = 0 + if y is None: + y = 0 + if width is None: + width = self.width + if height is None: + height = self.height + rect = gtk.gdk.Rectangle(x, y, width, height) + self.window.invalidate_rect(rect, True) + class GraphWindow(gtk.ScrolledWindow): def __init__(self, renderer): super(GraphWindow, self).__init__(None, None) @@ -157,37 +250,115 @@ class GraphWindow(gtk.ScrolledWindow): def get_graph_area(self): return self.garea -class MainWindow(object): - def __init__(self, renderer): - self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) +class MainWindow(gtk.Window): + WINDOW_WIDTH_REQ = 500 + WINDOW_HEIGHT_REQ = 300 + + def __init__(self, request_renderer_change): + super(MainWindow, self).__init__(gtk.WINDOW_TOPLEVEL) + + self.connect('delete_event', self.delete_event) + self.connect('destroy', self.die) + + self.connect('request-renderer-change', request_renderer_change) + + self.file_menu = gtk.Menu() + self.open_item = gtk.MenuItem('_Open', True) + self.open_item.connect('activate', self.open_item_activate) + self.file_menu.append(self.open_item) + self.open_item.show() + + self.quit_item = gtk.MenuItem('_Quit', True) + self.quit_item.connect('activate', self.quit_item_activate) + self.quit_item.show() + self.file_menu.append(self.quit_item) - self.window.connect('delete_event', self.delete_event) - self.window.connect('destroy', self.destroy) + self.file_item = gtk.MenuItem('_File', True) + self.file_item.set_submenu(self.file_menu) + self.file_item.show() + + self.menu_bar = gtk.MenuBar() + self.menu_bar.append(self.file_item) + + self.menu_bar.show() self.vbox = gtk.VBox(False, 0) - self.gwindow = GraphWindow(renderer) - self.gwindow.get_graph_area().connect('update-event-description', - self.update_event_description) - self.gwindow.show() + self.notebook = gtk.Notebook() + + self.notebook.last_page = -1 + self.notebook.connect('switch-page', self.switch_page) + + self.notebook.show() self.desc_label = gtk.Label('') self.desc_label.set_justify(gtk.JUSTIFY_LEFT) self.desc_label.show() - self.vbox.pack_start(self.gwindow, True, True, 0) + self.vbox.pack_start(self.menu_bar, False, False, 0) + self.vbox.pack_start(self.notebook, True, True, 0) self.vbox.pack_start(self.desc_label, False, False, 0) self.vbox.show() - self.window.add(self.vbox) - self.window.show() - + self.add(self.vbox) + + self.set_size_request(MainWindow.WINDOW_WIDTH_REQ, MainWindow.WINDOW_HEIGHT_REQ) + + self.show() + + def connect_widgets(self, garea): + garea.connect('update-event-description', self.update_event_description) + garea.connect('request-context-menu', self.request_context_menu) + + def set_renderers(self, renderers): + for i in range(0, self.notebook.get_n_pages()): + self.notebook.remove_page(0) + for title in renderers: + gwindow = GraphWindow(renderers[title]) + self.connect_widgets(gwindow.get_graph_area()) + gwindow.show() + self.notebook.append_page(gwindow, gtk.Label(title)) + + def switch_page(self, widget, page, page_num): + if self.notebook.last_page >= 0: + hvalue = self.notebook.get_nth_page(self.notebook.last_page).get_hadjustment().get_value() + self.notebook.get_nth_page(page_num).get_hadjustment().set_value(hvalue) + + self.notebook.last_page = page_num + def update_event_description(self, widget, event, msg): self.desc_label.set_text(msg) + def request_context_menu(self, widget, gdk_event, selected): + button = 0 + if hasattr(gdk_event, 'button'): + button = gdk_event.button + time = gdk_event.time + + menu = GraphContextMenu(selected) + menu.popup(None, None, None, button, time) + + def open_item_activate(self, widget): + dialog = gtk.FileChooserDialog('Open File', self, + gtk.FILE_CHOOSER_ACTION_OPEN, + (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, + gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT)) + dialog.set_select_multiple(True) + + if dialog.run() == gtk.RESPONSE_ACCEPT: + filenames = dialog.get_filenames() + dialog.destroy() + self.emit('request-renderer-change', filenames, None) + else: + dialog.destroy() + + + def quit_item_activate(self, widget): + self.destroy() + def delete_event(self, widget, event, data=None): return False - def destroy(self, widget, data=None): + def die(self, widget, data=None): gtk.main_quit() -- cgit v1.2.2