1#!/usr/bin/env python 2""" 3fs.browsewin 4============ 5 6Creates a window which can be used to browse the contents of a filesystem. 7To use, call the 'browse' method with a filesystem object. Double click a file 8or directory to display its properties. 9 10Requires wxPython. 11 12""" 13 14import wx 15import wx.gizmos 16 17from fs.path import isdotfile, pathsplit 18from fs.errors import FSError 19 20class InfoFrame(wx.Frame): 21 22 def __init__(self, parent, path, desc, info): 23 wx.Frame.__init__(self, parent, -1, style=wx.DEFAULT_FRAME_STYLE, size=(500, 500)) 24 25 self.SetTitle("FS Object info - %s (%s)" % (path, desc)) 26 27 keys = info.keys() 28 keys.sort() 29 30 self.list_ctrl = wx.ListCtrl(self, -1, style=wx.LC_REPORT|wx.SUNKEN_BORDER) 31 32 self.list_ctrl.InsertColumn(0, "Key") 33 self.list_ctrl.InsertColumn(1, "Value") 34 35 self.list_ctrl.SetColumnWidth(0, 190) 36 self.list_ctrl.SetColumnWidth(1, 300) 37 38 for key in sorted(keys, key=lambda k:k.lower()): 39 self.list_ctrl.Append((key, unicode(info.get(key)))) 40 41 self.Center() 42 43 44 45class BrowseFrame(wx.Frame): 46 47 def __init__(self, fs, hide_dotfiles=False): 48 49 wx.Frame.__init__(self, None, size=(1000, 600)) 50 51 self.fs = fs 52 self.hide_dotfiles = hide_dotfiles 53 self.SetTitle("FS Browser - " + unicode(fs)) 54 55 self.tree = wx.gizmos.TreeListCtrl(self, -1, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) 56 57 self.tree.AddColumn("File System") 58 self.tree.AddColumn("Description") 59 self.tree.AddColumn("Size") 60 self.tree.AddColumn("Created") 61 62 self.tree.SetColumnWidth(0, 300) 63 self.tree.SetColumnWidth(1, 250) 64 self.tree.SetColumnWidth(2, 150) 65 self.tree.SetColumnWidth(3, 250) 66 67 self.root_id = self.tree.AddRoot('root', data=wx.TreeItemData( {'path':"/", 'expanded':False} )) 68 69 rid = self.tree.GetItemData(self.root_id) 70 71 isz = (16, 16) 72 il = wx.ImageList(isz[0], isz[1]) 73 self.fldridx = il.Add(wx.ArtProvider_GetBitmap(wx.ART_FOLDER, wx.ART_OTHER, isz)) 74 self.fldropenidx = il.Add(wx.ArtProvider_GetBitmap(wx.ART_FILE_OPEN, wx.ART_OTHER, isz)) 75 self.fileidx = il.Add(wx.ArtProvider_GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, isz)) 76 77 self.tree.SetImageList(il) 78 self.il = il 79 80 self.tree.SetItemImage(self.root_id, self.fldridx, wx.TreeItemIcon_Normal) 81 self.tree.SetItemImage(self.root_id, self.fldropenidx, wx.TreeItemIcon_Expanded) 82 83 self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.OnItemExpanding) 84 self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated) 85 86 wx.CallAfter(self.OnInit) 87 88 def OnInit(self): 89 90 self.expand(self.root_id) 91 92 93 def expand(self, item_id): 94 95 item_data = self.tree.GetItemData(item_id).GetData() 96 97 path = item_data["path"] 98 99 if not self.fs.isdir(path): 100 return 101 102 if item_data['expanded']: 103 return 104 105 try: 106 paths = ( [(True, p) for p in self.fs.listdir(path, absolute=True, dirs_only=True)] + 107 [(False, p) for p in self.fs.listdir(path, absolute=True, files_only=True)] ) 108 except FSError, e: 109 msg = "Failed to get directory listing for %s\n\nThe following error was reported:\n\n%s" % (path, e) 110 wx.MessageDialog(self, msg, "Error listing directory", wx.OK).ShowModal() 111 paths = [] 112 113 114 #paths = [(self.fs.isdir(p), p) for p in self.fs.listdir(path, absolute=True)] 115 116 if self.hide_dotfiles: 117 paths = [p for p in paths if not isdotfile(p[1])] 118 119 if not paths: 120 #self.tree.SetItemHasChildren(item_id, False) 121 #self.tree.Collapse(item_id) 122 return 123 124 paths.sort(key=lambda p:(not p[0], p[1].lower())) 125 126 for is_dir, new_path in paths: 127 128 name = pathsplit(new_path)[-1] 129 130 new_item = self.tree.AppendItem(item_id, name, data=wx.TreeItemData({'path':new_path, 'expanded':False})) 131 132 info = self.fs.getinfo(new_path) 133 134 if is_dir: 135 136 self.tree.SetItemHasChildren(new_item) 137 self.tree.SetItemImage(new_item, self.fldridx, 0, wx.TreeItemIcon_Normal) 138 self.tree.SetItemImage(new_item, self.fldropenidx, 0, wx.TreeItemIcon_Expanded) 139 140 self.tree.SetItemText(new_item, "", 2) 141 142 ct = info.get('created_time', None) 143 if ct is not None: 144 self.tree.SetItemText(new_item, ct.ctime(), 3) 145 else: 146 self.tree.SetItemText(new_item, 'unknown', 3) 147 148 else: 149 self.tree.SetItemImage(new_item, self.fileidx, 0, wx.TreeItemIcon_Normal) 150 151 self.tree.SetItemText(new_item, str(info.get('size', '?'))+ " bytes", 2) 152 153 ct = info.get('created_time', None) 154 if ct is not None: 155 self.tree.SetItemText(new_item, ct.ctime(), 3) 156 else: 157 self.tree.SetItemText(new_item, 'unknown', 3) 158 159 self.tree.SetItemText(new_item, self.fs.desc(new_path), 1) 160 161 item_data['expanded'] = True 162 self.tree.Expand(item_id) 163 164 165 def OnItemExpanding(self, e): 166 167 self.expand(e.GetItem()) 168 e.Skip() 169 170 def OnItemActivated(self, e): 171 172 item_data = self.tree.GetItemData(e.GetItem()).GetData() 173 path = item_data["path"] 174 info = self.fs.getinfo(path) 175 176 info_frame = InfoFrame(self, path, self.fs.desc(path), info) 177 info_frame.Show() 178 info_frame.CenterOnParent() 179 180 181def browse(fs, hide_dotfiles=False): 182 """Displays a window containing a tree control that displays an FS 183 object. Double-click a file/folder to display extra info. 184 185 :param fs: A filesystem object 186 :param hide_dotfiles: If True, files and folders that begin with a dot will be hidden 187 188 """ 189 190 app = wx.PySimpleApp() 191 frame = BrowseFrame(fs, hide_dotfiles=hide_dotfiles) 192 frame.Show() 193 app.MainLoop() 194 195 196if __name__ == "__main__": 197 from osfs import OSFS 198 home_fs = OSFS("~/") 199 browse(home_fs, True) 200