📄 browse.py
字号:
widget = dir.get_widget( name ) return widget, self.focus def set_focus(self, focus): parent, name = focus self.focus = parent, name self._modified() def get_next(self, start_from): parent, name = start_from dir = get_directory( parent ) widget = dir.get_widget(name) target = widget.next_inorder() if target is None: return None, None return target, (target.dir, target.name) def get_prev(self, start_from): parent, name = start_from dir = get_directory( parent ) widget = dir.get_widget(name) target = widget.prev_inorder() if target is None: return None, None return target, (target.dir, target.name) class DirectoryBrowser: palette = [ ('body', 'black', 'light gray'), ('selected', 'black', 'dark green', ('bold','underline')), ('focus', 'light gray', 'dark blue', 'standout'), ('selected focus', 'yellow', 'dark cyan', ('bold','standout','underline')), ('head', 'yellow', 'black', 'standout'), ('foot', 'light gray', 'black'), ('key', 'light cyan', 'black','underline'), ('title', 'white', 'black', 'bold'), ('dirmark', 'black', 'dark cyan', 'bold'), ('flag', 'dark gray', 'light gray'), ('error', 'dark red', 'light gray'), ] footer_text = [ ('title', "Directory Browser"), " ", ('key', "UP"), ",", ('key', "DOWN"), ",", ('key', "PAGE UP"), ",", ('key', "PAGE DOWN"), " ", ('key', "SPACE" ), " ", ('key', "+"), ",", ('key', "-"), " ", ('key', "LEFT"), " ", ('key', "HOME"), " ", ('key', "END"), " ", ('key', "Q"), ] def __init__(self): cwd = os.getcwd() store_initial_cwd( cwd ) self.listbox = urwid.ListBox( DirectoryWalker( cwd ) ) self.listbox.offset_rows = 1 self.header = urwid.Text( "" ) self.footer = urwid.AttrWrap( urwid.Text( self.footer_text ), 'foot') self.view = urwid.Frame( urwid.AttrWrap( self.listbox, 'body' ), header=urwid.AttrWrap(self.header, 'head' ), footer=self.footer ) def main(self): """Run the program.""" self.ui = urwid.raw_display.Screen() self.ui.register_palette( self.palette ) self.ui.run_wrapper( self.run ) # on exit, write the selected filenames to the console names = [escape_filename_sh(x) for x in get_selected_names()] print " ".join(names) def run(self): """Handle user input and display updating.""" self.ui.set_mouse_tracking() size = self.ui.get_cols_rows() while 1: focus, _ign = self.listbox.body.get_focus() # update display of focus directory self.header.set_text( focus.dir ) canvas = self.view.render( size, focus=1 ) self.ui.draw_screen( size, canvas ) keys = None while not keys: keys = self.ui.get_input() for k in keys: if urwid.is_mouse_event(k): event, button, col, row = k self.view.mouse_event( size, event, button, col, row, focus=True ) continue if k == 'window resize': size = self.ui.get_cols_rows() elif k in ('q','Q'): return elif k == 'right': # right can behave like + k = "+" k = self.view.keypress( size, k ) # k = unhandled key or None if k == 'left': self.move_focus_to_parent( size ) elif k == '-': self.collapse_focus_parent( size ) elif k == 'home': self.focus_home( size ) elif k == 'end': self.focus_end( size ) def collapse_focus_parent(self, size): """Collapse parent directory.""" widget, pos = self.listbox.body.get_focus() self.move_focus_to_parent( size ) pwidget, ppos = self.listbox.body.get_focus() if widget.dir != pwidget.dir: self.view.keypress( size, "-") def move_focus_to_parent(self, size): """Move focus to parent of widget in focus.""" middle,top,bottom = self.listbox.calculate_visible( size ) row_offset, focus_widget, focus_pos, focus_rows, cursor = middle trim_top, fill_above = top parent,name = os.path.split(focus_widget.dir) if parent == focus_widget.dir: # no root dir, choose first element instead self.focus_home( size ) return for widget, pos, rows in fill_above: row_offset -= rows if pos == (parent,name): self.listbox.change_focus(size, pos, row_offset) return self.listbox.change_focus( size, (parent, name) ) return def focus_home( self, size ): """Move focus to very top.""" dir = get_directory("/") widget = dir.get_first() parent, name = widget.dir, widget.name self.listbox.change_focus( size, (parent, name) ) def focus_end( self, size ): """Move focus to far bottom.""" maxrow, maxcol = size dir = get_directory("/") widget = dir.get_last() parent, name = widget.dir, widget.name self.listbox.change_focus( size, (parent, name), maxrow-1 )def main(): DirectoryBrowser().main()######## global cache of directory information_dir_cache = {}def get_directory(name): """Return the Directory object for a given path. Create if necessary.""" if not _dir_cache.has_key(name): _dir_cache[name] = Directory(name) return _dir_cache[name]def directory_cached(name): """Return whether the directory is in the cache.""" return _dir_cache.has_key(name)def get_selected_names(): """Return a list of all filenames marked as selected.""" l = [] for d in _dir_cache.values(): for w in d.widgets.values(): if w.selected: l.append( os.path.join( w.dir, w.name ) ) return l ####### store path components of initial current working directory_initial_cwd = []def store_initial_cwd( name ): """Store the initial current working directory path components.""" global _initial_cwd _initial_cwd = name.split( dir_sep() )def starts_expanded( name ): """Return True if directory is a parent of initial cwd.""" l = name.split( dir_sep() ) if len(l) > len( _initial_cwd ): return False if l != _initial_cwd[:len(l)]: return False return Truedef escape_filename_sh( name ): """Return a hopefully safe shell-escaped version of a filename.""" # check whether we have unprintable characters for ch in name: if ord(ch) < 32: # found one so use the ansi-c escaping return escape_filename_sh_ansic( name ) # all printable characters, so return a double-quoted version name.replace('\\','\\\\') name.replace('"','\\"') name.replace('`','\\`') name.replace('$','\\$') return '"'+name+'"'def escape_filename_sh_ansic( name ): """Return an ansi-c shell-escaped version of a filename.""" out =[] # gather the escaped characters into a list for ch in name: if ord(ch) < 32: out.append("\\x%02x"% ord(ch)) elif ch == '\\': out.append('\\\\') else: out.append( ch ) # slap them back together in an ansi-c quote $'...' return "$'" + "".join(out) + "'"def sensible_cmp( name_a, name_b ): """Case insensitive compare with sensible numeric ordering. "blah7" < "BLAH08" < "blah9" < "blah10" """ # ai, bi are indexes into name_a, name_b ai = bi = 0 def next_atom( name, i ): """Return the next 'atom' and the next index. An 'atom' is either a nonnegative integer or an uppercased character used for defining sort order.""" a = name[i].upper() i += 1 if a.isdigit(): while i < len(name) and name[i].isdigit(): a += name[i] i += 1 a = long(a) return a, i # compare one atom at a time while ai < len(name_a) and bi < len(name_b): a, ai = next_atom( name_a, ai ) b, bi = next_atom( name_b, bi ) if a < b: return -1 if a > b: return 1 # if all out of atoms to compare, do a regular cmp if ai == len(name_a) and bi == len(name_b): return cmp(name_a,name_b) # the shorter one comes first if ai == len(name_a): return -1 return 1def dir_sep(): """Return the separator used in this os.""" return getattr(os.path,'sep','/')if __name__=="__main__": main()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -