📄 browse.py
字号:
#!/usr/bin/python## Urwid example lazy directory browser / tree view# Copyright (C) 2004-2007 Ian Ward## This library is free software; you can redistribute it and/or# modify it under the terms of the GNU Lesser General Public# License as published by the Free Software Foundation; either# version 2.1 of the License, or (at your option) any later version.## This library 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# Lesser General Public License for more details.## You should have received a copy of the GNU Lesser General Public# License along with this library; if not, write to the Free Software# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA## Urwid web site: http://excess.org/urwid/"""Urwid example lazy directory browser / tree viewFeatures:- custom selectable widgets for files and directories- custom message widgets to identify access errors and empty directories- custom list walker for displaying widgets in a tree fashion- outputs a quoted list of files and directories "selected" on exit"""import urwidimport urwid.raw_displayimport osclass TreeWidget(urwid.WidgetWrap): """A widget representing something in the file tree.""" def __init__(self, dir, name, index, display): self.dir = dir self.name = name self.index = index parent, _ign = os.path.split(dir) # we're at the top if parent is same as dir if dir == parent: self.depth = 0 else: self.depth = dir.count(dir_sep()) widget = urwid.Text([" "*self.depth, display]) self.widget = widget w = urwid.AttrWrap(widget, None) self.__super.__init__(w) self.selected = False self.update_w() def selectable(self): return True def keypress(self, (maxcol,), key): """Toggle selected on space, ignore other keys.""" if key == " ": self.selected = not self.selected self.update_w() else: return key def update_w(self): """Update the attributes of self.widget based on self.selected. """ if self.selected: self.w.attr = 'selected' self.w.focus_attr = 'selected focus' else: self.w.attr = 'body' self.w.focus_attr = 'focus' def first_child(self): """Default to have no children.""" return None def last_child(self): """Default to have no children.""" return None def next_inorder(self): """Return the next TreeWidget depth first from this one.""" child = self.first_child() if child: return child else: dir = get_directory(self.dir) return dir.next_inorder_from(self.index) def prev_inorder(self): """Return the previous TreeWidget depth first from this one.""" dir = get_directory(self.dir) return dir.prev_inorder_from(self.index)class EmptyWidget(TreeWidget): """A marker for expanded directories with no contents.""" def __init__(self, dir, name, index): self.__super.__init__(dir, name, index, ('flag',"(empty directory)")) def selectable(self): return False class ErrorWidget(TreeWidget): """A marker for errors reading directories.""" def __init__(self, dir, name, index): self.__super.__init__(dir, name, index, ('error',"(error/permission denied)")) def selectable(self): return Falseclass FileWidget(TreeWidget): """Widget for a simple file (or link, device node, etc).""" def __init__(self, dir, name, index): self.__super.__init__(dir, name, index, name)class DirectoryWidget(TreeWidget): """Widget for a directory.""" def __init__(self, dir, name, index ): self.__super.__init__(dir, name, index, "") # check if this directory starts expanded self.expanded = starts_expanded( os.path.join(dir,name) ) self.update_widget() def update_widget(self): """Update display widget text.""" if self.expanded: mark = "+" else: mark = "-" self.widget.set_text( [" "*(self.depth), ('dirmark', mark), " ", self.name] ) def keypress(self, (maxcol,), key ): """Handle expand & collapse requests.""" if key == "+": self.expanded = True self.update_widget() elif key == "-": self.expanded = False self.update_widget() else: return self.__super.keypress((maxcol,), key) def mouse_event(self, (maxcol,), event, button, col, row, focus): if event != 'mouse press' or button!=1: return False if row == 0 and col == 2*self.depth: self.expanded = not self.expanded self.update_widget() return True return False def first_child(self): """Return first child if expanded.""" if not self.expanded: return None full_dir = os.path.join(self.dir, self.name) dir = get_directory( full_dir ) return dir.get_first() def last_child(self): """Return last child if expanded.""" if not self.expanded: return None full_dir = os.path.join(self.dir, self.name) dir = get_directory( full_dir ) widget = dir.get_last() sub = widget.last_child() if sub is not None: return sub return widget class Directory: """Store sorted directory contents and cache TreeWidget objects.""" def __init__(self, path): self.path = path self.widgets = {} dirs = [] files = [] try: # separate dirs and files for a in os.listdir(path): if os.path.isdir(os.path.join(path,a)): dirs.append( a ) else: files.append( a ) except OSError, e: self.widgets[None] = ErrorWidget( self.path, None, 0 ) # sort dirs and files dirs.sort(sensible_cmp) files.sort(sensible_cmp) # store where the first file starts self.dir_count = len(dirs) # collect dirs and files together again self.items = dirs + files # if no items, put a dummy None item in the list if not self.items: self.items = [None] def get_widget(self, name): """Return the widget for a given file. Create if necessary.""" if self.widgets.has_key( name ): return self.widgets[name] # determine the correct TreeWidget type (constructor) index = self.items.index( name ) if name is None: constructor = EmptyWidget elif index < self.dir_count: constructor = DirectoryWidget else: constructor = FileWidget widget = constructor( self.path, name, index ) self.widgets[name] = widget return widget def next_inorder_from(self, index): """Return the TreeWidget following index depth first.""" index += 1 # try to get the next item at same level if index < len(self.items): return self.get_widget(self.items[index]) # need to go up a level parent, myname = os.path.split(self.path) # give up if we can't go higher if parent == self.path: return None # find my location in parent, and return next inorder pdir = get_directory( parent ) mywidget = pdir.get_widget( myname ) return pdir.next_inorder_from( mywidget.index ) def prev_inorder_from(self, index): """Return the TreeWidget preceeding index depth first.""" index -= 1 if index >= 0: widget = self.get_widget(self.items[index]) widget_child = widget.last_child() if widget_child: return widget_child else: return widget # need to go up a level parent, myname = os.path.split(self.path) # give up if we can't go higher if parent == self.path: return None # find myself in parent, and return pdir = get_directory( parent ) return pdir.get_widget( myname ) def get_first(self): """Return the first TreeWidget in the directory.""" return self.get_widget(self.items[0]) def get_last(self): """Return the last TreeWIdget in the directory.""" return self.get_widget(self.items[-1]) class DirectoryWalker(urwid.ListWalker): """ListWalker-compatible class for browsing directories. positions used are directory,filename tuples.""" def __init__(self, start_from ): parent = start_from dir = get_directory(parent) widget = dir.get_first() self.focus = parent, widget.name def get_focus(self): parent, name = self.focus dir = get_directory( parent )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -