# File: tree.py # References: # http://hg.python.org/cpython/file/4e32c450f438/Lib/tkinter/ttk.py # http://www.tcl.tk/man/tcl8.5/TkCmd/ttk_treeview.htm#M79 # http://svn.python.org/projects/python/branches/pep-0384/Demo/tkinter/ttk/dirbrowser.py import os from tkinter import * from tkinter import ttk #@Reimport from demopanels import MsgPanel, SeeDismissPanel # Constants for formatting file sizes KB = 1024.0 MB = KB * KB GB = MB * KB class TreeDemo(ttk.Frame): def __init__(self, isapp=True, name='treedemo'): ttk.Frame.__init__(self, name=name) self.pack(expand=Y, fill=BOTH) self.master.title('Tree Demo') self.isapp = isapp self._create_widgets() def _create_widgets(self): if self.isapp: MsgPanel(self, ["One of the new Tk themed widgets is a tree widget, which allows ", "the user to browse a hierarchical data-set such as a file system. ", "The tree widget not only allows for the tree part itself, but it ", "also supports an arbitrary number of additional columns which can ", "show additional data (in this case, the size of the files found ", "on your file system). You can also change the width of the columns ", "by dragging the boundary between them."]) SeeDismissPanel(self) self._create_demo_panel() def _create_demo_panel(self): demoPanel = Frame(self) demoPanel.pack(side=TOP, fill=BOTH, expand=Y) self._create_treeview(demoPanel) self._populate_root() def _create_treeview(self, parent): f = ttk.Frame(parent) f.pack(side=TOP, fill=BOTH, expand=Y) # create the tree and scrollbars self.dataCols = ('fullpath', 'type', 'size') self.tree = ttk.Treeview(columns=self.dataCols, displaycolumns='size') ysb = ttk.Scrollbar(orient=VERTICAL, command= self.tree.yview) xsb = ttk.Scrollbar(orient=HORIZONTAL, command= self.tree.xview) self.tree['yscroll'] = ysb.set self.tree['xscroll'] = xsb.set # setup column headings self.tree.heading('#0', text='Directory Structure', anchor=W) self.tree.heading('size', text='File Size', anchor=W) self.tree.column('size', stretch=0, width=70) # add tree and scrollbars to frame self.tree.grid(in_=f, row=0, column=0, sticky=NSEW) ysb.grid(in_=f, row=0, column=1, sticky=NS) xsb.grid(in_=f, row=1, column=0, sticky=EW) # set frame resizing priorities f.rowconfigure(0, weight=1) f.columnconfigure(0, weight=1) # action to perform when a node is expanded self.tree.bind('<<TreeviewOpen>>', self._update_tree) def _populate_root(self): # use current directory as root node self.path = os.getcwd() # insert current directory at top of tree # 'values' = column values: fullpath, type, size # if a column value is omitted, assumed empty parent = self.tree.insert('', END, text=self.path, values=[self.path, 'directory']) # add the files and sub-directories self._populate_tree(parent, self.path, os.listdir(self.path)) def _populate_tree(self, parent, fullpath, children): # parent - id of node acting as parent # fullpath - the parent node's full path # children - list of files and sub-directories # belonging to the 'parent' node for child in children: # build child's fullpath cpath = os.path.join(fullpath, child).replace('\\', '/') if os.path.isdir(cpath): # directory - only populate when expanded # (see _create_treeview() 'bind') cid =self.tree.insert(parent, END, text=child, values=[cpath, 'directory']) # add 'dummy' child to force node as expandable self.tree.insert(cid, END, text='dummy') else: # must be a 'file' size = self._format_size(os.stat(cpath).st_size) self.tree.insert(parent, END, text=child, values=[cpath, 'file', size]) def _format_size(self, size): if size >= GB: return '{:,.1f} GB'.format(size/GB) if size >= MB: return '{:,.1f} MB'.format(size/MB) if size >= KB: return '{:,.1f} KB'.format(size/KB) return '{} bytes'.format(size) def _update_tree(self, event): #@UnusedVariable # user expanded a node - build the related directory nodeId = self.tree.focus() # the id of the expanded node if self.tree.parent(nodeId): # not at root topChild = self.tree.get_children(nodeId)[0] # if the node only has a 'dummy' child, remove it and # build new directory; skip if the node is already # populated if self.tree.item(topChild, option='text') == 'dummy': self.tree.delete(topChild) path = self.tree.set(nodeId, 'fullpath') self._populate_tree(nodeId, path, os.listdir(path)) if __name__ == '__main__': TreeDemo().mainloop()
Sunday, July 29, 2012
Tkinter Treeview Demo
This code is based on the Tcl tree.tcl demo with reference to the Python example, dirbrowser.py. The Tcl demo displays all system drives and all directories and files. There doesn't appear to be a simple Pythonic way to do the same and, as the main intent is to demonstrate building a tree hierarchy, I've opted to only display the current working directory.
Labels:
Tkinter Demos,
Tkinter Treeview