3 # Browse a Python dictionary in a two pane graphical interface written
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
11 # Possible applications:
13 # - Windows registry browser
14 # - SPOOLSS printerdata browser
23 def __init__(self
, dict):
26 # This variable stores a list of (regexp, function) used to
27 # convert the raw value data to a displayable string.
29 self
.get_value_text_fns
= []
30 self
.get_key_text
= lambda x
: x
32 # We can filter the list of keys displayed using a regex
34 self
.filter_regex
= ""
36 # Create and configure user interface widgets. A string argument is
37 # used to set the window title.
39 def build_ui(self
, title
):
43 win
.connect("destroy", mainquit
)
47 hpaned
.set_border_width(5)
54 scrolled_win
= GtkScrolledWindow()
55 scrolled_win
.set_policy(POLICY_AUTOMATIC
, POLICY_AUTOMATIC
)
56 vbox
.pack_start(scrolled_win
)
60 vbox
.pack_end(hbox
, expand
= 0, padding
= 5)
63 label
= GtkLabel("Filter:")
64 hbox
.pack_start(label
, expand
= 0, padding
= 5)
67 self
.entry
= GtkEntry()
68 hbox
.pack_end(self
.entry
, padding
= 5)
71 self
.entry
.connect("activate", self
.filter_activated
)
74 self
.list.set_selection_mode(SELECTION_MULTIPLE
)
75 self
.list.set_selection_mode(SELECTION_BROWSE
)
76 scrolled_win
.add_with_viewport(self
.list)
79 self
.list.connect("select_child", self
.key_selected
)
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)
88 self
.text
.set_editable(FALSE
)
89 scrolled_win
.add_with_viewport(self
.text
)
92 self
.text
.connect("event", self
.event_handler
)
97 self
.font
= load_font("fixed")
103 # Add a key to the left hand side of the user interface
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
)
112 # Event handler registered by build_ui()
114 def event_handler(self
, event
, menu
):
117 # Set the text to appear in the right hand side of the user interface
119 def set_value_text(self
, item
):
121 # Clear old old value in text window
123 self
.text
.delete_text(0, self
.text
.get_length())
125 if type(item
) == str:
127 # The text widget has trouble inserting text containing NULL
130 item
= string
.replace(item
, "\x00", ".")
132 self
.text
.insert(self
.font
, None, None, item
)
138 self
.text
.insert(self
.font
, None, None, repr(item
))
140 # This function is called when a key is selected in the left hand side
141 # of the user interface.
143 def key_selected(self
, list, list_item
):
144 key
= list_item
.children()[0].get()
146 # Look for a match in the value display function list
148 text
= self
.dict[list_item
.get_data("raw_key")]
150 for entry
in self
.get_value_text_fns
:
151 if re
.match(entry
[0], key
):
152 text
= entry
[1](text
)
155 self
.set_value_text(text
)
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.
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
):
167 # Invoked when the user hits return in the filter text entry widget.
169 def filter_activated(self
, entry
):
170 self
.filter_regex
= entry
.get_text()
171 self
.update_keylist()
173 # Register a key display function
175 def register_get_key_text_fn(self
, fn
):
176 self
.get_key_text
= fn
178 # Register a value display function
180 def register_get_value_text_fn(self
, regexp
, fn
):
181 self
.get_value_text_fns
.append((regexp
, fn
))
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)
189 def hex_string(data
):
190 """Return a hex dump of a string as a string.
192 The output produced is in the standard 16 characters per line hex +
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 ........ ....
199 pos
= 0 # Position in data
200 line
= 0 # Line of data
202 hex = "" # Hex display
203 ascii
= "" # ASCII display
207 while pos
< len(data
):
212 hex = "%08x: " % (line
* 16)
217 hex = hex + "%02x " % (ord(data
[pos
]))
219 if ord(data
[pos
]) < 32 or ord(data
[pos
]) > 176:
222 ascii
= ascii
+ data
[pos
]
226 # Add separator if half way
235 result
= result
+ "%s %s\n" % (hex, ascii
)
244 for i
in range(0, (16 - (pos
% 16))):
252 result
= result
+ "%s %s\n" % (hex, ascii
)
256 # For testing purposes, create a fixed dictionary to browse with
258 if __name__
== "__main__":
260 dict = {"chicken": "ham", "spam": "fun", "subdict": {"a": "b", "c": "d"}}
262 db
= GtkDictBrowser(dict)
264 db
.build_ui("GtkDictBrowser")
266 # Override Python's handling of ctrl-c so we can break out of the
267 # gui from the command line.
270 signal
.signal(signal
.SIGINT
, signal
.SIG_DFL
)