2002-08-30 01:35:56 +00:00
#!/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
2002-09-18 08:16:22 +00:00
def set_value_text ( self , item ) :
2002-08-30 01:35:56 +00:00
2002-09-18 08:16:22 +00:00
# Clear old old value in text window
2002-08-30 01:35:56 +00:00
2002-09-18 08:16:22 +00:00
self . text . delete_text ( 0 , self . text . get_length ( ) )
if type ( item ) == str :
2002-08-30 01:35:56 +00:00
2002-09-18 08:16:22 +00:00
# The text widget has trouble inserting text containing NULL
# characters.
item = string . replace ( item , " \x00 " , " . " )
self . text . insert ( self . font , None , None , item )
2002-08-30 01:35:56 +00:00
2002-09-18 08:16:22 +00:00
else :
# A non-text item
self . text . insert ( self . font , None , None , repr ( item ) )
2002-08-30 01:35:56 +00:00
# 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 ) )
2002-09-03 01:55:21 +00:00
#
# 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
2002-08-30 01:35:56 +00:00
# For testing purposes, create a fixed dictionary to browse with
if __name__ == " __main__ " :
2002-09-18 08:16:22 +00:00
dict = { " chicken " : " ham " , " spam " : " fun " , " subdict " : { " a " : " b " , " c " : " d " } }
2002-08-30 01:35:56 +00:00
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 ( )