1#!/usr/bin/python 2# 3# Browse a Python dictionary in a two pane graphical interface written 4# in GTK. 5# 6# The GtkDictBrowser class is supposed to be generic enough to allow 7# applications to override enough methods and produce a 8# domain-specific browser provided the information is presented as a 9# Python dictionary. 10# 11# Possible applications: 12# 13# - Windows registry browser 14# - SPOOLSS printerdata browser 15# - tdb file browser 16# 17 18from gtk import * 19import string, re 20 21class GtkDictBrowser: 22 23 def __init__(self, dict): 24 self.dict = dict 25 26 # This variable stores a list of (regexp, function) used to 27 # convert the raw value data to a displayable string. 28 29 self.get_value_text_fns = [] 30 self.get_key_text = lambda x: x 31 32 # We can filter the list of keys displayed using a regex 33 34 self.filter_regex = "" 35 36 # Create and configure user interface widgets. A string argument is 37 # used to set the window title. 38 39 def build_ui(self, title): 40 win = GtkWindow() 41 win.set_title(title) 42 43 win.connect("destroy", mainquit) 44 45 hpaned = GtkHPaned() 46 win.add(hpaned) 47 hpaned.set_border_width(5) 48 hpaned.show() 49 50 vbox = GtkVBox() 51 hpaned.add1(vbox) 52 vbox.show() 53 54 scrolled_win = GtkScrolledWindow() 55 scrolled_win.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC) 56 vbox.pack_start(scrolled_win) 57 scrolled_win.show() 58 59 hbox = GtkHBox() 60 vbox.pack_end(hbox, expand = 0, padding = 5) 61 hbox.show() 62 63 label = GtkLabel("Filter:") 64 hbox.pack_start(label, expand = 0, padding = 5) 65 label.show() 66 67 self.entry = GtkEntry() 68 hbox.pack_end(self.entry, padding = 5) 69 self.entry.show() 70 71 self.entry.connect("activate", self.filter_activated) 72 73 self.list = GtkList() 74 self.list.set_selection_mode(SELECTION_MULTIPLE) 75 self.list.set_selection_mode(SELECTION_BROWSE) 76 scrolled_win.add_with_viewport(self.list) 77 self.list.show() 78 79 self.list.connect("select_child", self.key_selected) 80 81 scrolled_win = GtkScrolledWindow() 82 scrolled_win.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC) 83 hpaned.add2(scrolled_win) 84 scrolled_win.set_usize(500,400) 85 scrolled_win.show() 86 87 self.text = GtkText() 88 self.text.set_editable(FALSE) 89 scrolled_win.add_with_viewport(self.text) 90 self.text.show() 91 92 self.text.connect("event", self.event_handler) 93 94 self.menu = GtkMenu() 95 self.menu.show() 96 97 self.font = load_font("fixed") 98 99 self.update_keylist() 100 101 win.show() 102 103 # Add a key to the left hand side of the user interface 104 105 def add_key(self, key): 106 display_key = self.get_key_text(key) 107 list_item = GtkListItem(display_key) 108 list_item.set_data("raw_key", key) # Store raw key in item data 109 self.list.add(list_item) 110 list_item.show() 111 112 # Event handler registered by build_ui() 113 114 def event_handler(self, event, menu): 115 return FALSE 116 117 # Set the text to appear in the right hand side of the user interface 118 119 def set_value_text(self, item): 120 121 # Clear old old value in text window 122 123 self.text.delete_text(0, self.text.get_length()) 124 125 if type(item) == str: 126 127 # The text widget has trouble inserting text containing NULL 128 # characters. 129 130 item = string.replace(item, "\x00", ".") 131 132 self.text.insert(self.font, None, None, item) 133 134 else: 135 136 # A non-text item 137 138 self.text.insert(self.font, None, None, repr(item)) 139 140 # This function is called when a key is selected in the left hand side 141 # of the user interface. 142 143 def key_selected(self, list, list_item): 144 key = list_item.children()[0].get() 145 146 # Look for a match in the value display function list 147 148 text = self.dict[list_item.get_data("raw_key")] 149 150 for entry in self.get_value_text_fns: 151 if re.match(entry[0], key): 152 text = entry[1](text) 153 break 154 155 self.set_value_text(text) 156 157 # Refresh the key list by removing all items and re-inserting them. 158 # Items are only inserted if they pass through the filter regexp. 159 160 def update_keylist(self): 161 self.list.remove_items(self.list.children()) 162 self.set_value_text("") 163 for k in self.dict.keys(): 164 if re.match(self.filter_regex, k): 165 self.add_key(k) 166 167 # Invoked when the user hits return in the filter text entry widget. 168 169 def filter_activated(self, entry): 170 self.filter_regex = entry.get_text() 171 self.update_keylist() 172 173 # Register a key display function 174 175 def register_get_key_text_fn(self, fn): 176 self.get_key_text = fn 177 178 # Register a value display function 179 180 def register_get_value_text_fn(self, regexp, fn): 181 self.get_value_text_fns.append((regexp, fn)) 182 183# 184# A utility function to convert a string to the standard hex + ascii format. 185# To display all values in hex do: 186# register_get_value_text_fn("", gtkdictbrowser.hex_string) 187# 188 189def hex_string(data): 190 """Return a hex dump of a string as a string. 191 192 The output produced is in the standard 16 characters per line hex + 193 ascii format: 194 195 00000000: 40 00 00 00 00 00 00 00 40 00 00 00 01 00 04 80 @....... @....... 196 00000010: 01 01 00 00 00 00 00 01 00 00 00 00 ........ .... 197 """ 198 199 pos = 0 # Position in data 200 line = 0 # Line of data 201 202 hex = "" # Hex display 203 ascii = "" # ASCII display 204 205 result = "" 206 207 while pos < len(data): 208 209 # Start with header 210 211 if pos % 16 == 0: 212 hex = "%08x: " % (line * 16) 213 ascii = "" 214 215 # Add character 216 217 hex = hex + "%02x " % (ord(data[pos])) 218 219 if ord(data[pos]) < 32 or ord(data[pos]) > 176: 220 ascii = ascii + '.' 221 else: 222 ascii = ascii + data[pos] 223 224 pos = pos + 1 225 226 # Add separator if half way 227 228 if pos % 16 == 8: 229 hex = hex + " " 230 ascii = ascii + " " 231 232 # End of line 233 234 if pos % 16 == 0: 235 result = result + "%s %s\n" % (hex, ascii) 236 line = line + 1 237 238 # Leftover bits 239 240 if pos % 16 != 0: 241 242 # Pad hex string 243 244 for i in range(0, (16 - (pos % 16))): 245 hex = hex + " " 246 247 # Half way separator 248 249 if (pos % 16) < 8: 250 hex = hex + " " 251 252 result = result + "%s %s\n" % (hex, ascii) 253 254 return result 255 256# For testing purposes, create a fixed dictionary to browse with 257 258if __name__ == "__main__": 259 260 dict = {"chicken": "ham", "spam": "fun", "subdict": {"a": "b", "c": "d"}} 261 262 db = GtkDictBrowser(dict) 263 264 db.build_ui("GtkDictBrowser") 265 266 # Override Python's handling of ctrl-c so we can break out of the 267 # gui from the command line. 268 269 import signal 270 signal.signal(signal.SIGINT, signal.SIG_DFL) 271 272 mainloop() 273