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 |