summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrederic Back <fredericback@gmail.com>2006-09-28 08:57:12 +0000
committerFrederic Back <fredericback@gmail.com>2006-09-28 08:57:12 +0000
commite2ef77bbf4b992f454c840a0e8682e70bde0aa31 (patch)
treedbebed3e97e53630d95c01cf91bd47baee9fe6ba
parentd848e98ecb23b934c84706722d67d275978efb73 (diff)
* "merged" c_walker "branch" to trunk.
git-svn-id: file:///home/lennart/svn/public/fring/trunk@41 d0d2c35f-0a1e-0410-abeb-dabff30a67ee
-rw-r--r--TODO1
-rw-r--r--src/fringlib/fringrenderer.py40
-rw-r--r--src/fringlib/fringui.py61
-rw-r--r--src/fringlib/fringwalker.py290
-rwxr-xr-xsrc/fringlib/launch.py7
5 files changed, 259 insertions, 140 deletions
diff --git a/TODO b/TODO
index af2ab5e..0c7a1f5 100644
--- a/TODO
+++ b/TODO
@@ -12,6 +12,7 @@ TODO
[x] click on folder -> open it
[x] show busy cursor while walking dir tree
[x] do not show black backrgound when app starts up
+[ ] don't reload the busy cursor when it's already set
========================= DISPLAY
[x] display total in ring center
diff --git a/src/fringlib/fringrenderer.py b/src/fringlib/fringrenderer.py
index 1f27de0..0f71f8a 100644
--- a/src/fringlib/fringrenderer.py
+++ b/src/fringlib/fringrenderer.py
@@ -1,7 +1,6 @@
import cairo
import pango
from math import *
-from fringwalker import sum_list
from fringutil import *
import os
@@ -51,7 +50,11 @@ class FringRenderer:
def draw_segment(self,ctx, ring, start_angle, end_angle, start_hue, end_hue, data, previouspath=""):
- assert isinstance(data, sum_list)
+ if data is None: return
+
+ dataname = data[0]
+ datacontents = data[1]
+ datasize = data[2]
if ring == 0:
self.lookup_data = []
@@ -63,24 +66,29 @@ class FringRenderer:
CENTERX, CENTERY = self.WIDTH/2, self.HEIGHT/2
ctx.move_to(CENTERX, CENTERY)
- n = len(data.data)
+ n = len(datacontents)
i = 0
accumulated = 0
last = start_angle
- for fn, d in data.data:
+ if datacontents is None: return
+ for fn, contents, d in datacontents:
+
+ if contents == None: hasContents = False
+ else: hasContents = True
+
start = last
- value = self._list_value(d)
+ value = d # third tuple element now *always* contains the size
accumulated += value
-
- if data.the_sum == 0: continue
- end = start_angle+(end_angle - start_angle)*1.0*accumulated/data.the_sum
+
+ if datasize == 0: continue
+ end = start_angle+(end_angle - start_angle)*1.0*accumulated/datasize
if end-start >= .01:
p = previouspath+os.sep+fn
- self.lookup_data[ring].append(Segment(isinstance(d, sum_list), p, value, start, end))
+ self.lookup_data[ring].append( Segment(hasContents , p, value, start, end) )
v = start_hue + (end_hue-start_hue)*1.0*i/n
color = self._choose_color(start_hue + (end_hue-start_hue)*1.0*i/n, ring)
@@ -98,9 +106,9 @@ class FringRenderer:
ctx.set_source_rgb(0, 0, 0)
ctx.stroke()
- if isinstance(d, sum_list) and ring+1 < self.RINGS_MAX:
+ if hasContents and ring+1 < self.RINGS_MAX:
self.draw_segment(ctx, ring+1, start, end, v,
- start_hue + (end_hue-start_hue)*1.0*(i+1)/n, d,
+ start_hue + (end_hue-start_hue)*1.0*(i+1)/n, (fn, contents, d),
previouspath+os.sep+fn)
r += self.RING_RADIUS/2
@@ -135,7 +143,7 @@ class FringRenderer:
ctx.move_to(x+xmod,y)
# draw the side link label
- if isinstance(d, sum_list):
+ if hasContents:
ctx.set_source_rgb(0,0,1)
width,height = self._draw_centered_text(ctx, fn + "/", align_x)
else:
@@ -145,7 +153,7 @@ class FringRenderer:
if align_x == 0:
width *= -1
- self.__register_side_link(isinstance(d, sum_list),p,value,end-start,x,y+(height/2),x+(width),y-(height/2))
+ self.__register_side_link(hasContents,p,value,end-start,x,y+(height/2),x+(width),y-(height/2))
# write disk usage on segments
if self.RING_RADIUS >= ctx.text_extents("55%")[3]:
@@ -161,7 +169,7 @@ class FringRenderer:
if ring == 0:
ctx.set_source_rgb(.3,.3,.3)
- i = format_disk_space(data.the_sum)
+ i = format_disk_space(datasize)
ctx.move_to(CENTERX, CENTERY)
width,height = self._draw_centered_text(ctx, i[0], .5, 1 )
ctx.move_to(CENTERX, CENTERY+height)
@@ -243,10 +251,6 @@ class FringRenderer:
(t,p,v),
(v,p,q))[int(h)]
- def _list_value(self,l):
- if isinstance(l, sum_list):
- return l.the_sum
- return l
def _choose_color(self,v, ring):
color = self.hsv2rgb(v, .61, 1-ring*0.1)
diff --git a/src/fringlib/fringui.py b/src/fringlib/fringui.py
index ca634d1..a6c606d 100644
--- a/src/fringlib/fringui.py
+++ b/src/fringlib/fringui.py
@@ -3,7 +3,7 @@ import cairo
import sys
import os
-from fringwalker import FringWalker
+from fringwalker import *
from fringrenderer import FringRenderer
from fringutil import *
@@ -37,6 +37,14 @@ ui = """
<separator />
<menuitem action="Quit"/>
</menu>
+
+ <menu action="TreeWalker">
+ <menuitem action="Python"/>
+ <menuitem action="Python gnomevfs"/>
+ <menuitem action="c++"/>
+ </menu>
+
+
<menu action="View">
<menuitem action="Show Hidden Files"/>
<separator />
@@ -56,7 +64,7 @@ ui = """
class UI( gtk.Window ):
- def __init__(self, path):
+ def __init__(self, uri):
self.busy_cursor = 0
self.backgroundColour = (1,1,1)
self.data = None
@@ -67,14 +75,15 @@ class UI( gtk.Window ):
# create gui
gtk.Window.__init__(self)
self.set_title("fring");
- self.__init_gui(path)
+ self.__init_gui()
# walk directory
self.walker.connect("list-changed",self.__list_changed)
self.walker.connect("finished", self.__walker_finished)
- self.open_folder(path)
+ self.walker.connect("manually-stopped", lambda w: self.__show_busy_cursor(-1))
+ self.open_folder(uri)
- def __init_gui(self, path):
+ def __init_gui(self):
# create menubar
uimanager = gtk.UIManager()
@@ -82,6 +91,7 @@ class UI( gtk.Window ):
self.add_accel_group(accelgroup)
ag_global = gtk.ActionGroup('ag_global')
ag_global.add_actions([
+ ('TreeWalker', None, "TreeWalker"),
('FRing', None, "_Folder"),
('View', None, "_View"),
('Help', None, "_Help"),
@@ -91,12 +101,22 @@ class UI( gtk.Window ):
('Zoom In', gtk.STOCK_ZOOM_IN, "Zoom _In", "<ctrl>Up", "Zoom In", lambda w: self.zoom(w,0.2)),
('Zoom Out', gtk.STOCK_ZOOM_OUT, "Zoom _Out", "<ctrl>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", "<ctrl>R", "Refresh Tree", lambda w: self.__set_path(self.path)),
+ ('Refresh', gtk.STOCK_REFRESH, "_Refresh Tree", "<ctrl>R", "Refresh Tree", lambda w: self.__set_uri(self.uri)),
('About', gtk.STOCK_ABOUT, "_About", None, "About", self.about_dialog),
])
action = gtk.ToggleAction("Show Hidden Files", "Show Hidden Files", None, None)
+ action.set_active(True)
action.connect("toggled",self.__hidden_files_toggled)
ag_global.add_action(action)
+
+ # add a menu with radio buttons to choose the walking method
+ ag_global.add_radio_actions([
+ ('Python', None, "Python", None, None, WALKER_CLASSIC),
+ ('Python gnomevfs', None, "Python gnomevfs", None, None, WALKER_GNOMEVFS),
+ ('c++', None, "c++", None, None, WALKER_CPP),
+ ], 1)
+ self.methodaction = ag_global.get_action("c++")
+
uimanager.insert_action_group(ag_global, 0)
uimanager.add_ui_from_string(ui)
menubar = uimanager.get_widget('/MenuBar')
@@ -113,7 +133,8 @@ class UI( gtk.Window ):
b = gtk.FileChooserButton('Select a Folder')
b.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
- b.connect("current-folder-changed", lambda w: self.__set_path(w.get_current_folder()))
+ b.set_local_only(False)
+ b.connect("current-folder-changed", lambda w: self.__set_uri(w.get_uri()))
toolbar.pack_start( b, True )
self.filechooserbutton = b
@@ -155,12 +176,9 @@ class UI( gtk.Window ):
self.eventbox.connect("size-allocate",self.__on_resize)
self.connect("delete_event", self.close)
-
-
- def open_folder(self,path):
- print "open",path
+ def open_folder(self,uri):
# change folder by changing the chooser button
- self.filechooserbutton.set_current_folder(path)
+ self.filechooserbutton.set_current_folder_uri(uri)
def about_dialog(self,widget):
d = gtk.AboutDialog()
@@ -176,7 +194,7 @@ class UI( gtk.Window ):
gtk.main_quit()
def open_parent(self,widget):
- p = os.path.split(self.path)
+ p = os.path.split(self.uri)
p = os.path.join(p[:-1])
self.open_folder(p[0])
@@ -199,10 +217,9 @@ class UI( gtk.Window ):
self.renderer.HEIGHT = self.height
self.renderer.INNER_RADIUS = self.height/12
self.renderer.RING_RADIUS = self.height/10
- #self.renderer.INNER_RADIUS *= self.zoomfactor
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.path)
+ self.renderer.draw_segment(self.ctx, 0, 0, 1, 0, 1, self.data, self.uri)
self.image.queue_draw()
self.__show_busy_cursor(-1)
@@ -225,26 +242,26 @@ class UI( gtk.Window ):
#----------------------------------------------------------- private methods
- def __set_path(self,path):
+ def __set_uri(self,uri):
""" Set a path and start parsing. Also used to refresh or reset. """
- self.path = path
+ self.uri = uri
self.walker.stop()
- if path is None: return
- self.walker.walk(path);
- self.__show_busy_cursor(1)
+ if uri:
+ self.walker.walk(uri,self.methodaction.get_current_value())
+ self.__show_busy_cursor(1)
def __selectfolder(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_current_folder())
+ if d.run() == 1: self.open_folder(d.get_uri())
d.destroy()
def __hidden_files_toggled(self,widget):
if self.walker.showhidden == widget.get_active(): return
self.walker.showhidden = widget.get_active()
- self.__set_path(self.path)
+ self.__set_uri(self.uri)
def __move_event(self, widget, event):
diff --git a/src/fringlib/fringwalker.py b/src/fringlib/fringwalker.py
index bb6195b..a341955 100644
--- a/src/fringlib/fringwalker.py
+++ b/src/fringlib/fringwalker.py
@@ -1,147 +1,239 @@
-import os, os.path, stat, sys, time
+import os, os.path, stat, sys
+import threading
+import posixpath # instead of os.path for gnomevfs operations
+import time
+
import gobject, gtk
-from threading import Thread
+from gnomevfs import *
-class sum_list:
- def __init__(self, l, name = None):
+try: import fringtools
+except: print 'Error: Could not find "fringtools" extension module'
- self.the_sum = 0
- self.data = list(l)
- self.name = name
+WALKER_CLASSIC = 0
+WALKER_GNOMEVFS = 1
+WALKER_CPP = 2
- for fn, i in self.data:
+def print_tree( treearray, tab=0 ):
+ """ An utility function to print out a tree array """
- if isinstance(i, sum_list):
- self.the_sum += i.the_sum
- else:
- self.the_sum += i
+ fn, data, size = treearray
+ print " "*tab,"%s (%i)"%(fn,size)
+ if not data: return
+ if tab > 1: return
+ for e in data:
+ print_tree(e,tab+1)
- def __str__(self):
- return self.name+": "+str(self.data)
- def sort(self):
+def treearray_cmp_fn( a, b ):
+ """ a and b are tuples describing a directory tree. Compare first by directory
+ status, then by size, and finally by name. """
- def cmp_fn(a, b):
- a_dir = isinstance(a[1], sum_list)
- b_dir = isinstance(b[1], sum_list)
+ a_dir = a[1] != None
+ b_dir = b[1] != None
- if a_dir and not b_dir:
- return 1
- elif b_dir and not a_dir:
- return -1
- elif a_dir:
- return cmp(a[1].the_sum, b[1].the_sum)
- else:
- return cmp(a[1], b[1])
+ if a_dir and not b_dir:
+ return 1
+ elif b_dir and not a_dir:
+ return -1
+ elif a_dir and b_dir:
+ return cmp(a[2], b[2]) # compare sizes
+ else:
+ return cmp(a[0], b[0]) # compare names
- self.data.sort(cmp_fn)
class FringWalker( gobject.GObject ):
+ """ Manages requests for walking directories
+
+ Directory entries are represented by tuples of the following form:
+ (name, contents, size)
+
+ name: the file name of the directory
+ contents: a list of entries within the entry. If None, the entry is not a directory
+ size: the size of the entry.
+ """
__gsignals__ = {
'list-changed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
'finished': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
+ 'manually-stopped': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ),
'progress': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_INT,gobject.TYPE_INT)),
}
def __init__(self):
gobject.GObject.__init__(self)
self.thread = None
- self.stopsignal = False
self.showhidden = False
- def walk(self,path):
- self.thread = Thread(None,self._parse,None,(path,))
- self.stopsignal = False
- print "start thread (%s)"%path
+ def walk(self,uri,method=WALKER_GNOMEVFS):
+ self.stop()
+ self.thread = WalkThread(self,uri,self.showhidden,method)
self.thread.start()
def stop(self):
- if self.thread is None:
- return
-
- if not self.thread.isAlive():
+ if self.thread:
+ self.emit("manually-stopped")
+ self.thread.stopsignal = True
self.thread = None
- return
-
- self.stopsignal = True
- print "stopping thread:"
- #self.thread.join()
- print "ok"
-
- def _parse(self,path):
- """ Parse the root directory """
- def progress_fn(c, l, r):
- # emit signals
+ def _progress_fn(self, walkthread, c, l, r):
+ # only emit if called from the current request
+ if walkthread == self.thread:
gtk.gdk.threads_enter()
self.emit("progress", c, l)
self.emit("list-changed", r)
gtk.gdk.threads_leave()
-
- r = self._build_tree(path, progress_fn)
- gtk.gdk.threads_enter()
- self.emit("finished", r)
- gtk.gdk.threads_leave()
+ def _finished_fn(self, walkthread, r):
+ if walkthread == self.thread:
+ gtk.gdk.threads_enter()
+ self.emit("finished", r)
+ gtk.gdk.threads_leave()
+ self.thread = None
- print "finished walking", path
- def _build_tree(self, path, progress_fn = None):
- """ Parse directories recursively """
-
- ret = []
- tmp_dirs = []
-
+class WalkThread( threading.Thread ):
+ """ A separate class for the thread. """
+
+
+ def __init__(self, master, uri, showhidden, method=WALKER_CLASSIC):
+ """ Parameters: A FringWalker instance, a string with the path and a bool """
+ threading.Thread.__init__(self)
+ self.stopsignal = False
+ self.master = master
+ self.uri = URI(uri)
+ self.showhidden = showhidden
+ self.method = method
+
+ def __uri_tail(self, uri):
+ """ Return the tail (the filename) of a gnomevfs uri """
+ f = format_uri_for_display(str(uri))
+ f = posixpath.split( f )[-1]
+ return f
+
+ def build_tree_python(self, path):
+ l = []
+ total = 0
+ for fn in os.listdir(path):
+ if self.stopsignal: return (None,None,0)
+ if not self.showhidden and fn[0] == ".": continue
+ try: p = os.path.join(path, fn)
+ except: continue
+ s = os.lstat(p)
+ if stat.S_ISDIR(s.st_mode):
+ sub = self.build_tree_python(p)
+ l.append( sub )
+ total += sub[2]
+ elif stat.S_ISREG(s.st_mode):
+ l.append( (fn, None, s.st_size) )
+ total += s.st_size
+ return (os.path.split(path)[-1], l, total)
+
+
+
+ def build_tree_gnomevfs(self, uri):
+
+ try: h = DirectoryHandle(uri)
+ except InvalidURIError:
+ print uri,"is not a valid uri, skipped"
+ return (str(uri), None, 0)
+ except NotFoundError:
+ print uri,"not found, skipped"
+ return (str(uri), None, 0)
+
+ l = []
+ total = 0
+ d = h.next()
try:
- # walk files in directory
- for fn in os.listdir(path):
+ while True:
+ if self.stopsignal: return (None,None,0)
+ d = h.next()
+ if not self.showhidden and d.name[0] == ".": continue
- if self.stopsignal:
- return sum_list([])
+ if d.type == 2: # directory
+ sub = self.build_tree_gnomevfs(uri.append_path(d.name))
+ l.append( sub )
+ total += sub[2]
+ else:
+ l.append( (d.name, None, d.size) )
+ total += d.size
- if not self.showhidden and fn[0] == '.':
- continue
+ except StopIteration: pass
+ return (self.__uri_tail(uri), l, total)
- try:
- p = os.path.join(path, fn)
- s = os.lstat(p)
- except:
- continue
- if stat.S_ISDIR(s.st_mode):
- tmp_dirs.append(fn);
- elif stat.S_ISREG(s.st_mode):
- ret.append((fn, s.st_size))
- except OSError:
- pass
+ def run(self):
+ """ Parse the root directory """
+ # write some debug information
+ starttime = time.time()
+ print "start walking",self.uri,
+ if self.method == WALKER_CPP: print "(using c++ extension)"
+ elif self.method == WALKER_GNOMEVFS: print "(using python and gnomevfs)"
+ else: print "(using classic python)"
+
+ # scan root directory first (using gnomevfs)
+ try: h = DirectoryHandle(self.uri)
+ except InvalidURIError:
+ print uri,"is not a valid uri, skipped"
+ return (str(self.uri), None, 0)
+ except NotFoundError:
+ print uri,"not found, skipped"
+ return (str(self.uri), None, 0)
+
+ subdirectories = []
+ l = []
+ total = 0
+ d = h.next()
try:
- c = 0
-
- for fn in tmp_dirs:
- c += 1
-
- if self.stopsignal:
- return sum_list([])
-
- try:
- p = os.path.join(path, fn)
- except:
- continue
+ while True:
+ if self.stopsignal: return
+ d = h.next()
+ if not self.showhidden and d.name[0] == ".": continue
+
+ if d.type == 2: # directory
+ subdirectories.append( d.name );
+ else:
+ l.append( (d.name, None, d.size) )
+ total += d.size
+ except StopIteration: pass
+
+ # emit an intermediate version to fill up the screen while waiting
+ self.master._progress_fn(self,
+ 0, len(subdirectories),
+ (self.__uri_tail(self.uri), l, total))
+
+ # now walk the subdirectories with the faster extension function
+ c = 0
+
+ for directory in subdirectories:
+ c += 1
+
+ if self.method == WALKER_CPP:
+ path = get_local_path_from_uri(str(self.uri))+os.sep+directory
+ sub = fringtools.build_tree(path, self.showhidden)
+ elif self.method == WALKER_GNOMEVFS:
+ uri = self.uri
+ sub = self.build_tree_gnomevfs(uri.append_path(directory))
+ else:
+ path = get_local_path_from_uri(str(self.uri))+os.sep+directory
+ sub = self.build_tree_python(path)
- ret.append((fn, self._build_tree(p)))
+ if self.stopsignal: return
- if not (progress_fn is None):
- r = sum_list(ret, os.path.split(path)[-1])
- r.sort()
- progress_fn(c, len(tmp_dirs), r)
-
- except OSError:
- pass
+ total += sub[2]
+ l.append( (directory,sub[1],sub[2]) );
- r = sum_list(ret, os.path.split(path)[-1])
- r.sort()
+ l.sort(treearray_cmp_fn)
- return r
+ # emit an intermediate version after each directory
+ self.master._progress_fn(self,
+ c, len(subdirectories),
+ (self.__uri_tail(self.uri), l, total))
+
+
+ l.sort(treearray_cmp_fn)
+ # emit final signal
+ self.master._finished_fn(self,(self.__uri_tail(self.uri), l, total))
+ print "finished walking",self.uri,"(time=%s)"%round(time.time()-starttime,2)
+
diff --git a/src/fringlib/launch.py b/src/fringlib/launch.py
index 8e3b107..f420768 100755
--- a/src/fringlib/launch.py
+++ b/src/fringlib/launch.py
@@ -17,11 +17,12 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA.
-# Copyright Lennart Poettering, 2006
+# Copyright Lennart Poettering, Frederic Back 2006
import sys, os
import gtk
import fringui
+import gnomevfs
if __name__ == "__main__":
@@ -30,6 +31,10 @@ if __name__ == "__main__":
else:
tree_path = os.path.expanduser("~")
+ # first parameter can be a gnomevfs uri or a local path
+ #try: uri = URI(tree_path)
+ #except: uri = get_uri_from_local_path(tree_path)
+
ui = fringui.UI(tree_path)
gtk.gdk.threads_init()