Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | # |
2 | # FILE: TreeBrowser.py | |
3 | # | |
4 | # DESCRIPTION: | |
5 | # This file provides a generic hierarchical tree browser widget. | |
6 | # | |
7 | # AUTHOR: Steve Kinneberg <skinneberg@mvista.com>, | |
8 | # MontaVista Software, Inc. <source@mvista.com> | |
9 | # | |
10 | # Copyright 2001 MontaVista Software Inc. | |
11 | # | |
12 | # This program is free software; you can redistribute it and/or modify it | |
13 | # under the terms of the GNU General Public License as published by the | |
14 | # Free Software Foundation; either version 2 of the License, or (at your | |
15 | # option) any later version. | |
16 | # | |
17 | # THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
18 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
19 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | |
20 | # NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
21 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
22 | # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |
23 | # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
24 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
26 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
27 | # | |
28 | # You should have received a copy of the GNU General Public License along | |
29 | # with this program; if not, write to the Free Software Foundation, Inc., | |
30 | # 675 Mass Ave, Cambridge, MA 02139, USA. | |
31 | # | |
32 | ||
33 | ||
34 | import types | |
35 | import Tkinter | |
36 | import Pmw | |
37 | ||
38 | ||
39 | class _Branching: | |
40 | def __init__(self): | |
41 | # List of branch names | |
42 | self._nodeNames = [] | |
43 | ||
44 | # Map from branch name to branch info | |
45 | # branch Either _LeafNode or _BranchNode widget of the branch | |
46 | # nodetype Either 'TreeNode' or 'LeafNode' | |
47 | self._nodeAttrs = {} | |
48 | ||
49 | def addbranch(self, branchName = None, **kw): | |
50 | kw['indent'] = self['indent'] | |
51 | return apply(self._insertnode, | |
52 | ('tree', branchName, len(self._nodeNames), | |
53 | self._treeRoot), | |
54 | kw) | |
55 | ||
56 | def addleaf(self, leafName = None, **kw): | |
57 | return apply(self._insertnode, | |
58 | ('leaf', leafName, len(self._nodeNames), | |
59 | self._treeRoot), | |
60 | kw) | |
61 | ||
62 | def insertbranch(self, branchName = None, before = 0, **kw): | |
63 | kw['indent'] = self['indent'] | |
64 | return apply(self._insertnode, | |
65 | ('tree', branchName, before, self._treeRoot), | |
66 | kw) | |
67 | ||
68 | def insertleaf(self, leafName = None, before = 0, **kw): | |
69 | return apply(self._insertnode, | |
70 | ('leaf', leafName, before, self._treeRoot), | |
71 | kw) | |
72 | ||
73 | def _insertnode(self, type, nodeName, before, treeRoot, **kw): | |
74 | if 'selectbackground' not in kw.keys(): | |
75 | kw['selectbackground'] = self['selectbackground'] | |
76 | ||
77 | if 'selectforeground' not in kw.keys(): | |
78 | kw['selectforeground'] = self['selectforeground'] | |
79 | ||
80 | if 'background' not in kw.keys(): | |
81 | kw['background'] = self['background'] | |
82 | ||
83 | if 'foreground' not in kw.keys(): | |
84 | kw['foreground'] = self['foreground'] | |
85 | ||
86 | if nodeName == None: | |
87 | nodeName = self._nodeName + ".%d" % (len(self._nodeNames) + 1) | |
88 | ||
89 | if self._nodeAttrs.has_key(nodeName): | |
90 | msg = 'Node "%s" already exists.' % nodeName | |
91 | raise ValueError, msg | |
92 | ||
93 | # Do this early to catch bad <before> spec before creating any items. | |
94 | beforeIndex = self.index(before, 1) | |
95 | attributes = {} | |
96 | ||
97 | last = (beforeIndex == len(self._nodeNames)) | |
98 | if last and len(self._nodeNames) > 0: | |
99 | # set the previous node to not last | |
100 | self._nodeAttrs[self._nodeNames[-1]]['branch']._setlast(0) | |
101 | ||
102 | if(type == 'tree'): | |
103 | node = apply(self.createcomponent, ('branch%d'%len(self._nodeNames), | |
104 | (), None, | |
105 | _BranchNode, | |
106 | self._branchFrame, | |
107 | nodeName, | |
108 | treeRoot, | |
109 | self, | |
110 | last, | |
111 | ), kw) | |
112 | attributes['nodetype'] = 'TreeNode' | |
113 | else: | |
114 | node = apply(self.createcomponent, ('leaf%d'%len(self._nodeNames), | |
115 | (), None, | |
116 | _LeafNode, | |
117 | self._branchFrame, | |
118 | nodeName, | |
119 | treeRoot, | |
120 | self, | |
121 | last, | |
122 | ), kw) | |
123 | attributes['nodetype'] = 'LeafNode' | |
124 | ||
125 | if len(self._nodeNames) == beforeIndex: | |
126 | node.pack(anchor='w') | |
127 | else: | |
128 | bname = self._nodeNames[beforeIndex] | |
129 | battrs = self._nodeAttrs[bname] | |
130 | node.pack(anchor='w', before=battrs['branch']) | |
131 | ||
132 | attributes['branch'] = node | |
133 | ||
134 | self._nodeAttrs[nodeName] = attributes | |
135 | self._nodeNames.insert(beforeIndex, nodeName) | |
136 | self._sizechange() | |
137 | return node | |
138 | ||
139 | def delete(self, *nodes): | |
140 | curSel = self._treeRoot.curselection()[0] | |
141 | for node in nodes: | |
142 | index = self.index(node) | |
143 | name = self._nodeNames.pop(index) | |
144 | dnode = self._nodeAttrs[name]['branch'] | |
145 | del self._nodeAttrs[name] | |
146 | if dnode == curSel: | |
147 | self._treeRoot._unhightlightnode(dnode) | |
148 | dnode.destroy() | |
149 | self._sizechange() | |
150 | ||
151 | def destroy(self): | |
152 | for node in len(self._nodeNames): | |
153 | self.delete(node) | |
154 | Pmw.MegaWidget.destroy(self) | |
155 | ||
156 | def index(self, index, forInsert = 0): | |
157 | if isinstance(index, _LeafNode): | |
158 | index = index._nodeName | |
159 | listLength = len(self._nodeNames) | |
160 | if type(index) == types.IntType: | |
161 | if forInsert and index <= listLength: | |
162 | return index | |
163 | elif not forInsert and index < listLength: | |
164 | return index | |
165 | else: | |
166 | raise ValueError, 'index "%s" is out of range' % index | |
167 | elif type(index) == types.StringType: | |
168 | if index in self._nodeNames: | |
169 | return self._nodeNames.index(index) | |
170 | raise ValueError, 'bad branch or leaf name: %s' % index | |
171 | elif index is Pmw.END: | |
172 | if forInsert: | |
173 | return listLength | |
174 | elif listLength > 0: | |
175 | return listLength - 1 | |
176 | else: | |
177 | raise ValueError, 'TreeNode has no branches' | |
178 | #elif index is Pmw.SELECT: | |
179 | # if listLength == 0: | |
180 | # raise ValueError, 'TreeNode has no branches' | |
181 | # return self._pageNames.index(self.getcurselection()) | |
182 | else: | |
183 | validValues = 'a name, a number, Pmw.END, Pmw.SELECT, or a reference to a TreeBrowser Leaf or Branch' | |
184 | raise ValueError, \ | |
185 | 'bad index "%s": must be %s' % (index, validValues) | |
186 | ||
187 | def getnodenames(self): | |
188 | return self._nodeNames | |
189 | ||
190 | def getnode(self, node): | |
191 | nodeName = self._nodeNames[self.index(node)] | |
192 | return self._nodeAttrs[nodeName]['branch'] | |
193 | ||
194 | ||
195 | class _LeafNode(Pmw.MegaWidget): | |
196 | ||
197 | def __init__(self, parent, nodeName, treeRoot, parentnode, last = 1, **kw): | |
198 | colors = Pmw.Color.getdefaultpalette(parent) | |
199 | ||
200 | self._nodeName = nodeName | |
201 | self._treeRoot = treeRoot | |
202 | self._parentNode = parentnode | |
203 | ||
204 | self._last = last | |
205 | # Define the megawidget options. | |
206 | INITOPT = Pmw.INITOPT | |
207 | optiondefs = ( | |
208 | ('selectbackground', colors['selectBackground'], INITOPT), | |
209 | ('selectforeground', colors['selectForeground'], INITOPT), | |
210 | ('background', colors['background'], INITOPT), | |
211 | ('foreground', colors['foreground'], INITOPT), | |
212 | ('selectcommand', None, None), | |
213 | ('deselectcommand', None, None), | |
214 | ('labelpos', 'e', INITOPT), | |
215 | ('labelmargin', 0, INITOPT), | |
216 | ('label', None, None), | |
217 | ) | |
218 | self.defineoptions(kw, optiondefs) | |
219 | ||
220 | # Initialise the base class (after defining the options). | |
221 | Pmw.MegaWidget.__init__(self, parent) | |
222 | ||
223 | # Create the components | |
224 | interior = self._hull | |
225 | ||
226 | ||
227 | labelpos = self['labelpos'] | |
228 | ||
229 | if self['label'] == None: | |
230 | self._labelWidget = self.createcomponent('labelwidget', | |
231 | (), None, | |
232 | Pmw.LabeledWidget, | |
233 | (interior,), | |
234 | #background = self['background'], | |
235 | #foreground = self['foreground'], | |
236 | ) | |
237 | else: | |
238 | self._labelWidget = self.createcomponent('labelwidget', | |
239 | (), None, | |
240 | Pmw.LabeledWidget, | |
241 | (interior,), | |
242 | label_background = self['background'], | |
243 | label_foreground = self['foreground'], | |
244 | labelpos = labelpos, | |
245 | labelmargin = self['labelmargin'], | |
246 | label_text = self['label'], | |
247 | ) | |
248 | self._labelWidget.component('label').bind('<ButtonRelease-1>', | |
249 | self._selectevent) | |
250 | ||
251 | self._labelWidget.grid(column = 1, row = 0, sticky = 'w') | |
252 | ||
253 | self._labelWidget.update() | |
254 | ||
255 | self._labelheight = self._labelWidget.winfo_height() | |
256 | ||
257 | self._lineCanvas = self.createcomponent('linecanvas', | |
258 | (), None, | |
259 | Tkinter.Canvas, | |
260 | (interior,), | |
261 | width = self._labelheight, | |
262 | height = self._labelheight, | |
263 | ) | |
264 | self._lineCanvas.grid( column = 0, row = 0, sticky = 'news') | |
265 | self._lineCanvas.update() | |
266 | ||
267 | cw = int(self._lineCanvas['width']) | |
268 | ch = int(self._lineCanvas['height']) | |
269 | ||
270 | self._lineCanvas.create_line(cw/2, ch/2, cw, ch/2, tag='hline') | |
271 | if last: | |
272 | self._lineCanvas.create_line(cw/2, 0, cw/2, ch/2, tag='vline') | |
273 | else: | |
274 | self._lineCanvas.create_line(cw/2, 0, cw/2, ch, tag='vline') | |
275 | ||
276 | # Check keywords and initialise options. | |
277 | self.initialiseoptions() | |
278 | ||
279 | ||
280 | def interior(self): | |
281 | return self._labelWidget.interior() | |
282 | ||
283 | def select(self): | |
284 | self._highlight() | |
285 | ||
286 | def getname(self): | |
287 | return self._nodeName | |
288 | ||
289 | def getlabel(self): | |
290 | return self['label'] | |
291 | ||
292 | def _selectevent(self, event): | |
293 | self._highlight() | |
294 | ||
295 | def _highlight(self): | |
296 | self._treeRoot._highlightnode(self) | |
297 | #self._subHull.configure(background = self._selectbg, relief = 'raised') | |
298 | if self['label'] != None: | |
299 | self._labelWidget.configure(label_background = self['selectbackground']) | |
300 | self._labelWidget.configure(label_foreground = self['selectforeground']) | |
301 | #self._viewButton.configure(background = self._selectbg) | |
302 | cmd = self['selectcommand'] | |
303 | if callable(cmd): | |
304 | cmd(self) | |
305 | ||
306 | def _unhighlight(self): | |
307 | #self._subHull.configure(background = self._bg, relief = 'flat') | |
308 | if self['label'] != None: | |
309 | self._labelWidget.configure(label_background = self['background']) | |
310 | self._labelWidget.configure(label_foreground = self['foreground']) | |
311 | #self._viewButton.configure(background = self._bg) | |
312 | cmd = self['deselectcommand'] | |
313 | if callable(cmd): | |
314 | cmd(self) | |
315 | ||
316 | def _setlast(self, last): | |
317 | self._last = last | |
318 | ||
319 | cw = int(self._lineCanvas['width']) | |
320 | ch = int(self._lineCanvas['height']) | |
321 | ||
322 | if last: | |
323 | self._lineCanvas.create_line(cw/2, 0, cw/2, ch/2, tag='vline') | |
324 | else: | |
325 | self._lineCanvas.create_line(cw/2, 0, cw/2, ch, tag='vline') | |
326 | ||
327 | ||
328 | class _BranchNode(_LeafNode, _Branching): #Pmw.MegaWidget): | |
329 | ||
330 | def __init__(self, parent, nodeName, treeRoot, parentnode, last = 1, **kw): | |
331 | # Define the megawidget options. | |
332 | INITOPT = Pmw.INITOPT | |
333 | optiondefs = ( | |
334 | ('view', 'collapsed', None), | |
335 | ('expandcommand', None, None), | |
336 | ('collapsecommand', None, None), | |
337 | ('indent', 0, INITOPT) | |
338 | ) | |
339 | self.defineoptions(kw, optiondefs) | |
340 | ||
341 | # Initialise the base class (after defining the options). | |
342 | apply(_LeafNode.__init__, | |
343 | (self, parent, nodeName, treeRoot, parentnode, last), | |
344 | kw) | |
345 | _Branching.__init__(self) | |
346 | ||
347 | # Create the components | |
348 | interior = self._hull | |
349 | ||
350 | # Create the expand/collapse button | |
351 | self._viewButton = self.createcomponent('viewbutton', (), None, | |
352 | Tkinter.Canvas, | |
353 | (interior,), | |
354 | background = self['background'], | |
355 | width = self._labelheight - 4, | |
356 | height = self._labelheight - 4, | |
357 | borderwidth = 2, | |
358 | relief = 'raised') | |
359 | ||
360 | self._viewButton.grid(column = 0, row = 0, sticky='se') | |
361 | self._viewButton.bind('<ButtonPress-1>', self._showbuttonpress) | |
362 | self._viewButton.bind('<ButtonRelease-1>', self._toggleview) | |
363 | ||
364 | # The label widget is already created by the base class, however | |
365 | # we do need to make some slight modifications. | |
366 | if self['label'] != None: | |
367 | self._labelWidget.component('label').bind('<Double-1>', | |
368 | self._toggleview) | |
369 | self._labelWidget.grid(column=1, row=0, columnspan = 3, sticky='sw') | |
370 | ||
371 | # A line canvas is already created for us, we just need to make | |
372 | # some slight modifications | |
373 | self._lineCanvas.delete('hline') | |
374 | self._lineCanvas.grid_forget() | |
375 | ||
376 | ||
377 | # Set the minsize of column 1 to control additional branch frame indentation | |
378 | self.grid_columnconfigure(1, minsize = self['indent']) | |
379 | ||
380 | # Create the branch frame that will contain all the branch/leaf nodes | |
381 | self._branchFrame = self.createcomponent('frame', (), None, | |
382 | Tkinter.Frame, (interior,), | |
383 | #borderwidth=2, | |
384 | #relief='ridge', | |
385 | ) | |
386 | self.grid_columnconfigure(2,minsize=0, weight=1) | |
387 | #self.grid_rowconfigure(0,minsize=0) | |
388 | ||
389 | if(self['view'] == 'expanded'): | |
390 | Pmw.drawarrow(self._viewButton, | |
391 | self['foreground'], | |
392 | 'down', 'arrow') | |
393 | self._branchFrame.grid(column = 2, row = 1, sticky='nw') | |
394 | if not self._last: | |
395 | self._branchFrame.update() | |
396 | bh = self._branchFrame.winfo_height() | |
397 | self._lineCanvas.configure(height = bh) | |
398 | self._lineCanvas.grid(column = 0, row = 1, sticky='news') | |
399 | cw = int(self._lineCanvas['width']) | |
400 | ch = int(self._lineCanvas['height']) | |
401 | #self._lineCanvas.create_line(cw/2, 1, cw/2, ch, tag = 'vline') | |
402 | self._lineCanvas.coords('vline', cw/2, 1, cw/2, ch) | |
403 | else: | |
404 | Pmw.drawarrow(self._viewButton, | |
405 | self['foreground'], | |
406 | 'right', 'arrow') | |
407 | self._viewButton.configure(relief = 'raised') | |
408 | ||
409 | ||
410 | # Check keywords and initialise options. | |
411 | self.initialiseoptions() | |
412 | ||
413 | ||
414 | ||
415 | def _showbuttonpress(self, event): | |
416 | self._viewButton.configure(relief = 'sunken') | |
417 | ||
418 | def _toggleview(self, event): | |
419 | self._viewButton.configure(relief = 'sunken') | |
420 | self.select() | |
421 | if(self['view'] == 'expanded'): | |
422 | self.collapsetree() | |
423 | else: | |
424 | self.expandtree() | |
425 | self._viewButton.configure(relief = 'raised') | |
426 | ||
427 | def expandtree(self): | |
428 | if(self['view'] == 'collapsed'): | |
429 | cmd = self['expandcommand'] | |
430 | if cmd is not None: | |
431 | cmd(self) | |
432 | self['view'] = 'expanded' | |
433 | Pmw.drawarrow(self._viewButton, | |
434 | self['foreground'], | |
435 | 'down', 'arrow') | |
436 | self._branchFrame.grid(column = 2, row = 1, sticky='nw') | |
437 | ||
438 | if not self._last: | |
439 | self._branchFrame.update() | |
440 | bh = self._branchFrame.winfo_height() | |
441 | self._lineCanvas.configure(height = bh) | |
442 | self._lineCanvas.grid(column = 0, row = 1, sticky='news') | |
443 | cw = int(self._lineCanvas['width']) | |
444 | ch = int(self._lineCanvas['height']) | |
445 | #self._lineCanvas.create_line( cw/2, 1, cw/2, ch, tag = 'vline') | |
446 | self._lineCanvas.coords('vline', cw/2, 1, cw/2, ch) | |
447 | self._parentNode._sizechange() | |
448 | ||
449 | def collapsetree(self): | |
450 | if(self['view'] == 'expanded'): | |
451 | cmd = self['collapsecommand'] | |
452 | if cmd is not None: | |
453 | cmd(self) | |
454 | self['view'] = 'collapsed' | |
455 | Pmw.drawarrow(self._viewButton, | |
456 | self['foreground'], | |
457 | 'right', 'arrow') | |
458 | self._branchFrame.grid_forget() | |
459 | if not self._last: | |
460 | #self._lineCanvas.delete('vline') | |
461 | self._lineCanvas.grid_forget() | |
462 | self._parentNode._sizechange() | |
463 | ||
464 | def _setlast(self, last): | |
465 | self._last = last | |
466 | if self['view'] == 'expanded': | |
467 | self._branchFrame.update() | |
468 | bh = self._branchFrame.winfo_height() | |
469 | self._lineCanvas.configure(height = bh) | |
470 | cw = int(self._lineCanvas['width']) | |
471 | ch = int(self._lineCanvas['height']) | |
472 | self._lineCanvas.delete('vline') | |
473 | if not last: | |
474 | self._lineCanvas.create_line(cw/2, 1, cw/2, ch, tag='vline') | |
475 | ||
476 | ||
477 | def _sizechange(self): | |
478 | if not self._last and self['view'] == 'expanded': | |
479 | self._branchFrame.update() | |
480 | bh = self._branchFrame.winfo_height() | |
481 | self._lineCanvas.configure(height = bh) | |
482 | if self._lineCanvas.coords('vline')[3] < bh: | |
483 | cw = int(self._lineCanvas['width']) | |
484 | ch = int(self._lineCanvas['height']) | |
485 | #self._lineCanvas.delete('vline') | |
486 | #self._lineCanvas.create_line(cw/2, 1, cw/2, ch, tag='vline') | |
487 | self._lineCanvas.coords('vline', cw/2, 1, cw/2, ch) | |
488 | self._parentNode._sizechange() | |
489 | ||
490 | class TreeBrowser(Pmw.MegaWidget, _Branching): | |
491 | ||
492 | def __init__(self, parent = None, nodeName = '0', **kw): | |
493 | colors = Pmw.Color.getdefaultpalette(parent) | |
494 | ||
495 | # Define the megawidget options. | |
496 | INITOPT = Pmw.INITOPT | |
497 | optiondefs = ( | |
498 | ('indent', 0, INITOPT), | |
499 | ('selectbackground', colors['selectBackground'], INITOPT), | |
500 | ('selectforeground', colors['selectForeground'], INITOPT), | |
501 | ('background', colors['background'], INITOPT), | |
502 | ('foreground', colors['foreground'], INITOPT), | |
503 | #('selectrelief', 'raised', INITOPT), | |
504 | ) | |
505 | ||
506 | self.defineoptions(kw, optiondefs) | |
507 | ||
508 | # Initialise the base class (after defining the options). | |
509 | Pmw.MegaWidget.__init__(self, parent) | |
510 | _Branching.__init__(self) | |
511 | ||
512 | ||
513 | # Create the components | |
514 | interior = self._hull | |
515 | ||
516 | browserFrame = self.createcomponent('frame', (), None, | |
517 | Pmw.ScrolledFrame, | |
518 | (interior,), | |
519 | ) | |
520 | ||
521 | browserFrame.pack(expand = 1, fill='both') | |
522 | ||
523 | self._branchFrame = browserFrame.interior() | |
524 | ||
525 | self._highlightedNode = None | |
526 | self._treeRoot = self | |
527 | self._nodeName = nodeName | |
528 | # Check keywords and initialise options. | |
529 | self.initialiseoptions() | |
530 | ||
531 | def _highlightnode(self, newNode): | |
532 | if self._highlightedNode != newNode: | |
533 | if self._highlightedNode != None: | |
534 | self._highlightedNode._unhighlight() | |
535 | self._highlightedNode = newNode | |
536 | ||
537 | def _unhighlightnode(self): | |
538 | if self._highlightedNode != None: | |
539 | self._highlightedNode._unhighlight() | |
540 | self._highlightedNode = None | |
541 | ||
542 | def curselection(self): | |
543 | retVal = None | |
544 | if self._highlightedNode != None: | |
545 | retVal = (self._highlightedNode, | |
546 | self._highlightedNode._nodeName, | |
547 | self._highlightedNode['label']) | |
548 | return retVal | |
549 | ||
550 | def getname(self): | |
551 | return self._nodeName | |
552 | ||
553 | # The top-level TreeBrowser widget only shows nodes in an expanded view | |
554 | # but still provides collapsetree() and expandtree() methods so that users | |
555 | # don't have to special case the top-level node | |
556 | ||
557 | def collapsetree(self): | |
558 | return | |
559 | ||
560 | def expandtree(self): | |
561 | return | |
562 | ||
563 | def _sizechange(self): | |
564 | return | |
565 | ||
566 | if __name__ == '__main__': | |
567 | ||
568 | rootWin = Tkinter.Tk() | |
569 | ||
570 | Pmw.initialise() | |
571 | ||
572 | rootWin.title('TreeBrowser Demo') | |
573 | ||
574 | # Create the hierarchical tree browser widget | |
575 | treeBrowser = TreeBrowser(rootWin, | |
576 | #selectbackground = "darkgreen", | |
577 | #selectforeground = 'lightgreen', | |
578 | #background = 'green', | |
579 | #indent = 10, | |
580 | ) | |
581 | ||
582 | ||
583 | def printselected(node): | |
584 | selection = treeBrowser.curselection() | |
585 | if selection != None: | |
586 | print "Selected node name:", selection[1], " label:", selection[2] | |
587 | ||
588 | ||
589 | def printdeselected(node): | |
590 | selection = treeBrowser.curselection() | |
591 | if selection != None: | |
592 | print "Deselected node name:", selection[1], " label:", selection[2] | |
593 | ||
594 | def printexpanded(node): | |
595 | print "Expanded node name:", node.getname(), " label:", node.getlabel() | |
596 | ||
597 | def printcollapsed(node): | |
598 | print "Collapsed node name:", node.getname(), " label:", node.getlabel() | |
599 | ||
600 | ||
601 | ||
602 | for i in range(3): | |
603 | # Add a tree node to the top level | |
604 | treeNodeLevel1 = treeBrowser.addbranch(label = 'TreeNode %d'%i, | |
605 | selectcommand = printselected, | |
606 | deselectcommand = printdeselected, | |
607 | expandcommand = printexpanded, | |
608 | collapsecommand = printcollapsed, | |
609 | ) | |
610 | for j in range(3): | |
611 | # Add a tree node to the second level | |
612 | treeNodeLevel2 = treeNodeLevel1.addbranch(label = 'TreeNode %d.%d'%(i,j), | |
613 | #selectforeground = 'yellow', | |
614 | selectcommand = printselected, | |
615 | deselectcommand = printdeselected, | |
616 | expandcommand = printexpanded, | |
617 | collapsecommand = printcollapsed, | |
618 | ) | |
619 | if i == 0 and j == 1: | |
620 | dynamicTreeRootNode = treeNodeLevel1 | |
621 | dynamicTreePosNode = treeNodeLevel2 | |
622 | ||
623 | for item in range((i+1)*(j+1)): | |
624 | # Add a leaf node to the third level | |
625 | leaf = treeNodeLevel2.addleaf(label = "Item %c"%(item+65), | |
626 | #selectbackground = 'blue', | |
627 | selectcommand = printselected, | |
628 | deselectcommand = printdeselected) | |
629 | for item in range(i+1): | |
630 | # Add a leaf node to the top level | |
631 | leaf = treeNodeLevel1.addleaf(label = "Item %c"%(item+65), | |
632 | selectcommand = printselected, | |
633 | deselectcommand = printdeselected) | |
634 | ||
635 | ||
636 | treeNodeLevel1 = treeBrowser.addbranch(label = 'Check Button Label', | |
637 | selectcommand = printselected, | |
638 | deselectcommand = printdeselected, | |
639 | expandcommand = printexpanded, | |
640 | collapsecommand = printcollapsed, | |
641 | ) | |
642 | checkButton = Tkinter.Checkbutton(treeNodeLevel1.interior(), | |
643 | text = 'Da Check Button', | |
644 | relief = 'ridge', | |
645 | command = treeNodeLevel1.select) | |
646 | checkButton.pack() | |
647 | ||
648 | treeNodeLevel1.addleaf(label = 'Labeled Leaf', | |
649 | selectcommand = printselected, | |
650 | deselectcommand = printdeselected) | |
651 | leaf = treeNodeLevel1.addleaf(label = 'Labeled Leaf w/ Checkbutton', | |
652 | selectcommand = printselected, | |
653 | deselectcommand = printdeselected) | |
654 | checkButton = Tkinter.Checkbutton(leaf.interior(), | |
655 | text = 'Da Check Button', | |
656 | relief = 'ridge', | |
657 | command = leaf.select) | |
658 | checkButton.pack() | |
659 | ||
660 | ||
661 | treeNodeLevel1 = treeBrowser.addbranch(selectcommand = printselected, | |
662 | deselectcommand = printdeselected, | |
663 | expandcommand = printexpanded, | |
664 | collapsecommand = printcollapsed, | |
665 | ) | |
666 | checkButton = Tkinter.Checkbutton(treeNodeLevel1.interior(), | |
667 | text = 'Check Button with no label', | |
668 | relief = 'ridge', | |
669 | command = treeNodeLevel1.select) | |
670 | checkButton.pack() | |
671 | ||
672 | treeNodeLevel1 = treeBrowser.addbranch(label = 'Label', | |
673 | selectcommand = printselected, | |
674 | deselectcommand = printdeselected, | |
675 | expandcommand = printexpanded, | |
676 | collapsecommand = printcollapsed, | |
677 | ) | |
678 | ||
679 | # setup dynamic tree node insertion and removal | |
680 | class dynTree: | |
681 | def __init__(self): | |
682 | self.dyn = Tkinter.IntVar() | |
683 | self.dtree = None | |
684 | ||
685 | self.dLeaf = treeBrowser.addleaf(selectcommand = self.dynSelected, | |
686 | deselectcommand = self.dynDeselected) | |
687 | ||
688 | self.dCheckButton = Tkinter.Checkbutton(self.dLeaf.interior(), | |
689 | text = 'Enable Dynamic Tree', | |
690 | variable = self.dyn, | |
691 | command = self.ChkBtnHandler) | |
692 | self.dCheckButton.pack() | |
693 | ||
694 | ||
695 | def dynSelected(self, node): | |
696 | self.dCheckButton.configure(background = self.dLeaf.configure('selectbackground')[4]) | |
697 | printselected(node) | |
698 | ||
699 | def dynDeselected(self, node): | |
700 | self.dCheckButton.configure(background = self.dLeaf.configure('background')[4]) | |
701 | printdeselected(node) | |
702 | ||
703 | def ChkBtnHandler(self): | |
704 | self.dLeaf.select() | |
705 | if self.dyn.get() == 1: | |
706 | self.dtree = dynamicTreeRootNode.insertbranch(label = 'Dynamic Tree Node', | |
707 | selectcommand = printselected, | |
708 | deselectcommand = printdeselected, | |
709 | expandcommand = printexpanded, | |
710 | collapsecommand = printcollapsed, | |
711 | before = dynamicTreePosNode) | |
712 | self.dtree.addleaf(label = 'Dynamic Leaf 1', | |
713 | selectcommand = printselected, | |
714 | deselectcommand = printdeselected) | |
715 | self.dtree.addleaf(label = 'Dynamic Leaf 2', | |
716 | selectcommand = printselected, | |
717 | deselectcommand = printdeselected) | |
718 | else: | |
719 | if self.dtree != None: | |
720 | dynamicTreeRootNode.delete(self.dtree) | |
721 | self.dtree = None | |
722 | ||
723 | ||
724 | foo = dynTree() | |
725 | ||
726 | ||
727 | treeBrowser.pack(expand = 1, fill='both') | |
728 | ||
729 | exitButton = Tkinter.Button(rootWin, text="Quit", command=rootWin.quit) | |
730 | exitButton.pack() | |
731 | ||
732 | rootWin.mainloop() |