Commits

nneonneo  committed d19ef48 Merge

Merged in oliver_g/hachoir/hachoir-gtk (pull request #5)

improve hachoir-gtk frontend

  • Participants
  • Parent commits a848210, c5fa998

Comments (0)

Files changed (2)

File hachoir-gtk/hachoir-gtk

 #!/usr/bin/env python
 import pygtk
 import sys
+import os
 pygtk.require ('2.0') # 2.2 for Clipboard
 import gtk.glade
 from hachoir_core.cmd_line import unicodeFilename
 from hachoir_parser import createParser
+from hachoir_parser import guessParser, HachoirParserList
+from hachoir_core.field import SubFile
+from hachoir_core.stream import FileInputStream
 
-UI_FILENAME = "hachoir.glade"
+UI_FILENAME = os.path.dirname( __file__ ) + "/hachoir.glade"
+
+ROW_NORMAL = 1
+ROW_SUBTREE_DUMMY = 2
+ROW_SUBFILE = 3
+ROW_SUBFILE_DUMMY = 4
+ROW_SHOW_MORE = 5
+
 
 class Interface:
-    def __init__(self, fieldset):
+    def __init__(self, filename):
         self.xml = gtk.glade.XML(UI_FILENAME, "main_window")
         self.xml.signal_autoconnect(self)
-        self.fieldset = fieldset
         self.table = self.xml.get_widget("table")
 
+        self.table.connect("row-expanded", self.on_row_expanded)
+        self.table.connect("button-press-event", self.on_button_press)
+        self.table.connect("cursor-changed", self.on_cursor_changed)
+
+        self.xml.get_widget("main_window").set_title("Hachoir - %s" % filename)
+
+        self.table.set_search_equal_func(resultSearchCb)
+
+        self.selectedParsers = {}
+
+        self.parserDb = HachoirParserList.getInstance()
+        self.categories = {}
+        for p in self.parserDb:
+            tags = p.getParserTags()
+            if tags.has_key('category'):
+                cat = tags['category']
+            else:
+                cat = 'misc'
+            if not self.categories.has_key(cat):
+                self.categories[cat] = []
+            self.categories[cat].append(tags)
+
+
     def quit(self):
         gtk.main_quit()
 
     def on_window_destroy(self, widget, data=None):
         self.quit()
 
+    def on_row_expanded (self, treeview, it, path):
+        model = treeview.get_model()
+        childIt = model.iter_children(it)
+        if childIt and model.get_value(childIt, 0) is None:
+            rowType = model.get_value(childIt, 3)
+            if rowType == ROW_SUBTREE_DUMMY:
+                # normal subtree
+                field = model.get_value(childIt, 2)
+                self.fill_tree(field, it, model)
+                model.remove(childIt)
+            else:
+                # SubFile
+                assert(rowType == ROW_SUBFILE_DUMMY)
+                stream = model.get_value(childIt, 2)
+                selectedParser = None
+                if self.selectedParsers.has_key(path):
+                    selectedParser = self.selectedParsers[path]
+
+                    parser = None
+                    for p in self.parserDb:
+                        tags = p.getParserTags()
+                        if tags['id'] == selectedParser:
+                            parser = p(stream)
+                else:
+                    parser = guessParser(stream)
+
+                if not parser:
+                    print "no parser found for %s" % stream.source
+                    model[childIt] = ("Subfile", "(unrecognized)", stream, ROW_SUBFILE_DUMMY)
+                else:
+                    self.fill_tree(parser, it, model)
+                    model[childIt] = ("Subfile", parser.description, stream, ROW_SUBFILE_DUMMY)
+
+    def on_button_press (self, treeview, event):
+        if event.button == 3:
+            pathTuple = treeview.get_path_at_pos(int(event.x), int(event.y))
+            if pathTuple is not None:
+                path = pathTuple[0]
+                model = treeview.get_model()
+
+                # show parser selection menu only for Subfile entries:
+                childIter = model.iter_children(model.get_iter(path))
+                if childIter and treeview.get_model()[childIter][3] == ROW_SUBFILE_DUMMY:
+                    menu = gtk.Menu()
+
+                    selectedParser = None
+                    if self.selectedParsers.has_key(path):
+                        selectedParser = self.selectedParsers[path]
+
+                    treeview.grab_focus()
+                    treeview.set_cursor(path, pathTuple[1], False)
+
+                    mi = gtk.RadioMenuItem(None, "Auto", use_underline=False)
+                    if selectedParser is None:
+                        mi.set_active(True)
+                    mi.connect("activate", self.on_select_parser, model, path, None)
+                    menu.append(mi)
+                    firstMi = mi
+
+                    for cat in sorted(self.categories.keys()):
+                        submenu = gtk.Menu()
+                        selectedSubmenu = False
+                        for tags in self.categories[cat]:
+                            mi = gtk.RadioMenuItem(firstMi, tags['id'] + ": " + tags['description'], use_underline=False)
+                            if selectedParser == tags['id']:
+                                mi.set_active(True)
+                                selectedSubmenu = True
+                            mi.connect("activate", self.on_select_parser, model, path, tags['id'])
+                            submenu.append(mi)
+
+                        itemName = cat
+                        if selectedSubmenu:
+                            itemName += " (*)"
+                        mi = gtk.MenuItem(itemName, use_underline=False)
+                        mi.set_submenu(submenu)
+                        menu.append(mi)
+
+                    menu.show_all()
+                    menu.popup(None, None, None, event.button, event.time)
+
+    def on_select_parser (self, menuitem, model, path, name):
+        if menuitem.get_active():
+            if name is None:
+                del(self.selectedParsers[path])
+            else:
+                self.selectedParsers[path] = name
+
+            # remove any existing parsed children for this menu item:
+            it = model.get_iter(path)
+            childIt = model.iter_children(it)
+            if childIt:
+                stream = model.get_value(childIt, 2)
+                while model.remove(childIt): pass
+
+                model.append(it,(None, "new_dummy", stream, ROW_SUBFILE_DUMMY))
+
+    def on_cursor_changed (self, treeview):
+        pathTuple = treeview.get_cursor()
+        path = pathTuple[0]
+        rowType = treeview.get_model()[path][3]
+
+        if rowType == ROW_SHOW_MORE:
+            (parentParser, lastIndex) = treeview.get_model()[path][2]
+            model = treeview.get_model()
+            thisIt = model.get_iter(path)
+            parentIt = model.iter_parent(thisIt)
+            self.fill_tree(parentParser, parentIt, model, lastIndex)
+
+            model.remove(thisIt)
+            treeview.set_cursor(path)
+
     def treeview_add_column(self, treeview, name, num):
         col = gtk.TreeViewColumn(name)
         treeview.append_column(col)
         treeview.set_search_column(num)
         return num+1
 
-    def display_tree(self, parent):
-        treeStore = gtk.TreeStore(str, str)
+    def display_tree(self, stream):
+        treeStore = gtk.TreeStore(str, str, object, int)
+
+        topItem = treeStore.append(None, ("File", stream.source, stream, ROW_SUBFILE))
+        treeStore.append(topItem, (None, "dummy-top", stream, ROW_SUBFILE_DUMMY))
+
         self.table.set_model(treeStore)
         self.treeview_add_column(self.table, "name", 0)
         self.treeview_add_column(self.table, "value", 1)
-        self.fill_tree(parent,None,treeStore)
 
-    def fill_tree(self, parent, treeparent, treeStore):
+        topPath = treeStore.get_path(topItem)
+        self.table.expand_row(topPath, False)
+
+    def fill_tree(self, parent, treeparent, treeStore, start=0):
+        index = 0
+        fieldsAdded = 0
         for field in parent:
+            if index < start:
+                index+=1
+                continue
+
+            if fieldsAdded > 100:
+                treeStore.append(treeparent,(None, "(show more)", (parent, index), ROW_SHOW_MORE))
+                break
+
             if field.hasValue():
-                #value = field.display
-                value = field.raw_display
+                value = field.display
+                #value = field.raw_display
             else:
-                value = ""
-            newparent = treeStore.append(treeparent,(field.name,value))
+                value = field.description
+            newparent = treeStore.append(treeparent,(field.name, value, None, ROW_NORMAL))
             if field.is_field_set:
-                self.fill_tree(field, newparent, treeStore)
+                treeStore.append(newparent,(None, "dummy_tree", field, ROW_SUBTREE_DUMMY))
+            elif type(field) == SubFile:
+                stream = field.getSubIStream()
+                treeStore[newparent][3] = ROW_SUBFILE
+                treeStore[newparent][2] = stream
+                treeStore.append(newparent,(None, "dummy_subfile", stream, ROW_SUBFILE_DUMMY))
+
+            index+=1
+            fieldsAdded+=1
+
+def resultSearchCb (model, column, key, it):
+    """Callback function for searching in treeview"""
+    plainText = model.get_value(it, column)
+
+    # if search text contains only lower-case characters, do case-insensitive matching:
+    if key.islower():
+        plainText = plainText.lower()
+
+    # if the line contains the search text, it matches:
+    if plainText.find(key) >= 0:
+        return False
+
+    # line doesn't match:
+    return True
+
+
+#import cProfile
+# import profile
 
 def main():
     if(len(sys.argv)!=2 ):
         return
     filename = sys.argv[1]
 
-    # Create parser
     filename, realname = unicodeFilename(filename), filename
-    fieldset = createParser(filename, realname)
-
-    # test if there is a least one tree
-    if  fieldset is  None :
-        print "No fields to display in this file. This is probably a text file."
-        return
+    topStream = FileInputStream(filename, realname)
 
     # Create window and gtk event loop
-    interface = Interface(fieldset)
-    interface.display_tree(fieldset)
+    interface = Interface(filename)
+    interface.display_tree(topStream)
     gtk.main()
 
 if __name__ == "__main__":

File hachoir-gtk/hachoir.glade

 <glade-interface>
 
 <widget class="GtkWindow" id="main_window">
-  <property name="width_request">450</property>
-  <property name="height_request">400</property>
+  <property name="width_request">550</property>
+  <property name="height_request">650</property>
   <property name="visible">True</property>
   <property name="title" translatable="yes">Hachoir</property>
   <property name="type">GTK_WINDOW_TOPLEVEL</property>
               <property name="headers_visible">True</property>
               <property name="rules_hint">False</property>
               <property name="reorderable">False</property>
-              <property name="enable_search">False</property>
+              <property name="enable_search">True</property>
               <property name="fixed_height_mode">False</property>
               <property name="hover_selection">False</property>
               <property name="hover_expand">False</property>