import gtk import cairo import sys import os import unicodedata from fringwalker import * from fringrenderer import FringRenderer from fringutil import * from fringdata import SumListCollection CURSOR_NONE = 0 CURSOR_BUSY = 1 CURSOR_LINK = 2 ui = """ """ class UI( gtk.Window ): def __init__(self, uri): self.busy_cursor = 0 self.backgroundColour = (1,1,1) self.data = None self.zoomfactor = 1.0 self.renderer = FringRenderer() self.collection = SumListCollection() self.walker = FringWalker(self.collection); self.scan_active = False self.cursor = None self.maxrings = 4 # create gui gtk.Window.__init__(self) self.set_title("fring"); self.__init_gui() # walk directory self.walker.connect("list-changed",self.__event_walker_progress) self.walker.connect("finished", self.__event_walker_finished) self.walker.connect("manually-stopped", lambda w: self.__show_busy_cursor(-1)) self.open_folder(uri) def __init_gui(self): # create menubar uimanager = gtk.UIManager() accelgroup = uimanager.get_accel_group() self.add_accel_group(accelgroup) ag_global = gtk.ActionGroup('ag_global') ag_global.add_actions([ ('FRing', None, _("_Folder")), ('View', None, _("_View")), ('Help', None, _("_Help")), ('OpenParent', gtk.STOCK_GO_UP, _("Open _Parent"), "Up", _("Open the parent folder"), self.open_parent), ('SelectFolder', gtk.STOCK_OPEN, _("_Open Folder..."), None, _("Open Folder..."), self.dialog_openfolder), ('Quit', gtk.STOCK_QUIT, _("_Quit"), None, _("Quit"), self.close), ('Zoom In', gtk.STOCK_ZOOM_IN, _("Zoom _In"), "Up", _("Zoom In"), lambda w: self.zoom(w,0.2)), ('Zoom Out', gtk.STOCK_ZOOM_OUT, _("Zoom _Out"), "Down", _("Zoom Out"), lambda w: self.zoom(w,-0.2)), ('AddRing', gtk.STOCK_ADD, _("_Add Ring"), "plus", _("Add Ring"), lambda w: self.zoom_rings(1)), ('RemRing', gtk.STOCK_REMOVE, _("_Remove Ring"), "minus", _("Remove Ring"), lambda w: self.zoom_rings(-1)), ('Save Image...', gtk.STOCK_SAVE_AS, _("_Save Image..."), None, _("Save Image..."), self.save_image), ('Refresh', gtk.STOCK_REFRESH, _("_Refresh Tree"), "R", _("Refresh Tree"), self.refresh_tree), ('About', gtk.STOCK_ABOUT, _("_About"), None, "About", self.dialog_about), ]) action = gtk.ToggleAction("Show Hidden Files", _("Show Hidden Files"), None, None) action.set_active(True) action.connect("toggled",self.__event_togglehidden) ag_global.add_action(action) uimanager.insert_action_group(ag_global, 0) uimanager.add_ui_from_string(ui) menubar = uimanager.get_widget('/MenuBar') menubar.show() # create toolbar toolbar = gtk.HBox() b = gtk.Button() img = gtk.image_new_from_stock(gtk.STOCK_GO_UP, gtk.ICON_SIZE_BUTTON) b.set_property("image", img) uimanager.get_action("/MenuBar/FRing/OpenParent").connect_proxy(b) toolbar.pack_start( b, False ) b.connect("enter-notify-event",self.__event_parentbutton_enter) b.connect("leave-notify-event",self.__event_parentbutton_leave) b = gtk.FileChooserButton('Select a Folder') b.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) b.set_local_only(False) h = b.connect("current-folder-changed", lambda w: self.__set_uri(w.get_uri())) toolbar.pack_start( b, True ) self.filechooserbutton = b self.filechooserbutton.handler_folderchanged = h # create canvas self.eventbox = gtk.EventBox() self.image = gtk.Image() self.eventbox.add(self.image) self.eventbox.add_events( gtk.gdk.EXPOSURE_MASK | gtk.gdk.LEAVE_NOTIFY_MASK | gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK ) self.eventbox.connect("scroll_event", self.__event_scroll) self.eventbox.connect("button-press-event",self.__event_click) self.eventbox.connect("motion-notify-event",self.__event_move) # Create label self.label = gtk.Label() self.label.set_alignment(0, .5) self.label.set_padding(6, 0) self.label.set_text("") # aligh menubar, toolbar and canvas vertically vbox = gtk.VBox(); vbox.pack_start(menubar,False,False) vbox.pack_start(toolbar,False,False) f = gtk.Frame() f.set_shadow_type(gtk.SHADOW_IN) f.add(self.eventbox) vbox.pack_start(f) vbox.pack_start(self.label,False,False,6) self.add(vbox) # show everything # Note: With set_default_size, the image can't be downsized. self.set_size_request(100,100) # minimum size, needed! self.resize(500,350) self.show_all() self.__create_canvas() self.eventbox.connect("size-allocate",self.__event_resized) self.connect("delete_event", self.close) def dialog_about(self,widget): d = gtk.AboutDialog() d.set_name("fring"); d.set_license(LICENSE); c = unicodedata.lookup('COPYRIGHT SIGN') e = unicodedata.lookup('LATIN SMALL LETTER E WITH ACUTE') d.set_copyright(u"Copyright "+c+" 2006 Lennart Poettering, Fr"+e+"d"+e+"ric Back") d.set_comments(_("Visual disk usage analyser")) d.run() d.destroy() def dialog_openfolder(self,widget): """ Open a dialog to select a folder """ d = gtk.FileChooserDialog(None, self, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, (gtk.STOCK_CANCEL,0,gtk.STOCK_OK,1)) if d.run() == 1: self.open_folder(d.get_uri()) d.destroy() def open_folder(self,uri): # change folder by changing the chooser button self.filechooserbutton.set_current_folder_uri(uri) def close(self,w=None,e=None): self.walker.stop() self.destroy() gtk.main_quit() def open_parent(self,widget): self.open_folder(get_parent_from_uri(self.uri)) def zoom(self,widget,factor): self.zoomfactor += factor if self.zoomfactor < 0: self.zoomfactor = 0 self.redraw() def zoom_rings(self,mod): r = self.maxrings r += mod if r < 2: r = 2 if r != self.maxrings: self.maxrings = r #print "rings",self.maxrings self.redraw() def redraw(self,widget=None): # clear the canvas self.__show_busy_cursor(1) self.ctx.rectangle(0,0,self.width,self.height) self.ctx.set_source_rgb(*self.backgroundColour) self.ctx.fill() # draw segments if not self.data: return self.renderer.WIDTH = self.width self.renderer.HEIGHT = self.height self.renderer.INNER_RADIUS = self.height/12 self.renderer.RING_RADIUS = self.height/10 self.renderer.RING_RADIUS *= self.zoomfactor self.renderer.RINGS_MAX = self.maxrings self.renderer.draw_segment(self.ctx, 0, 0, 1, 0, 1, self.data, self.uri) self.image.queue_draw() self.__show_busy_cursor(-1) def save_image(self, widget=None): d = gtk.FileChooserDialog("Save As...", self.get_toplevel(), gtk.FILE_CHOOSER_ACTION_SAVE, (gtk.STOCK_CANCEL,0,gtk.STOCK_OK,1), None) d.set_current_name( self.data.name.replace(" ","_") + ".png" ) r = d.run() if r == 1: #print d.get_uri() self.__show_busy_cursor(1) pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,True,8,self.width,self.height) pixmap, mask = self.image.get_pixmap() pixbuf.get_from_drawable(pixmap,pixmap.get_colormap(),0,0,0,0,-1,-1) #pixbuf.save(d.get_filename(), "jpeg", {"quality":"100"}) pixbuf.save(d.get_filename(), "png", {}) self.__show_busy_cursor(-1) d.destroy() def refresh_tree(self,widget): """ Refresh the current folder """ self.walker.stop() self.collection.clear() self.walker.walk(self.uri) self.scan_active = True self.__show_busy_cursor(1) #----------------------------------------------------------- private methods def __set_uri(self,uri): print "requested:",uri, d = self.collection.get_sumlist(uri) if d is not None: print "(load from collection)" self.data = self.collection.get_sumlist(uri) self.uri = uri self.redraw() else: print "(walk)" self.uri = uri self.walker.stop() if uri: self.walker.walk(uri) self.scan_active = True self.__show_busy_cursor(1) def __create_canvas(self): """ Create a new pixmap and a new cairo context """ r = self.eventbox.get_allocation() self.width, self.height = r.width, r.height self.pixmap = gtk.gdk.Pixmap(self.window,r.width,r.height) self.image.set_from_pixmap(self.pixmap,None) self.ctx = self.pixmap.cairo_create() self.renderer.prepare_layouts(self.ctx) self.ctx.rectangle(0,0,self.width,self.height) self.ctx.set_source_rgb(*self.backgroundColour) self.ctx.fill() def __show_busy_cursor(self, value=0): self.busy_cursor += value if self.busy_cursor > 0: self.__set_cursor(CURSOR_BUSY) else: self.__set_cursor(CURSOR_NONE) def __set_cursor(self, cursor): if self.window is None: return if self.cursor == cursor: return if cursor == CURSOR_NONE: self.window.set_cursor(None) elif cursor == CURSOR_BUSY: self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) elif cursor == CURSOR_LINK: self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND1)) #--------------------------------------------------- gobject event callbacks def __event_togglehidden(self,widget): if self.walker.showhidden == widget.get_active(): return self.walker.showhidden = widget.get_active() self.refresh_tree() def __event_move(self, widget, event): f = self.renderer.get_hotspot_at(event.x, event.y) self.label.set_text("") if f: markup = "%s, %s, %0.1f%%" % (format_uri(f.path), pretty_size(f.sumlist.size), f.value*100) self.label.set_text(markup) if f.sumlist.has_children(): self.__set_cursor(CURSOR_LINK) return self.__show_busy_cursor() # reset busy cursor def __event_click(self, widget, event): f = self.renderer.get_hotspot_at(event.x, event.y) if f and f.sumlist.children: self.open_folder(f.path) #if self.scan_active: # if we are still walking the tree, FORCE a new scan on the path # because we CANNOT guarantee that the path we point at is ready. # self.open_folder(f.path) #else: # if we are sure that the sumlist is complete, jump there directly # block filechooser callbacks while changing it # self.filechooserbutton.handler_block(self.filechooserbutton.handler_folderchanged) # self.uri = f.path # self.__event_walker_progress(None,f.sumlist) # self.filechooserbutton.set_current_folder_uri(f.path) # self.filechooserbutton.handler_unblock(self.filechooserbutton.handler_folderchanged) def __event_scroll(self, widget, event): if event.direction is gtk.gdk.SCROLL_UP: self.zoom(widget, 0.2) elif event.direction is gtk.gdk.SCROLL_DOWN: self.zoom(widget, -0.2) def __event_walker_finished(self, widget, data): self.__event_walker_progress(widget, data) self.__show_busy_cursor(-1) self.scan_active = False def __event_resized(self, widget, event): r = self.eventbox.get_allocation() if (r.width,r.height) != (self.width,self.height): self.__create_canvas() self.redraw() def __event_parentbutton_enter(self,widget,event): markup = "%s" %format_uri(get_parent_from_uri(self.uri)) self.label.set_text(markup) def __event_parentbutton_leave(self,widget,event): self.label.set_text("") def __event_walker_progress(self,widget,data): self.data = data self.redraw()