| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
| 2 # See LICENSE for details. | |
| 3 | |
| 4 """ | |
| 5 What I want it to look like: | |
| 6 | |
| 7 +- One | |
| 8 | \- Two | |
| 9 | |- Three | |
| 10 | |- Four | |
| 11 | +- Five | |
| 12 | | \- Six | |
| 13 | |- Seven | |
| 14 +- Eight | |
| 15 | \- Nine | |
| 16 """ | |
| 17 | |
| 18 import os | |
| 19 from Tkinter import * | |
| 20 | |
| 21 class Node: | |
| 22 def __init__(self): | |
| 23 """ | |
| 24 Do whatever you want here. | |
| 25 """ | |
| 26 self.item=None | |
| 27 def getName(self): | |
| 28 """ | |
| 29 Return the name of this node in the tree. | |
| 30 """ | |
| 31 pass | |
| 32 def isExpandable(self): | |
| 33 """ | |
| 34 Return true if this node is expandable. | |
| 35 """ | |
| 36 return len(self.getSubNodes())>0 | |
| 37 def getSubNodes(self): | |
| 38 """ | |
| 39 Return the sub nodes of this node. | |
| 40 """ | |
| 41 return [] | |
| 42 def gotDoubleClick(self): | |
| 43 """ | |
| 44 Called when we are double clicked. | |
| 45 """ | |
| 46 pass | |
| 47 def updateMe(self): | |
| 48 """ | |
| 49 Call me when something about me changes, so that my representation | |
| 50 changes. | |
| 51 """ | |
| 52 if self.item: | |
| 53 self.item.update() | |
| 54 | |
| 55 class FileNode(Node): | |
| 56 def __init__(self,name): | |
| 57 Node.__init__(self) | |
| 58 self.name=name | |
| 59 def getName(self): | |
| 60 return os.path.basename(self.name) | |
| 61 def isExpandable(self): | |
| 62 return os.path.isdir(self.name) | |
| 63 def getSubNodes(self): | |
| 64 names=map(lambda x,n=self.name:os.path.join(n,x),os.listdir(self.name)) | |
| 65 return map(FileNode,names) | |
| 66 | |
| 67 class TreeItem: | |
| 68 def __init__(self,widget,parent,node): | |
| 69 self.widget=widget | |
| 70 self.node=node | |
| 71 node.item=self | |
| 72 if self.node.isExpandable(): | |
| 73 self.expand=0 | |
| 74 else: | |
| 75 self.expand=None | |
| 76 self.parent=parent | |
| 77 if parent: | |
| 78 self.level=self.parent.level+1 | |
| 79 else: | |
| 80 self.level=0 | |
| 81 self.first=0 # gets set in Tree.expand() | |
| 82 self.subitems=[] | |
| 83 def __del__(self): | |
| 84 del self.node | |
| 85 del self.widget | |
| 86 def __repr__(self): | |
| 87 return "<Item for Node %s at level %s>"%(self.node.getName(),self.level) | |
| 88 def render(self): | |
| 89 """ | |
| 90 Override in a subclass. | |
| 91 """ | |
| 92 raise NotImplementedError | |
| 93 def update(self): | |
| 94 self.widget.update(self) | |
| 95 | |
| 96 class ListboxTreeItem(TreeItem): | |
| 97 def render(self): | |
| 98 start=self.level*"| " | |
| 99 if self.expand==None and not self.first: | |
| 100 start=start+"|" | |
| 101 elif self.expand==0: | |
| 102 start=start+"L" | |
| 103 elif self.expand==1: | |
| 104 start=start+"+" | |
| 105 else: | |
| 106 start=start+"\\" | |
| 107 r=[start+"- "+self.node.getName()] | |
| 108 if self.expand: | |
| 109 for i in self.subitems: | |
| 110 r.extend(i.render()) | |
| 111 return r | |
| 112 | |
| 113 class ListboxTree: | |
| 114 def __init__(self,parent=None,**options): | |
| 115 self.box=apply(Listbox,[parent],options) | |
| 116 self.box.bind("<Double-1>",self.flip) | |
| 117 self.roots=[] | |
| 118 self.items=[] | |
| 119 def pack(self,*args,**kw): | |
| 120 """ | |
| 121 for packing. | |
| 122 """ | |
| 123 apply(self.box.pack,args,kw) | |
| 124 def grid(self,*args,**kw): | |
| 125 """ | |
| 126 for gridding. | |
| 127 """ | |
| 128 apply(self.box.grid,args,kw) | |
| 129 def yview(self,*args,**kw): | |
| 130 """ | |
| 131 for scrolling. | |
| 132 """ | |
| 133 apply(self.box.yview,args,kw) | |
| 134 def addRoot(self,node): | |
| 135 r=ListboxTreeItem(self,None,node) | |
| 136 self.roots.append(r) | |
| 137 self.items.append(r) | |
| 138 self.box.insert(END,r.render()[0]) | |
| 139 return r | |
| 140 def curselection(self): | |
| 141 c=self.box.curselection() | |
| 142 if not c: return | |
| 143 return self.items[int(c[0])] | |
| 144 def flip(self,*foo): | |
| 145 if not self.box.curselection(): return | |
| 146 item=self.items[int(self.box.curselection()[0])] | |
| 147 if item.expand==None: return | |
| 148 if not item.expand: | |
| 149 self.expand(item) | |
| 150 else: | |
| 151 self.close(item) | |
| 152 item.node.gotDoubleClick() | |
| 153 def expand(self,item): | |
| 154 if item.expand or item.expand==None: return | |
| 155 item.expand=1 | |
| 156 item.subitems=map(lambda x,i=item,s=self:ListboxTreeItem(s,i,x),item.nod
e.getSubNodes()) | |
| 157 if item.subitems: | |
| 158 item.subitems[0].first=1 | |
| 159 i=self.items.index(item) | |
| 160 self.items,after=self.items[:i+1],self.items[i+1:] | |
| 161 self.items=self.items+item.subitems+after | |
| 162 c=self.items.index(item) | |
| 163 self.box.delete(c) | |
| 164 r=item.render() | |
| 165 for i in r: | |
| 166 self.box.insert(c,i) | |
| 167 c=c+1 | |
| 168 def close(self,item): | |
| 169 if not item.expand: return | |
| 170 item.expand=0 | |
| 171 length=len(item.subitems) | |
| 172 for i in item.subitems: | |
| 173 self.close(i) | |
| 174 c=self.items.index(item) | |
| 175 del self.items[c+1:c+1+length] | |
| 176 for i in range(length+1): | |
| 177 self.box.delete(c) | |
| 178 self.box.insert(c,item.render()[0]) | |
| 179 def remove(self,item): | |
| 180 if item.expand: | |
| 181 self.close(item) | |
| 182 c=self.items.index(item) | |
| 183 del self.items[c] | |
| 184 if item.parent: | |
| 185 item.parent.subitems.remove(item) | |
| 186 self.box.delete(c) | |
| 187 def update(self,item): | |
| 188 if item.expand==None: | |
| 189 c=self.items.index(item) | |
| 190 self.box.delete(c) | |
| 191 self.box.insert(c,item.render()[0]) | |
| 192 elif item.expand: | |
| 193 self.close(item) | |
| 194 self.expand(item) | |
| 195 | |
| 196 if __name__=="__main__": | |
| 197 tk=Tk() | |
| 198 s=Scrollbar() | |
| 199 t=ListboxTree(tk,yscrollcommand=s.set) | |
| 200 t.pack(side=LEFT,fill=BOTH) | |
| 201 s.config(command=t.yview) | |
| 202 s.pack(side=RIGHT,fill=Y) | |
| 203 t.addRoot(FileNode("C:/")) | |
| 204 #mainloop() | |
| OLD | NEW |