import gtk import cairo import sys import os from fringwalker import * from fringrenderer import FringRenderer from fringutil import * 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.walker = FringWalker(); self.scan_active = False self.cursor = None self.folder_memory = {} # remember some folders # 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)), ('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("Initializing") # 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); d.set_copyright("copyright 2006 Lennart Poettering, Frederic Back") 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 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 = 3 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.walker.walk(self.uri) self.scan_active = True self.__show_busy_cursor(1) #----------------------------------------------------------- private methods def __set_uri(self,uri): if uri in self.folder_memory: self.data = self.folder_memory[uri] self.uri = uri self.redraw() else: 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.__set_uri(self.uri) 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%%" % (f.path, pretty_size(f.sumlist.size), f.value*100) self.label.set_text(markup) if f.sumlist.children is not None: 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: if self.scan_active: # if we are still walking the tree, start a new scan on the path self.open_folder(f.path) else: # if we are sure that the sumlist is complete, jump there directly self.folder_memory[self.uri] = self.data # remember current data # 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" %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()