1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 import Tkdnd
80 from Tkinter import *
81 import tkFont
82
83
85 """report exception on sys.stderr."""
86 import traceback
87 import sys
88
89 sys.stderr.write("Exception in Tree control callback\n")
90 traceback.print_exc()
91
92
94 """Helper object for add_node() method"""
97
98
100 """Tree helper class that's instantiated for each element in the tree. It
101 has several useful attributes:
102 parent_node - immediate parent node
103 id - id assigned at creation
104 expanded_icon - image displayed when folder is expanded to display
105 children
106 collapsed_icon - image displayed when node is not a folder or folder is
107 collapsed.
108 parent_widget - reference to tree widget that contains node.
109 expandable_flag - is true when node is a folder that may be expanded or
110 collapsed.
111 expanded_flag - true to indicate node is currently expanded.
112 h_line - canvas line to left of node image.
113 v_line - canvas line below node image that connects children.
114 indic - expand/collapse canvas image.
115 label - canvas text label
116 symbol - current canvas image
117
118 Please note that methods prefixed PVT_* are not meant to be used by
119 client programs."""
120
121 - def __init__(self, parent_node, id, collapsed_icon, x, y,
122 parent_widget=None, expanded_icon=None, label=None,
123 expandable_flag=0,expanded=0,action=None):
124 """Create node and initialize it. This also displays the node at the
125 given position on the canvas, and binds mouseclicks."""
126
127 self.parent_node=parent_node
128
129 self.id=id
130
131 self.expanded_icon=expanded_icon
132 self.collapsed_icon=collapsed_icon
133 self.action=action
134
135 if parent_widget:
136 self.widget=parent_widget
137 else:
138 self.widget=parent_node.widget
139
140 sw=self.widget
141
142 self.child_nodes=[]
143
144 self.expandable_flag=expandable_flag
145 self.expanded_flag=0
146
147 if parent_node and sw.line_flag:
148 self.h_line=sw.create_line(x, y, x-sw.dist_x, y)
149 else:
150 self.h_line=None
151 self.v_line=None
152
153 self.symbol=sw.create_image(x, y, image=self.collapsed_icon)
154
155 self.indic=None
156 self.font = sw.font
157 if expandable_flag and sw.line_flag and sw.plus_icon and sw.minus_icon:
158 self.indic=sw.create_image(x-sw.dist_x, y, image=sw.plus_icon)
159 if self.action:
160 font = self.font[:2]+('bold',)
161 else:
162 font = self.font
163
164 self.label=sw.create_text(x+sw.text_offset, y, text=label, anchor='w',font=font)
165
166 if self.indic:
167 sw.tag_bind(self.indic, '<1>', self.PVT_click)
168 else:
169 sw.tag_bind(self.symbol, '<1>', self.PVT_click)
170 if self.action:
171 sw.tag_bind(self.label,"<ButtonPress-1>", self.invoke)
172 else:
173 sw.tag_bind(self.label,"<ButtonPress-1>",self.highlight)
174
175
176
177
178
179 - def invoke(self, event=None):
183
186
187
188
189
190
191
193 """Set node's collapsed image"""
194 self.collapsed_icon=icon
195 if not self.expanded_flag:
196 self.widget.itemconfig(self.symbol, image=icon)
197
199 """Set node's expanded image"""
200 self.expanded_icon=icon
201 if self.expanded_flag:
202 self.widget.itemconfig(self.symbol, image=icon)
203
205 """Return node's parent node"""
206 return self.parent_node
207
209 """Return node's previous sibling (the child immediately above it)"""
210 i=self.parent_node.child_nodes.index(self)-1
211 if i >= 0:
212 return self.parent_node.child_nodes[i]
213 else:
214 return None
215
217 """Return node's next sibling (the child immediately below it)"""
218 i=self.parent_node.child_nodes.index(self)+1
219 if i < len(self.parent_node.child_nodes):
220 return self.parent_node.child_nodes[i]
221 else:
222 return None
223
225 """Return next lower visible node"""
226 n=self
227 if n.child_nodes:
228
229 return n.child_nodes[0]
230 while n.parent_node:
231
232 i=n.parent_node.child_nodes.index(n)+1
233 if i < len(n.parent_node.child_nodes):
234 return n.parent_node.child_nodes[i]
235
236 n=n.parent_node
237
238 return self
239
241 """Return next higher visible node"""
242 n=self
243 if n.parent_node:
244 i=n.parent_node.child_nodes.index(n)-1
245 if i < 0:
246 return n.parent_node
247 else:
248 j=n.parent_node.child_nodes[i]
249 return j.PVT_last()
250 else:
251 return n
252
254 """Return list of node's children"""
255 return self.child_nodes[:]
256
258 """Return string containing text of current label"""
259 return self.widget.itemcget(self.label, 'text')
260
262 """Set current text label"""
263 self.widget.itemconfig(self.label, text=label)
264
266 """Returns true if node is currently expanded, false otherwise"""
267 return self.expanded_flag
268
270 """Returns true if node can be expanded (i.e. if it's a folder)"""
271 return self.expandable_flag
272
274 """Return list of IDs of all parents and node ID"""
275 if self.parent_node:
276 return self.parent_node.full_id()+(self.id,)
277 else:
278 return (self.id,)
279
281 """Expand node if possible"""
282 if not self.expanded_flag:
283 self.PVT_set_state(1)
284
286 """Collapse node if possible"""
287 if self.expanded_flag:
288 self.PVT_set_state(0)
289
291 """Delete node from tree. ("me_too" is a hack not to be used by
292 external code, please!)"""
293 sw=self.widget
294 if not self.parent_node and me_too:
295
296 raise ValueError, "can't delete root node"
297 self.PVT_delete_subtree()
298
299 n=self.next_visible()
300 x1, y1=sw.coords(self.symbol)
301 x2, y2=sw.coords(n.symbol)
302 if me_too:
303 dist=y2-y1
304 else:
305 dist=y2-y1-sw.dist_y
306 self.PVT_tag_move(-dist)
307 n=self
308 if me_too:
309 if sw.pos == self:
310
311 sw.move_cursor(self.parent_node)
312 self.PVT_unbind_all()
313 sw.delete(self.symbol)
314 sw.delete(self.label)
315 sw.delete(self.h_line)
316 sw.delete(self.v_line)
317 sw.delete(self.indic)
318 self.parent_node.child_nodes.remove(self)
319
320 n=self.parent_node
321 self.parent_node=None
322 n.PVT_cleanup_lines()
323 n.PVT_update_scrollregion()
324
326 """Insert list of nodes as siblings before this node. Call parent
327 node's add_node() function to generate the list of nodes."""
328 i=self.parent_node.child_nodes.index(self)
329 self.parent_node.PVT_insert(nodes, i, self.prev_visible())
330
332 """Insert list of nodes as siblings after this node. Call parent
333 node's add_node() function to generate the list of nodes."""
334 i=self.parent_node.child_nodes.index(self)+1
335 self.parent_node.PVT_insert(nodes, i, self.PVT_last())
336
338 """Insert list of nodes as children of this node. Call node's
339 add_node() function to generate the list of nodes."""
340 self.PVT_insert(nodes, 0, self)
341
343 """Toggle node's state between expanded and collapsed, if possible"""
344 if self.expandable_flag:
345 if self.expanded_flag:
346 self.PVT_set_state(0)
347 else:
348 self.PVT_set_state(1)
349
350
352 """detect mouse hover for drag'n'drop"""
353 self.widget.target=self
354
356 """Notification that dnd processing has been ended. It DOES NOT imply
357 that we've been dropped somewhere useful, we could have just been
358 dropped into deep space and nothing happened to any data structures,
359 or it could have been just a plain mouse-click w/o any dragging."""
360 if not self.widget.drag:
361
362
363 self.toggle_state()
364 self.widget.drag=0
365
366
367
369 """Return bottom-most node in subtree"""
370 n=self
371 while n.child_nodes:
372 n=n.child_nodes[-1]
373 return n
374
376 """Used by searching functions"""
377 if self.id != search[0]:
378
379 return None
380 if len(search) == 1:
381 return self
382
383 i=map(lambda x: x.id, self.child_nodes)
384
385 try:
386 return self.child_nodes[i.index(search[1])].PVT_find(search[1:])
387 except:
388 return None
389
391 """Create and insert new children. "nodes" is list previously created
392 via calls to add_list(). "pos" is index in the list of children where
393 the new nodes are inserted. "below" is node which new children should
394 appear immediately below."""
395 if not self.expandable_flag:
396 raise TypeError, 'not an expandable node'
397
398 sw=self.widget
399
400 children=[]
401 self.expanded_flag=1
402 sw.itemconfig(self.symbol, image=self.expanded_icon)
403 if sw.minus_icon and sw.line_flag:
404 sw.itemconfig(self.indic, image=sw.minus_icon)
405 if len(nodes):
406
407 below.PVT_tag_move(sw.dist_y*len(nodes))
408
409 xp, dummy=sw.coords(self.symbol)
410 dummy, yp=sw.coords(below.symbol)
411 xp=xp+sw.dist_x
412 yp=yp+sw.dist_y
413
414 if sw.line_flag and not self.v_line:
415 self.v_line=sw.create_line(
416 xp, yp,
417 xp, yp+sw.dist_y*len(nodes))
418 sw.tag_lower(self.v_line, self.symbol)
419 n=sw.node_class
420 for i in nodes:
421
422
423 children.append(
424 n(parent_node=self, expandable_flag=i.flag, label=i.name,
425 id=i.id, collapsed_icon=i.collapsed_icon,
426 expanded_icon=i.expanded_icon, x=xp, y=yp,expanded=i.expanded,action=i.action))
427 yp=yp+sw.dist_y
428 self.child_nodes[pos:pos]=children
429 self.PVT_cleanup_lines()
430 self.PVT_update_scrollregion()
431 sw.move_cursor(sw.pos)
432
434 """Common code forexpanding/collapsing folders. It's not re-entrant,
435 and there are certain cases in which we can be called again before
436 we're done, so we use a mutex."""
437 while self.widget.spinlock:
438 pass
439 self.widget.spinlock=1
440
441 if state:
442 self.child_nodes=[]
443 self.widget.new_nodes=[]
444 if self.widget.get_contents_callback:
445
446 try:
447 self.widget.get_contents_callback(self)
448 except:
449 report_callback_exception()
450 self.PVT_insert(self.widget.new_nodes, 0, self)
451
452 else:
453 self.expanded_flag=0
454 self.widget.itemconfig(self.symbol, image=self.collapsed_icon)
455 if self.indic:
456 self.widget.itemconfig(self.indic, image=self.widget.plus_icon)
457 self.delete(0)
458
459 self.widget.spinlock=0
460
462 """Resize connecting lines"""
463 if self.widget.line_flag:
464 n=self
465 while n:
466 if n.child_nodes:
467 x1, y1=self.widget.coords(n.symbol)
468 x2, y2=self.widget.coords(n.child_nodes[-1].symbol)
469 self.widget.coords(n.v_line, x1, y1, x1, y2)
470 n=n.parent_node
471
476
478 """Recursively delete subtree & clean up cyclic references to make
479 garbage collection happy"""
480 sw=self.widget
481 sw.delete(self.v_line)
482 self.v_line=None
483 for i in self.child_nodes:
484
485 i.PVT_delete_subtree()
486 i.PVT_unbind_all()
487
488 sw.delete(i.symbol)
489 sw.delete(i.label)
490 sw.delete(i.h_line)
491 sw.delete(i.v_line)
492 sw.delete(i.indic)
493
494 i.parent_node=None
495
496 if sw.pos in self.child_nodes:
497 sw.move_cursor(self)
498
499 self.child_nodes=[]
500
502 """Unbind callbacks so node gets garbage-collected. This wasn't easy
503 to figure out the proper way to do this. See also tag_bind() for the
504 Tree widget itself."""
505 for j in (self.symbol, self.label, self.indic, self.h_line,
506 self.v_line):
507 for k in self.widget.bindings.get(j, ()):
508 self.widget.tag_unbind(j, k[0], k[1])
509
511 """Move everything below current icon, to make room for subtree using
512 the Disney magic of item tags. This is the secret of making
513 everything as fast as it is."""
514
515 bbox1=self.widget.bbox(self.widget.root.symbol, self.label)
516 bbox2=self.widget.bbox('all')
517 self.widget.dtag('move')
518 self.widget.addtag('move', 'overlapping',
519 bbox2[0], bbox1[3], bbox2[2], bbox2[3])
520
521 self.widget.dtag(self.widget.cursor_box, 'move')
522 self.widget.dtag(self.symbol, 'move')
523 self.widget.dtag(self.label, 'move')
524
525 self.widget.move('move', 0, dist)
526
528 """Handle mouse clicks by kicking off possible drag'n'drop
529 processing"""
530 if self.widget.drop_callback:
531 if Tkdnd.dnd_start(self, event):
532 x1, y1, x2, y2=self.widget.bbox(self.symbol)
533 self.x_off=(x1-x2)/2
534 self.y_off=(y1-y2)/2
535 else:
536
537 self.widget.drag=0
538 self.dnd_end(None, None)
539
540
542
543 - def __init__(self, master, root_id, root_label='',
544 get_contents_callback=None, dist_x=15, dist_y=15,
545 text_offset=10, line_flag=1, expanded_icon=None,
546 collapsed_icon=None, regular_icon=None, plus_icon=None,
547 minus_icon=None, node_class=Node, root_action=None,drop_callback=None,font=None,
548 *args, **kw_args):
549
550
551 Canvas.__init__(self, master, *args, **kw_args)
552
553
554 self.node_class=node_class
555
556 self.bindings={}
557
558 self.spinlock=0
559
560 self.drag=0
561
562
563
564
565
566
567
568
569
570 self.expanded_icon=expanded_icon
571
572
573
574
575
576
577
578 self.collapsed_icon=collapsed_icon
579
580
581
582
583
584
585
586 self.regular_icon=regular_icon
587
588 if plus_icon == None:
589 self.plus_icon=PhotoImage(
590 data='R0lGODdhCQAJAPEAAAAAAH9/f////wAAACwAAAAACQAJAAAC' \
591 'FIyPoiu2sJyCyoF7W3hxz850CFIA\nADs=')
592 else:
593 self.plus_icon=plus_icon
594 if minus_icon == None:
595 self.minus_icon=PhotoImage(
596 data='R0lGODdhCQAJAPEAAAAAAH9/f////wAAACwAAAAACQAJAAAC' \
597 'EYyPoivG614LAlg7ZZbxoR8UADs=')
598 else:
599 self.minus_icon=minus_icon
600
601 self.font = font
602
603
604 treeFont = tkFont.Font(font=font)
605
606
607 self.dist_x=dist_x
608
609 self.dist_y=treeFont.metrics()['linespace'] + 5
610
611 self.text_offset=text_offset
612
613 self.line_flag=line_flag
614
615 self.get_contents_callback=get_contents_callback
616
617 self.drop_callback=drop_callback
618
619 self.root=node_class(parent_node=None, label=root_label,
620 id=root_id, expandable_flag=1,
621 collapsed_icon=self.collapsed_icon,
622 expanded_icon=self.expanded_icon,
623 x=dist_x, y=dist_y, parent_widget=self,action=root_action)
624
625 x1, y1, x2, y2=self.bbox('all')
626 self.configure(scrollregion=(x1, y1, x2+5, y2+5))
627
628 self.cursor_box=self.create_rectangle(0, 0, 0, 0)
629 self.move_cursor(self.root)
630
631 self.bind('<Enter>', self.PVT_mousefocus)
632
633
634
635 self.bind('<Next>', self.pagedown)
636 self.bind('<Prior>', self.pageup)
637
638 self.bind('<Down>', self.next)
639 self.bind('<Up>', self.prev)
640
641 self.bind('<Left>', self.ascend)
642
643 self.bind('<Right>', self.descend)
644
645 self.bind('<Home>', self.first)
646 self.bind('<End>', self.last)
647
648 self.bind('<Key-space>', self.toggle)
649
650
651
653 """Soak up event argument when moused-over"""
654 self.focus_set()
655
656
657 - def tag_bind(self, tag, seq, *args, **kw_args):
658 """Keep track of callback bindings so we can delete them later. I
659 shouldn't have to do this!!!!"""
660
661 func_id=apply(Canvas.tag_bind, (self, tag, seq)+args, kw_args)
662
663 self.bindings[tag]=self.bindings.get(tag, [])+[(seq, func_id)]
664
665 - def add_list(self, list=None, name=None, id=None, flag=0,
666 expanded_icon=None, collapsed_icon=None, expanded=0, action=None):
667 """Add node construction info to list"""
668 n=Struct()
669 n.name=name
670 n.id=id
671 n.flag=flag
672 n.action=action
673 n.expanded=expanded
674 if collapsed_icon:
675 n.collapsed_icon=collapsed_icon
676 else:
677 if flag:
678
679 n.collapsed_icon=self.collapsed_icon
680 else:
681
682 n.collapsed_icon=self.regular_icon
683 if flag:
684 if expanded_icon:
685 n.expanded_icon=expanded_icon
686 else:
687 n.expanded_icon=self.expanded_icon
688 else:
689
690 n.expanded_icon=None
691 if list == None:
692 list=[]
693 list.append(n)
694 return list
695
696 - def add_node(self, name=None, id=None, flag=0, expanded_icon=None,
697 collapsed_icon=None,expanded=0,action=None):
698 """Add a node during get_contents_callback()"""
699 self.add_list(self.new_nodes, name, id, flag, expanded_icon,
700 collapsed_icon,expanded,action)
701
705
707 """Return node under cursor"""
708 return self.pos
709
710 - def see(self, *items):
711 """Scroll (in a series of nudges) so items are visible"""
712 x1, y1, x2, y2=apply(self.bbox, items)
713 while x2 > self.canvasx(0)+self.winfo_width():
714 old=self.canvasx(0)
715 self.xview('scroll', 1, 'units')
716
717 if old == self.canvasx(0):
718 break
719 while y2 > self.canvasy(0)+self.winfo_height():
720 old=self.canvasy(0)
721 self.yview('scroll', 1, 'units')
722 if old == self.canvasy(0):
723 break
724
725 while x1 < self.canvasx(0):
726 old=self.canvasx(0)
727 self.xview('scroll', -1, 'units')
728 if old == self.canvasx(0):
729 break
730 while y1 < self.canvasy(0):
731 old=self.canvasy(0)
732 self.yview('scroll', -1, 'units')
733 if old == self.canvasy(0):
734 break
735
737 """Move cursor to node"""
738 self.pos=node
739 x1, y1, x2, y2=self.bbox(node.symbol, node.label)
740 self.coords(self.cursor_box, x1-1, y1-1, x2+1, y2+1)
741 self.see(node.symbol, node.label)
742
743 - def toggle(self, event=None):
744 """Expand/collapse subtree"""
745 self.pos.toggle_state()
746
747 - def next(self, event=None):
750
751 - def prev(self, event=None):
754
755 - def ascend(self, event=None):
756 """Move to immediate parent"""
757 if self.pos.parent_node:
758
759 self.move_cursor(self.pos.parent_node)
760
762 """Move right, expanding as we go"""
763 if self.pos.expandable_flag:
764 self.pos.expand()
765 if self.pos.child_nodes:
766
767 self.move_cursor(self.pos.child_nodes[0])
768 return
769
770 self.next()
771
772 - def first(self, event=None):
773 """Go to root node"""
774
775 self.move_cursor(self.root)
776
777 - def last(self, event=None):
781
782 - def pageup(self, event=None):
783 """Previous page"""
784 n=self.pos
785 j=self.winfo_height()/self.dist_y
786 for i in range(j-3):
787 n=n.prev_visible()
788 self.yview('scroll', -1, 'pages')
789 self.move_cursor(n)
790
791 - def pagedown(self, event=None):
792 """Next page"""
793 n=self.pos
794 j=self.winfo_height()/self.dist_y
795 for i in range(j-3):
796 n=n.next_visible()
797 self.yview('scroll', 1, 'pages')
798 self.move_cursor(n)
799
800
802 """Determine drag location in canvas coordinates. event.x & event.y
803 don't seem to be what we want."""
804
805 x_org=self.winfo_rootx()
806 y_org=self.winfo_rooty()
807
808
809 x=self.canvasx(event.x_root-x_org)
810 y=self.canvasy(event.y_root-y_org)
811 return x, y
812
814 """Accept dnd messages, i.e. we're a legit drop target, and we do
815 implement d&d functions."""
816 self.target=None
817 return self
818
820 """Get ready to drag or drag has entered widget (create drag
821 object)"""
822
823 self.drag=1
824 x, y=self.where(event)
825 x1, y1, x2, y2=source.widget.bbox(source.symbol, source.label)
826 dx, dy=x2-x1, y2-y1
827
828 if source.expanded_flag:
829 self.dnd_symbol=self.create_image(x, y,
830 image=source.expanded_icon)
831 else:
832 self.dnd_symbol=self.create_image(x, y,
833 image=source.collapsed_icon)
834 self.dnd_label=self.create_text(x+self.text_offset, y,
835 text=source.get_label(),
836 justify='left',
837 anchor='w')
838
840 """Move drag icon"""
841 self.drag=1
842 x, y=self.where(event)
843 x1, y1, x2, y2=self.bbox(self.dnd_symbol, self.dnd_label)
844 self.move(self.dnd_symbol, x-x1+source.x_off, y-y1+source.y_off)
845 self.move(self.dnd_label, x-x1+source.x_off, y-y1+source.y_off)
846
848 """Finish dragging or drag has left widget (destroy drag object)"""
849 self.delete(self.dnd_symbol)
850 self.delete(self.dnd_label)
851
853 """Object has been dropped here"""
854
855 self.dnd_leave(source, event)
856
857
858
859 self.update()
860 if not self.target:
861
862 return
863
864 if self.drop_callback:
865 try:
866
867
868
869 self.drop_callback(source, self.target)
870 except:
871 report_callback_exception()
872
873
874
875 if __name__ == '__main__':
876 import os
877 import sys
878
879
880
881
882
883 - def get_contents(node):
884 path=apply(os.path.join, node.full_id())
885 for filename in os.listdir(path):
886 full=os.path.join(path, filename)
887 name=filename
888 folder=0
889 if os.path.isdir(full):
890
891 folder=1
892 elif not os.path.isfile(full):
893
894 name=name+' (special)'
895 if os.path.islink(full):
896
897 name=name+' (link to '+os.readlink(full)+')'
898 node.widget.add_node(name=name, id=filename, flag=folder)
899
900 root=Tk()
901 root.title(os.path.basename(sys.argv[0]))
902 tree=os.sep
903 if sys.platform == 'win32':
904
905
906
907 tree='C:'+os.sep
908
909
910 t=Tree(master=root,
911 root_id=tree,
912 root_label=tree,
913 get_contents_callback=get_contents,
914 width=300)
915 t.grid(row=0, column=0, sticky='nsew')
916
917
918 root.grid_rowconfigure(0, weight=1)
919 root.grid_columnconfigure(0, weight=1)
920
921
922 sb=Scrollbar(root)
923 sb.grid(row=0, column=1, sticky='ns')
924 t.configure(yscrollcommand=sb.set)
925 sb.configure(command=t.yview)
926
927 sb=Scrollbar(root, orient=HORIZONTAL)
928 sb.grid(row=1, column=0, sticky='ew')
929 t.configure(xscrollcommand=sb.set)
930 sb.configure(command=t.xview)
931
932
933 t.focus_set()
934
935
936 Button(root, text='Quit', command=root.quit).grid(row=2, column=0,
937 columnspan=2)
938
939
940 t.root.expand()
941
942 root.mainloop()
943
944
945
946
947
951
953 for c,index in zip(self['children'],range(len(self['children']))):
954 if c['id'] == child['id']:
955 return index
956 return -1
957
961
962 - def delete(self,recursive=True):
971
972
973
974 - def addChild(self,tree,node_id=None,label=None,isLeaf=False,debug=False):
975 child = EasyNode({'parent':self,'children':[],'label':label,'id':node_id,'isLeaf':isLeaf,'action':None,'expanded':False,'widget':tree})
976 if not node_id:
977 node_id = id(child)
978 child['id'] = node_id
979 if not label:
980 label = node_id
981 child['label'] = label
982
983 self['children'].append(child)
984
985 if debug:
986 print "Adding child node to parent %s" % self['label']
987 print "\tName: %s" % label
988
989
990 pNode = tree.find_full_id(self.full_id())
991
992 if pNode and pNode.expanded():
993 if debug:
994 print "\tParent is in an expanded state"
995 oldersibling = None
996 if len(pNode.children()) > 0:
997 oldersibling = pNode.children()[len(pNode.children())-1]
998 oldersibling.insert_after(tree.add_list(list=None,name=child['label'],id=child['id'],flag=int(not child['isLeaf']),action=child['action']))
999 else:
1000 pNode.insert_children(tree.add_list(list=None,name=child['label'],id=child['id'],flag=int(not child['isLeaf']),action=child['action']))
1001
1002 return child
1003
1005
1006 if not tree:
1007 tree = self['widget']
1008
1009
1010 selected=(tree.selected_node().full_id() == self.full_id())
1011
1012
1013 pNode = tree.find_full_id(self.full_id())
1014 if pNode:
1015 parent = pNode.parent()
1016 if parent:
1017 siblings = parent.children()
1018 index = siblings.index(pNode)
1019 oldersibling = None
1020 youngersibling = None
1021 if index > 0:
1022 oldersibling = siblings[index-1]
1023 if not index == len(siblings)-1:
1024 youngersibling = siblings[index+1]
1025 pNode.delete()
1026 if oldersibling:
1027 oldersibling.insert_after(tree.add_list(list=None,name=self['label'],id=self['id'],flag=int(not self['isLeaf']),action=self['action']))
1028 elif youngersibling:
1029 youngersibling.insert_before(tree.add_list(list=None,name=self['label'],id=self['id'],flag=int(not self['isLeaf']),action=self['action']))
1030 else:
1031 parent.insert_children(tree.add_list(list=None,name=self['label'],id=self['id'],flag=int(not self['isLeaf']),action=self['action']))
1032 else:
1033
1034 pNode.set_label(self['label'])
1035 pNode.action = self['action']
1036 if selected:
1037 node = tree.find_full_id(self.full_id())
1038 tree.move_cursor(node)
1039
1040
1042 if self['parent']:
1043 return self['parent'].full_id() + (self['id'],)
1044 else:
1045 return (self['id'],)
1046
1048 - def __init__(self, master, root_id='root', root_label='root',
1049 get_contents_callback=None, dist_x=15, dist_y=30,
1050 text_offset=5, line_flag=1, expanded_icon=None,
1051 collapsed_icon=None, regular_icon=None, plus_icon=None,
1052 minus_icon=None, node_class=Node, root_action=None,drop_callback=None,
1053 *args, **kw_args):
1054
1055 if not get_contents_callback:
1056 get_contents_callback = self._getnodes_
1057
1058 Tree.__init__(self,master,root_id,root_label=root_label,
1059 get_contents_callback=get_contents_callback,dist_x=dist_x,dist_y=dist_y,
1060 text_offset=text_offset,line_flag=line_flag,expanded_icon=expanded_icon,
1061 collapsed_icon=collapsed_icon,regular_icon=regular_icon,plus_icon=plus_icon,
1062 minus_icon=minus_icon,node_class=node_class,root_action=root_action,drop_callback=drop_callback,
1063 *args, **kw_args)
1064
1065 self.easyRoot = EasyNode({'parent':None,'children':[],'label':root_label,'id':root_id,'expanded':False,'widget': self})
1066
1070
1072 if len(id) == 0:
1073 return search_node
1074 else:
1075 for child in search_node['children']:
1076 if child['id'] == id[0]:
1077 return self._find_internal_node_(id[1:],child)
1078 return None
1079
1095