import pygtk pygtk.require('2.0') import gtk import gtk.glade import gobject import os.path from trace import Trace from trace_view import TraceView from trace_tree_model import TraceTreeModel class TraceTreeWindow (gtk.Expander): def __init__ (self, trace, box = None, remove_cb = None, cursor_cb = None, highlight_cb = None): gtk.Expander.__init__ (self, trace.name) self.trace = trace self.set_expanded (True) ttm = TraceTreeModel (trace) # TreeView. tv = gtk.TreeView () self.tv = tv cell = gtk.CellRendererText () tvc = gtk.TreeViewColumn (ttm.column_names[0], cell, text=0) tv.append_column (tvc) cell = gtk.CellRendererText () tvc = gtk.TreeViewColumn (ttm.column_names[2], cell, text=2) tv.append_column (tvc) tv.set_model (ttm) tv.set_search_column (2) tv.connect ('cursor-changed', self.row_selected) self.no_update = False self.cursor_cb = cursor_cb # Actions. action_box = gtk.VBox () remove_button = gtk.Button () remove_button.add (gtk.image_new_from_stock (gtk.STOCK_DELETE, gtk.ICON_SIZE_MENU)) action_box.pack_start (remove_button, expand = False) remove_button.connect ('clicked', self.remove_clicked) self.remove_cb = remove_cb # Add. sw = gtk.ScrolledWindow () sw.set_policy (gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS) sw.add (tv) hbox = gtk.HBox () hbox.add (sw) hbox.pack_start (action_box, expand = False) self.add (hbox) # Handle expansion. self.box = box if box: self.connect ('notify::expanded', self.notify_expanded) # Handle highlight. self.highlight_cb = highlight_cb if highlight_cb: self.connect ('enter-notify-event', self.highlight_on) self.connect ('leave-notify-event', self.highlight_off) def notify_expanded (self, expander, param_spec): if self.get_expanded (): self.box.set_child_packing (self, True, True, 0, gtk.PACK_START) else: self.box.set_child_packing (self, False, True, 0, gtk.PACK_START) def set_cursor (self, date): if not self.no_update: self.no_update = True row = self.trace.row (date) self.tv.set_cursor (row) self.no_update = False def row_selected (self, treeview): if not self.no_update: self.no_update = True row = treeview.get_cursor ()[0][0] tracerow = self.trace.trace[row] date = tracerow.date aux = tracerow.aux () if self.cursor_cb: self.cursor_cb (date, aux) self.no_update = False def remove_clicked (self, button): self.remove_cb (self.trace) def highlight_on (self, widget, event): self.highlight_cb (self.trace) def highlight_off (self, widget, event): self.highlight_cb () class TracesListView (gtk.TreeView): def __init__ (self, trace_visible_toggle_cb): self.model = gtk.ListStore (bool, str, object) gtk.TreeView.__init__ (self, self.model) cell = gtk.CellRendererToggle () cell.set_property ('activatable', True) cell.connect ('toggled', self.visible_toggle_cb, None) tvc = gtk.TreeViewColumn ("Show", cell, active = 0) self.append_column (tvc) cell = gtk.CellRendererText () tvc = gtk.TreeViewColumn ("Trace", cell, text = 1) self.append_column (tvc) self.trace_visible_toggle_cb = trace_visible_toggle_cb def add (self, trace): self.model.append ((trace.visible, trace.name, trace)) def remove (self, trace): for l in self.model: if l[2] is trace: self.model.remove (l.iter) def show (self, trace, state = True): for l in self.model: if l[2] is trace: l[0] = state def hide (self, trace): self.show (trace, False) def visible_toggle_cb (self, cell, path, user): self.trace_visible_toggle_cb (self.model[path][2]) class Gui: def __init__ (self, zoom = 1.0): traceviewerdir = os.path.dirname (__file__) xml = gtk.glade.XML (os.path.join (traceviewerdir, 'traceviewer.glade')) self.window = xml.get_widget ('main_window') self.window.connect ('destroy', lambda w: gtk.main_quit ()) xml.signal_autoconnect (self) self.trace_view = TraceView () self.trace_view.zoom = zoom self.trace_sw = xml.get_widget ('trace_sw') self.trace_sw.add_with_viewport (self.trace_view) self.trace_view.connect ('cursor-tck', self.on_cursor_tck) self.trace_sw.connect ('scroll-event', self.on_zoom_scroll_event) self.trace_tree_box = xml.get_widget ('trace_tree_box') self.traces_list_view = TracesListView (self.on_trace_visible_toggle) control_box = xml.get_widget ('control_box') control_box.pack_start (self.traces_list_view, expand = False) self.zoom_entry = xml.get_widget ('zoom_entry') self.user_cursor_entry = ( xml.get_widget ('user_cursor_1_entry'), xml.get_widget ('user_cursor_2_entry'), ) for uce, i in zip (self.user_cursor_entry, range (len (self.user_cursor_entry))): uce.child.i = i uce.child.connect ('changed', self.on_user_cursor_entry_change) ucem = gtk.ListStore (gobject.TYPE_STRING) uce.set_model (ucem) uce.set_text_column (0) self.user_cursor_diff_label = xml.get_widget ( 'user_cursor_diff_label') self.update_zoom () self.update_cursors () self.window.show_all () self.no_cursor_update = False def add (self, trace): self.trace_view.add (trace) self.traces_list_view.add (trace) ttw = TraceTreeWindow (trace, self.trace_tree_box, self.remove, self.set_cursor, self.trace_view.highlight) self.trace_tree_box.add (ttw) ttw.show_all () def remove (self, trace): for ttw in self.trace_tree_box.get_children (): if ttw.trace is trace: self.trace_view.remove (trace) self.traces_list_view.remove (trace) self.trace_tree_box.remove (ttw) def show (self, trace): for ttw in self.trace_tree_box.get_children (): if ttw.trace is trace: self.trace_view.show (trace) self.traces_list_view.show (trace) ttw.show () def hide (self, trace): for ttw in self.trace_tree_box.get_children (): if ttw.trace is trace: self.trace_view.hide (trace) self.traces_list_view.hide (trace) ttw.hide () def show_all (self): self.window.show_all () def zoom (self, zoom, x = None): zoom_change = zoom / self.trace_view.zoom adj = self.trace_sw.get_hadjustment () if x is None: offset = adj.page_size * 0.5 else: offset = x - adj.value old_pos = adj.value + offset self.trace_view.zoom_set (zoom) self.update_zoom () new_value = zoom_change * old_pos - offset new_value = min (new_value, self.trace_view.get_size_request ()[0] - adj.page_size) new_value = max (0, new_value) adj.value = new_value def on_zoom_scroll_event (self, button, event): if event.direction == gtk.gdk.SCROLL_UP: self.zoom (self.trace_view.zoom * 2, event.x) else: self.zoom (self.trace_view.zoom * 0.5, event.x) return True def on_zoom_fit_clicked (self, button): self.trace_view.zoom_fit (self.trace_sw.get_allocation ().width - 40) self.update_zoom () def on_zoom_in_clicked (self, button): self.zoom (self.trace_view.zoom * 2) def on_zoom_out_clicked (self, button): self.zoom (self.trace_view.zoom * 0.5) def on_center_clicked (self, button): self.center () def on_zoom_entry_activate (self, entry): self.zoom (float (self.zoom_entry.get_text ())) def on_user_cursor_entry_change (self, entry): try: date = eval (entry.get_text ()) except: date = None else: date = long (date) self.trace_view.set_cursor_user (entry.i, date) self.update_cursors () def on_quit_clicked (self, button): gtk.main_quit () def on_cursor_tck (self, trace_view, date): for i in self.trace_tree_box.get_children (): i.set_cursor (date) self.set_cursor (date) def set_cursor (self, date, aux = None): if not self.no_cursor_update: self.no_cursor_update = True self.trace_view.set_cursor (date, aux) for uce in self.user_cursor_entry: uce.get_model ().clear () uce.get_model ().append (('0x%08x' % date, )) if aux: for a in aux: uce.get_model ().append (('0x%08x' % a, )) self.no_cursor_update = False self.update_cursors () def update_cursors (self): cu = self.trace_view.cursor_user c = self.trace_view.cursor label = '' if cu[0] is not None and cu[1] is not None: diff = cu[1] - cu[0] label += '%d (%.2f)' % (diff, float (diff) / 25) label += '\n' for i in xrange (len (cu)): if c is not None and cu[i] is not None: diff = c - cu[i] label += 'c%d%+d (%+.2f)' % (i + 1, diff, float (diff) / 25) label += '\n' self.user_cursor_diff_label.set_label (label) def update_zoom (self): self.zoom_entry.set_text ('%g' % self.trace_view.zoom) def on_trace_visible_toggle (self, trace): if trace.visible: self.hide (trace) else: self.show (trace) def center (self, date = None): if date is None: date = self.trace_view.cursor if date is not None: date_x = self.trace_view.date_to_coord_x (date) adj = self.trace_sw.get_hadjustment () value = date_x - adj.page_size // 2 value = max (value, 0) value = min (value, adj.upper - adj.page_size) adj.value = value def run (self): gtk.main () if __name__ == '__main__': gui = Gui () # Traces. trace = Trace ('test') tf = [ '[.] first undated\r\n', '[.] second undated\r\n', '[0x00000011] first dated\r\n', '[0x00000022] second dated date=0x00000011 date=0x00000033\r\n', '[.] third undated\r\n', '[0x00000033] third dated\r\n', '[0x00000033] third dated bis\r\n', ] trace.from_dump (tf) gui.add (trace) trace = Trace ('test 2') tf = [ '[.] foo\r\n', '[.] bar\r\n', '[0x00000015] foo bar\r\n', '[0x00000022] bar foo\r\n', '[0x00000040] bar bar\r\n', '[.] foo foo\r\n', '[0x00000050] bar foo bar\r\n', ] trace.from_dump (tf) gui.add (trace) gtk.main ()