hachoir / 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 = 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, filename):
        self.xml = gtk.glade.XML(UI_FILENAME, "main_window")
        self.xml.signal_autoconnect(self)
        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_quit_activate(self, widget):
        self.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)
        cell = gtk.CellRendererText()
        col.pack_start(cell, True)
        col.add_attribute(cell, 'text', num)
        treeview.set_search_column(num)
        return num+1

    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)

        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
            else:
                value = field.description
            newparent = treeStore.append(treeparent,(field.name, value, None, ROW_NORMAL))
            if field.is_field_set:
                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 ):
        print "usage: %s file" %sys.argv[0]
        return
    filename = sys.argv[1]

    filename, realname = unicodeFilename(filename), filename
    topStream = FileInputStream(filename, realname)

    # Create window and gtk event loop
    interface = Interface(filename)
    interface.display_tree(topStream)
    gtk.main()

if __name__ == "__main__":
    main()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.