#!/usr/bin/python # # Browse a Python dictionary in a two pane graphical interface written # in GTK. # # The GtkDictBrowser class is supposed to be generic enough to allow # applications to override enough methods and produce a # domain-specific browser provided the information is presented as a # Python dictionary. # # Possible applications: # # - Windows registry browser # - SPOOLSS printerdata browser # - tdb file browser # from gtk import * import string, re class GtkDictBrowser: def __init__(self, dict): self.dict = dict # This variable stores a list of (regexp, function) used to # convert the raw value data to a displayable string. self.get_value_text_fns = [] self.get_key_text = lambda x: x # We can filter the list of keys displayed using a regex self.filter_regex = "" # Create and configure user interface widgets. A string argument is # used to set the window title. def build_ui(self, title): win = GtkWindow() win.set_title(title) win.connect("destroy", mainquit) hpaned = GtkHPaned() win.add(hpaned) hpaned.set_border_width(5) hpaned.show() vbox = GtkVBox() hpaned.add1(vbox) vbox.show() scrolled_win = GtkScrolledWindow() scrolled_win.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC) vbox.pack_start(scrolled_win) scrolled_win.show() hbox = GtkHBox() vbox.pack_end(hbox, expand = 0, padding = 5) hbox.show() label = GtkLabel("Filter:") hbox.pack_start(label, expand = 0, padding = 5) label.show() self.entry = GtkEntry() hbox.pack_end(self.entry, padding = 5) self.entry.show() self.entry.connect("activate", self.filter_activated) self.list = GtkList() self.list.set_selection_mode(SELECTION_MULTIPLE) self.list.set_selection_mode(SELECTION_BROWSE) scrolled_win.add_with_viewport(self.list) self.list.show() self.list.connect("select_child", self.key_selected) scrolled_win = GtkScrolledWindow() scrolled_win.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC) hpaned.add2(scrolled_win) scrolled_win.set_usize(500,400) scrolled_win.show() self.text = GtkText() self.text.set_editable(FALSE) scrolled_win.add_with_viewport(self.text) self.text.show() self.text.connect("event", self.event_handler) self.menu = GtkMenu() self.menu.show() self.font = load_font("fixed") self.update_keylist() win.show() # Add a key to the left hand side of the user interface def add_key(self, key): display_key = self.get_key_text(key) list_item = GtkListItem(display_key) list_item.set_data("raw_key", key) # Store raw key in item data self.list.add(list_item) list_item.show() # Event handler registered by build_ui() def event_handler(self, event, menu): return FALSE # Set the text to appear in the right hand side of the user interface def set_value_text(self, item): # Clear old old value in text window self.text.delete_text(0, self.text.get_length()) if type(item) == str: # The text widget has trouble inserting text containing NULL # characters. item = string.replace(item, "\x00", ".") self.text.insert(self.font, None, None, item) else: # A non-text item self.text.insert(self.font, None, None, repr(item)) # This function is called when a key is selected in the left hand side # of the user interface. def key_selected(self, list, list_item): key = list_item.children()[0].get() # Look for a match in the value display function list text = self.dict[list_item.get_data("raw_key")] for entry in self.get_value_text_fns: if re.match(entry[0], key): text = entry[1](text) break self.set_value_text(text) # Refresh the key list by removing all items and re-inserting them. # Items are only inserted if they pass through the filter regexp. def update_keylist(self): self.list.remove_items(self.list.children()) self.set_value_text("") for k in self.dict.keys(): if re.match(self.filter_regex, k): self.add_key(k) # Invoked when the user hits return in the filter text entry widget. def filter_activated(self, entry): self.filter_regex = entry.get_text() self.update_keylist() # Register a key display function def register_get_key_text_fn(self, fn): self.get_key_text = fn # Register a value display function def register_get_value_text_fn(self, regexp, fn): self.get_value_text_fns.append((regexp, fn)) # # A utility function to convert a string to the standard hex + ascii format. # To display all values in hex do: # register_get_value_text_fn("", gtkdictbrowser.hex_string) # def hex_string(data): """Return a hex dump of a string as a string. The output produced is in the standard 16 characters per line hex + ascii format: 00000000: 40 00 00 00 00 00 00 00 40 00 00 00 01 00 04 80 @....... @....... 00000010: 01 01 00 00 00 00 00 01 00 00 00 00 ........ .... """ pos = 0 # Position in data line = 0 # Line of data hex = "" # Hex display ascii = "" # ASCII display result = "" while pos < len(data): # Start with header if pos % 16 == 0: hex = "%08x: " % (line * 16) ascii = "" # Add character hex = hex + "%02x " % (ord(data[pos])) if ord(data[pos]) < 32 or ord(data[pos]) > 176: ascii = ascii + '.' else: ascii = ascii + data[pos] pos = pos + 1 # Add separator if half way if pos % 16 == 8: hex = hex + " " ascii = ascii + " " # End of line if pos % 16 == 0: result = result + "%s %s\n" % (hex, ascii) line = line + 1 # Leftover bits if pos % 16 != 0: # Pad hex string for i in range(0, (16 - (pos % 16))): hex = hex + " " # Half way separator if (pos % 16) < 8: hex = hex + " " result = result + "%s %s\n" % (hex, ascii) return result # For testing purposes, create a fixed dictionary to browse with if __name__ == "__main__": dict = {"chicken": "ham", "spam": "fun", "subdict": {"a": "b", "c": "d"}} db = GtkDictBrowser(dict) db.build_ui("GtkDictBrowser") # Override Python's handling of ctrl-c so we can break out of the # gui from the command line. import signal signal.signal(signal.SIGINT, signal.SIG_DFL) mainloop()