⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dirdiff.py

📁 LINUX下的文件比较工具
💻 PY
📖 第 1 页 / 共 3 页
字号:
### Copyright (C) 2002-2006 Stephen Kennedy <stevek@gnome.org>### This program is free software; you can redistribute it and/or modify### it under the terms of the GNU General Public License as published by### the Free Software Foundation; either version 2 of the License, or### (at your option) any later version.### This program is distributed in the hope that it will be useful,### but WITHOUT ANY WARRANTY; without even the implied warranty of### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the### GNU General Public License for more details.### You should have received a copy of the GNU General Public License### along with this program; if not, write to the Free Software### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USAfrom __future__ import generatorsimport pathsimport gnomegladeimport gobjectimport gtkimport gtk.keysymsimport mathimport miscimport osimport shutilimport melddocimport treeimport reimport statimport timegdk = gtk.gdk################################################################################## Local Functions#################################################################################def uniq(l):    i = iter(l)    a = i.next()    yield a    while 1:        b = i.next()        if a != b:            yield b            a = b_cache = {}def _files_same(lof, regexes):    """Return 1 if all the files in 'lof' have the same contents.       If the files are the same after the regular expression substitution, return 2.       Finally, return 0 if the files still differ.    """    # early out if only one file    if len(lof) <= 1:        return 1    # get sigs    lof = tuple(lof)    def sig(f):        s = os.stat(f)        return misc.struct(mode=stat.S_IFMT(s.st_mode), size=s.st_size, time=s.st_mtime)    def all_same(l):        for i in l[1:]:            if l[0] != i:                return 0        return 1    sigs = tuple( [ sig(f) for f in lof ] )    # check for directories    arefiles = [ stat.S_ISREG(s.mode) for s in sigs ]    if arefiles.count(0) == len(arefiles): # all dirs        return 1    elif arefiles.count(0): # mixture        return 0    # if no substitutions look for different sizes    if len(regexes) == 0 and all_same( [s.size for s in sigs] ) == 0:        return 0    # try cache    try:        cache = _cache[ lof ]    except KeyError:        pass    else:        if cache.sigs == sigs: # up to date            return cache.result    # do it    contents = [ open(f, "r").read() for f in lof ]    if all_same(contents):        result = 1    else:        for r in regexes:            contents = [ re.sub(r, "", c) for c in contents ]        result = all_same(contents) and 2    _cache[ lof ] = misc.struct(sigs=sigs, result=result)    return resultdef _not_none(l):    """Return list with Nones filtered out"""    return filter(lambda x: x!=None, l)join = os.path.joinCOL_EMBLEM = tree.COL_END + 1pixbuf_newer = gnomeglade.load_pixbuf( paths.share_dir("glade2/pixmaps/tree-file-newer.png"), 14)TYPE_PIXBUF = type(pixbuf_newer)################################################################################## DirDiffTreeStore#################################################################################class DirDiffTreeStore(tree.DiffTreeStore):    def __init__(self, ntree):        types = [type("")] * COL_EMBLEM * ntree        types[tree.COL_ICON*ntree:tree.COL_ICON*ntree+ntree] = [TYPE_PIXBUF] * ntree        types[COL_EMBLEM*ntree:COL_EMBLEM*ntree+ntree] = [TYPE_PIXBUF] * ntree        gtk.TreeStore.__init__(self, *types)        self.ntree = ntree        self._setup_default_styles()################################################################################## EmblemCellRenderer#################################################################################class EmblemCellRenderer(gtk.GenericCellRenderer):    __gproperties__ = {        'pixbuf': (gtk.gdk.Pixbuf, 'pixmap property', 'the base pixmap', gobject.PARAM_READWRITE),        'emblem': (gtk.gdk.Pixbuf, 'emblem property', 'the emblem pixmap', gobject.PARAM_READWRITE),    }    def __init__(self):        self.__gobject_init__()        self.renderer = gtk.CellRendererPixbuf()        self.pixbuf = None        self.emblem = None    def do_set_property(self, pspec, value):        if not hasattr(self, pspec.name):            raise AttributeError, 'unknown property %s' % pspec.name        setattr(self, pspec.name, value)    def do_get_property(self, pspec):        return getattr(self, pspec.name)    def on_render(self, window, widget, background_area, cell_area, expose_area, flags):        r = self.renderer        r.set_property("pixbuf", self.pixbuf)        r.render(window, widget, background_area, cell_area, expose_area, flags)        r.set_property("pixbuf", self.emblem)        r.render(window, widget, background_area, cell_area, expose_area, flags)    def on_get_size(self, widget, cell_area):        if not hasattr(self, "size"):            r = self.renderer            r.set_property("pixbuf", self.pixbuf)            self.size = r.get_size(widget, cell_area)        return self.sizeif gobject.pygtk_version < (2,8,0):    gobject.type_register(EmblemCellRenderer)################################################################################## DirDiffMenu#################################################################################class DirDiffMenu(gnomeglade.Component):    def __init__(self, app):        gladefile = paths.share_dir("glade2/dirdiff.glade")        gnomeglade.Component.__init__(self, gladefile, "popup")        self.parent = app    def popup_in_pane( self, pane ):        self.copy_left.set_sensitive( pane > 0 )        self.copy_right.set_sensitive( pane+1 < self.parent.num_panes )        self.widget.popup( None, None, None, 3, gtk.get_current_event_time() )    def on_popup_compare_activate(self, menuitem):        self.parent.launch_comparisons_on_selected()    def on_popup_copy_left_activate(self, menuitem):        self.parent.on_button_copy_left_clicked( None )    def on_popup_copy_right_activate(self, menuitem):        self.parent.on_button_copy_right_clicked( None )    def on_popup_delete_activate(self, menuitem):        self.parent.on_button_delete_clicked( None )    def on_popup_edit_activate(self, menuitem):        self.parent.on_button_edit_clicked( None )################################################################################## TypeFilter#################################################################################class TypeFilter(object):    __slots__ = ("label", "filter", "active")    def __init__(self, label, active, filter):        self.label = label        self.active = active        self.filter = filter################################################################################## DirDiff#################################################################################class DirDiff(melddoc.MeldDoc, gnomeglade.Component):    """Two or three way diff of directories"""    def __init__(self, prefs, num_panes):        melddoc.MeldDoc.__init__(self, prefs)        gnomeglade.Component.__init__(self, paths.share_dir("glade2/dirdiff.glade"), "dirdiff")        self.toolbar.set_style( self.prefs.get_toolbar_style() )        self._map_widgets_into_lists( ["treeview", "fileentry", "diffmap", "scrolledwindow", "linkmap"] )        self.popup_menu = DirDiffMenu(self)        self.set_num_panes(num_panes)        self.on_treeview_focus_out_event(None, None)        self.treeview_focussed = None        for i in range(3):            self.treeview[i].get_selection().set_mode(gtk.SELECTION_MULTIPLE)            column = gtk.TreeViewColumn()            rentext = gtk.CellRendererText()            renicon = EmblemCellRenderer()            column.pack_start(renicon, expand=0)            column.pack_start(rentext, expand=1)            column.set_attributes(renicon, pixbuf=self.model.column_index(tree.COL_ICON,i),                                           emblem=self.model.column_index(COL_EMBLEM,i))            column.set_attributes(rentext, markup=self.model.column_index(tree.COL_TEXT,i))            self.treeview[i].append_column(column)            self.scrolledwindow[i].get_vadjustment().connect("value-changed", self._sync_vscroll )            self.scrolledwindow[i].get_hadjustment().connect("value-changed", self._sync_hscroll )        self.linediffs = [[], []]        self.state_filters = [            tree.STATE_NORMAL,            tree.STATE_MODIFIED,            tree.STATE_NEW,        ]        self.create_name_filters()        self.update_regexes()    def update_regexes(self):        self.regexes = []        for r in [ misc.ListItem(i) for i in self.prefs.regexes.split("\n") ]:            if r.active:                try:                    self.regexes.append( re.compile(r.value+"(?m)") )                except re.error:                    misc.run_dialog(                        text=_("Error converting pattern '%s' to regular expression") % r.value )    def create_name_filters(self):        self.name_filters_available = []        for f in [misc.ListItem(s) for s in self.prefs.filters.split("\n") ]:            bits = f.value.split()            if len(bits) > 1:                regex = "(%s)$" % ")|(".join( [misc.shell_to_regex(b)[:-1] for b in bits] )            elif len(bits):                regex = misc.shell_to_regex(bits[0])            else: # an empty pattern would match anything, skip it                continue            try:                cregex = re.compile(regex)            except re.error:                misc.run_dialog( _("Error converting pattern '%s' to regular expression") % f.value, self )            else:                func = lambda x, r=cregex : r.match(x) == None                self.name_filters_available.append( TypeFilter(f.name, f.active, func) )        self.name_filters = []        tips = gtk.Tooltips()        for i,f in enumerate(self.name_filters_available):            icon = gtk.Image()            icon.set_from_stock(gtk.STOCK_FIND, gtk.ICON_SIZE_LARGE_TOOLBAR)            toggle = gtk.ToggleToolButton()            toggle.set_property("label",f.label)            toggle.set_icon_widget(icon)            toggle.connect("toggled", lambda b,i=i : self._update_name_filter(b,i) )            toggle.set_active(f.active)            self.toolbar.insert(toggle, -1)            toggle.show_all()            toggle.set_tooltip(tips, _("Hide %s") % f.label )    def on_preference_changed(self, key, value):        if key == "toolbar_style":            self.toolbar.set_style( self.prefs.get_toolbar_style() )        elif key == "regexes":            self.update_regexes()    def _do_to_others(self, master, objects, methodname, args):        if not hasattr(self, "do_to_others_lock"):            self.do_to_others_lock = 1            try:                for o in filter(lambda x:x!=master, objects[:self.num_panes]):                    method = getattr(o,methodname)                    method(*args)            finally:                delattr(self, "do_to_others_lock")    def _sync_vscroll(self, adjustment):        adjs = map(lambda x: x.get_vadjustment(), self.scrolledwindow)        self._do_to_others( adjustment, adjs, "set_value", (adjustment.value,) )    def _sync_hscroll(self, adjustment):        adjs = map(lambda x: x.get_hadjustment(), self.scrolledwindow)        self._do_to_others( adjustment, adjs, "set_value", (adjustment.value,) )    def _get_focused_pane(self):        focus = [ t.is_focus() for t in self.treeview ]        try:            return focus.index(1)        except ValueError:            return None    def file_deleted(self, path, pane):        # is file still extant in other pane?        it = self.model.get_iter(path)        files = self.model.value_paths(it)        is_present = [ os.path.exists(f) for f in files ]        if 1 in is_present:            self._update_item_state(it)        else: # nope its gone            self.model.remove(it)        self._update_diffmaps()    def file_created(self, path, pane):        it = self.model.get_iter(path)        while it and self.model.get_path(it) != (0,):            self._update_item_state( it )            it = self.model.iter_parent(it)        self._update_diffmaps()    def on_fileentry_activate(self, entry):        locs = [ e.get_full_path(0) for e in self.fileentry[:self.num_panes] ]        self.set_locations(locs)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -