Salome HOME
PN
[tools/eficas.git] / Editeur / treewidget.py
1 # -*- coding: utf-8 -*-
2 #            CONFIGURATION MANAGEMENT OF EDF VERSION
3 # ======================================================================
4 # COPYRIGHT (C) 1991 - 2002  EDF R&D                  WWW.CODE-ASTER.ORG
5 # THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
6 # IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
7 # THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
8 # (AT YOUR OPTION) ANY LATER VERSION.
9 #
10 # THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
11 # WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
12 # MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
13 # GENERAL PUBLIC LICENSE FOR MORE DETAILS.
14 #
15 # YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
16 # ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
17 #    1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
18 #
19 #
20 # ======================================================================
21 import os,sys,string,re,types,traceback
22 from Tkinter import *
23
24
25 import fontes
26 import images
27 from Ihm import CONNECTOR
28
29 #
30 __version__="$Name:  $"
31 __Id__="$Id: treewidget.py,v 1.29 2005/11/29 17:39:50 eficas Exp $"
32 #
33
34 Fonte_Standard = fontes.standard
35
36 class Tree :
37     def __init__(self,appli,jdc_item,scrolledcanvas,command = None,rmenu=None):
38         self.item = jdc_item
39         self.scrolledcanvas = scrolledcanvas
40         self.canvas = self.scrolledcanvas.component('canvas')
41         self.id_up=self.canvas.bind("<F11>", self.page_up)
42         self.id_down=self.canvas.bind("<F12>", self.page_down)
43         self.id_um=self.canvas.bind("<Key-Left>", self.mot_up)
44         self.id_dm=self.canvas.bind("<Key-Right>", self.mot_down)
45         self.id_s=self.canvas.bind("<1>", self.canvas_select)             
46         self.tree = self
47         self.command = command
48         self.rmenu=rmenu
49         self.appli = appli
50         self.parent = None
51         self.racine = self
52         self.node_selected = None
53         self.build_children()
54
55     def canvas_select(self,event):
56         self.canvas.focus_set()
57
58     def page_up(self,event):
59         event.widget.yview_scroll(-1, "page")
60
61     def page_down(self,event):
62         event.widget.yview_scroll(1, "page")
63         
64     def unit_up(self,event):
65         event.widget.yview_scroll(-1, "unit")
66
67     def unit_down(self,event):
68         event.widget.yview_scroll(1, "unit")              
69
70     def mot_down(self,event):
71         self.select_next(None)
72         self.canvas.focus_set()
73
74     def mot_down_force(self):
75         self.select_next(None)
76         self.canvas.focus_set()
77
78     def mot_up(self,event):
79         self.node_selected.select_mot_previous()
80         self.canvas.focus_set()
81
82     def mot_up_force(self):
83         self.node_selected.select_mot_prev()
84         self.canvas.focus_set()
85
86     def deplieReplieNode(self):
87         self.node_selected.deplieReplieNode()
88
89     def build_children(self):
90         """ Construit la liste des enfants de self """
91         self.children = []
92         child = self.item.itemNode(self,self.item,self.command,self.rmenu)
93         self.children.append(child)
94         child.state='expanded'
95
96     def draw(self):
97         """ Dessine l'arbre """
98         lasty = 8
99         x = 5
100         for child in self.children:
101             child.draw(x,lasty)
102             lasty = child.lasty + 15
103         self.children[0].select()
104         self.resizescrollregion()
105
106     def deselectall(self):
107         """ déselectionne tous les éléments de l'arbre """
108         if self.node_selected :
109             self.node_selected.deselect()
110             
111     def update(self):
112         """ Update tous les éléments de l'arbre """
113         for child in self.children:
114             child.update()
115
116     def supprime(self):
117         """ supprime tous les éléments de l'arbre """
118         #print "supprime",self
119         self.canvas.unbind("<Key-Prior>",self.id_up)
120         self.canvas.unbind("<Key-Next>",self.id_down)
121         self.canvas.unbind("<Key-Left>",self.id_um)
122         self.canvas.unbind("<Key-Right>",self.id_dm)
123         self.canvas.unbind("<1>",self.id_s)             
124         self.tree = None
125         self.racine = None
126         self.node_selected = None
127         self.item = None
128         self.scrolledcanvas = None
129         self.canvas = None
130         self.command = None
131         self.rmenu=None
132         for child in self.children:
133             child.supprime()
134         self.children=[]
135
136     def update_valid(self) :
137         """Cette methode a pour but de mettre a jour la validite du noeud
138            et de propager la demande de mise à jour à son parent
139         """
140         pass
141
142     def resizescrollregion(self):
143         x0,y0,x1,y1=self.canvas.bbox(ALL)
144         # On ajoute une marge approximativement de la moitié du canvas
145         y1=y1+self.canvas.winfo_height()/2
146         self.canvas.configure(scrollregion = (x0,y0,x1,y1))
147
148     def select_next(self,event):
149         self.node_selected.select_next()
150         self.canvas.focus_set()
151
152     def select_previous(self,event):
153         self.node_selected.select_previous()
154
155     def see(self,items):
156         x1, y1, x2, y2=apply(self.canvas.bbox, items)
157         while x2 > self.canvas.canvasx(0)+self.canvas.winfo_width():
158             old=self.canvas.canvasx(0)
159             self.canvas.xview_scroll( 1, 'units')
160             # avoid endless loop if we can't scroll
161             if old == self.canvas.canvasx(0):
162                 break
163         while y2 > self.canvas.canvasy(0)+self.canvas.winfo_height():
164             old=self.canvas.canvasy(0)
165             self.canvas.yview_scroll( 1, 'units')
166             if old == self.canvas.canvasy(0):
167                 break
168         # done in this order to ensure upper-left of object is visible
169         while x1 < self.canvas.canvasx(0):
170             old=self.canvas.canvasx(0)
171             self.canvas.xview_scroll( -1, 'units')
172             if old == self.canvas.canvasx(0):
173                 break
174         while y1 < self.canvas.canvasy(0):
175             old=self.canvas.canvasy(0)
176             self.canvas.yview_scroll( -1, 'units')
177             if old == self.canvas.canvasy(0):
178                 break
179
180     #def __del__(self):
181     #   print "__del__",self
182
183             
184 class Node :
185     def __init__(self,parent,item,command=None,rmenu=None):
186         self.parent = parent
187         self.item = item
188         self.connect()
189         self.command = command
190         self.rmenu=rmenu
191         self.tree = self.parent.tree
192         self.appli = self.parent.appli
193         self.canvas = self.parent.canvas
194         self.init()
195
196     def init(self):
197         self.state='collapsed'
198         self.displayed = 0
199         self.selected = 0
200         self.x = self.y  =None
201         self.lasty = 0
202         self.children = None
203         self.id = []
204         if self.parent is self.tree:
205            self.racine=self
206         else:
207            self.racine = self.parent.racine
208            
209     def connect(self):
210         self.item.connect("add",self.onAdd,())
211         self.item.connect("supp",self.onSupp,())
212         self.item.connect("valid",self.onValid,())
213
214     #def __del__(self):
215     #    print "__del__",self
216
217     def force_select(self):
218         if self.selected:
219            # le noeud est selectionné. On force la reconstruction du panel associé
220            if self.command:apply(self.command,(None,))
221            self.select()
222
223     def onValid(self):
224         #print "onValid : l'item a changé de validité ",self.item,self.item.object,self.item.object.isvalid()
225         self.update_node_valid()
226         self.update_node_label()
227         self.update_node_texte()
228         if self.selected and self.command:
229            self.command(self)
230
231     def onAdd(self,objet):
232         #print "onAdd : un objet a été ajouté aux fils de l'item ",self.item.object,objet
233         self.expand_node()
234         old_nodes=self.children
235         self.update_nodes()
236         self.redraw_children(old_nodes)
237         self.force_select()
238
239     def onSupp(self,objet):
240         #print "onSupp : un objet a été supprimé des fils de l'item ",self.item.object,objet
241         self.expand_node()
242         old_nodes=self.children
243         self.update_nodes()
244         self.redraw_children(old_nodes)
245         self.force_select()
246
247     def update_nodes(self):
248         #print "update_nodes",self
249         newnodes=[]
250         inodes=iter(self.children)
251         sublist=self.item._GetSubList()
252         iliste=iter(sublist)
253
254         while(1):
255            old_item=item=None
256            for node in inodes:
257               old_item=node.item
258               if old_item in sublist:break
259               #print "item supprime",old_item
260            for item in iliste:
261               if item is old_item:break
262               #print "item ajoute",item
263               child = item.itemNode(self,item,self.command,self.rmenu)
264               newnodes.append(child)
265
266            if old_item is None and item is None:break
267            if old_item is item:
268               #print "item conserve",item
269               newnodes.append(node)
270
271         self.children=newnodes
272         self.connect()
273
274     def supprime(self):
275         #print "supprime",self
276         self.efface_node()
277         self.racine = None
278         self.command = None
279         self.rmenu=None
280         if not self.children : return
281         for child in self.children:
282             child.supprime()
283         self.children=None
284
285     def redraw_children(self,old_nodes):
286         #print "redraw_children",old_nodes
287         #print self.children
288         y = self.y + 20
289         x = self.x + 15
290         supp_nodes=[]
291
292         inodes=iter(old_nodes)
293         iliste=iter(self.children)
294         # on parcourt la liste des anciens noeuds (node)
295         # et la liste des nouveaux noeuds (new_node) en parallele (iterateurs)
296
297         while(1):
298            new_node=node=None
299            for node in inodes:
300               #print "ancien noeud",node
301               if node in self.children:break # ancien noeud toujours present
302               #print "noeud supprime",node,node.item.GetLabelText()[0]
303               dy=node.y-node.lasty -20
304               #print "deplacer noeuds",y,dy
305               node.move_nodes(y,dy)
306               node.supprime()
307               #supp_nodes.append(node)
308
309            for new_node in iliste:
310               #print "nouveau noeud",new_node
311               if new_node in old_nodes: break # nouveau noeud deja present
312               #print "noeud ajoute",new_node,new_node.item.GetLabelText()[0]
313               y=self.draw_node(new_node,x,y)
314
315            if node is None and new_node is None : break
316
317            if node is new_node: # ancien noeud
318               #print "noeud conserve",node
319               node.update_node_label()
320               y=y+node.lasty-node.y +20
321
322         self.racine.update_coords()
323         self.canvas.delete('line')
324         self.racine.trace_ligne()
325         self.tree.resizescrollregion()
326         # Mettre à 1 pour verifier les cycles entre objets node
327         #withCyclops=0
328         #if withCyclops:
329            #from Misc import Cyclops
330            #z = Cyclops.CycleFinder()
331            #print supp_nodes
332            #for o in supp_nodes:
333              #z.register(o)
334            #del supp_nodes
335            #del o
336            #z.find_cycles()
337            #z.show_stats()
338            #z.show_cycles()
339
340     def tag_move_nodes(self,y):
341         """ Marque pour deplacement tous les noeuds au dela de l'ordonnée y """
342         #print "tag_move_nodes",y
343         self.canvas.dtag(ALL,'move')
344         # on marque tous les ids au dela de y
345         x0, y0, x1, y1 = self.canvas.bbox(ALL)
346         if y > y1: # pas d'objet a deplacer
347            return
348         self.canvas.addtag_overlapping('move',x0,y,x1,y1)
349
350     def move_nodes(self,y,dy):
351         """ Déplace de l'incrément dy les noeuds au dela de l'ordonnée y """
352         #print "move_nodes",y,dy
353         self.tag_move_nodes(y)
354         # on déplace tous les items de dy
355         self.canvas.move('move',0,dy)
356
357     def draw_node(self,new_node,x,y):
358         """ Dessine le noeud new_node en x,y en deplacant les noeuds existants
359             en y et au dela
360             Retourne la position du premier des noeuds deplaces
361         """
362         #print "draw_node",new_node,x,y
363         self.tag_move_nodes(y)
364         #if new_node.item.isactif():
365            #new_node.state = 'expanded'
366         new_node.state = 'expanded'
367         new_node.draw(x,y)
368         dy=(new_node.get_nb_children()+1)*20
369         #print "deplacer noeuds",y,dy
370         self.canvas.move('move',0,dy)
371         return new_node.lasty+20
372
373     def build_children(self):
374         """ Construit la liste des enfants de self """
375         self.children = []
376         sublist = self.item._GetSubList()
377         if not sublist : return
378         for item in sublist :
379             child = item.itemNode(self,item,self.command,self.rmenu)
380             self.children.append(child)
381             
382     #-----------------------------------------------
383     # Méthodes de sélection/déselection d'un noeud
384     #-----------------------------------------------
385     
386     def select(self, event=None):
387         """
388         Rend le noeud courant (self) sélectionné et déselectionne
389         tous les autres
390         """
391         #print "SELECT",self
392         if not self.children : self.build_children()
393         self.tree.deselectall()
394         self.selected = 1
395         self.tree.node_selected = self
396         if self.command:apply(self.command,(self,))
397         self.highlight()
398         self.make_visible()
399
400     def deselect(self, event=None):
401         """ Déselectionne self """
402         self.selected = 0
403         if self.displayed == 1 : self.dehighlight()
404             
405     def make_visible(self):
406         """ Rend l'objet self visible cad déplace le scroll pour que self soit dans
407             la fenêtre de visu
408         """
409         lchild=self.last_child()
410         self.tree.see((self.image_id,lchild.image_id))
411         
412     def select_next(self,ind=0):
413         """ on doit chercher à sélectionner dans l'ordre:
414                 - son premier fils s'il est affiché
415                 - son frère cadet s'il existe
416                 - son oncle (benjamin de son père)
417                 - ... appel récursif ...
418         """
419         if self.state=='expanded' and len(self.children) > ind:
420             self.children[ind].select()
421         else :
422             index = self.parent.children.index(self) + 1
423             try :
424               if isinstance(self.parent,TREE) :
425                 try:
426                     self.children[ind].select()
427                 except:
428                     self.children[0].select()
429             except :
430                 if self.parent is self.tree:
431                    pass
432                 else :
433                    self.parent.select_next(index)
434
435     def select_mot_prev(self):
436         index = self.parent.children.index(self) - 1
437         try :
438            if index > -1  :
439               self.parent.children[index].select()
440               if self.parent.children[index].state=="expanded":
441                  print len(self.parent.children[index].children)
442                  if len(self.parent.children[index].children)!=0 :
443                     max=len(self.parent.children[index].children) - 1
444                     self.parent.children[index].children[max].select()
445                  else :
446                     self.parent.children[index].select()
447               else :
448                  self.parent.children[index].select()
449            elif self.parent is self.tree:
450               pass
451            else :
452               self.parent.select()
453         except:
454             if self.parent is self.tree:
455                    pass
456             else :
457                self.parent.select_previous()
458
459         
460     def select_mot_previous(self):
461         index = self.parent.children.index(self) - 1
462         try :
463             if index > -1  :
464                self.parent.children[index].select()
465             elif self.parent is self.tree:
466                pass
467             else :
468                self.parent.select()
469         except:
470             if self.parent is self.tree:
471                    pass
472             else :
473                self.parent.select_previous()
474
475     def select_previous(self):
476         """ on doit d'abord sélectionner(dans l'ordre) :
477              - son frère aîné
478              - son père
479         """
480         index = self.parent.children.index(self) - 1
481         try :
482             self.parent.children[index].select()
483         except:
484             #self.parent.select()
485             if self.parent is self.tree:
486                    pass
487             else :
488                self.parent.select_previous()
489
490     def popup(self,event=None):
491         """
492             Declenche le traitement associé au clic droit de la souris
493             sur l'icone du Node
494         """
495         if not self.rmenu:return
496         apply(self.rmenu,(self,event))
497
498     #-----------------------------------------------
499     # Méthodes de recherche d'informations
500     #-----------------------------------------------
501     def geticonimage(self,name=None):
502         """
503         Retourne l'image qui doit être associée à self
504         """
505         if not name :
506             name = self.item.GetIconName()
507         if not name or name == 'aucune' :
508             return None
509         return images.get_image(name)
510
511     def get_nb_children(self):
512         """ Retourne le nombre d'enfants affichés de self """
513         nb = 0
514         if self.state =='collapsed' :  return nb
515         for child in self.children :
516             nb = nb + 1 + child.get_nb_children()
517         return nb
518
519     def get_liste_id(self):
520         """ Retourne la liste de tous les id (filiation comprise) de self """
521         liste = self.id
522         for child in self.children:
523             liste.extend(child.get_liste_id())
524         return liste
525
526     def get_node_fils(self,name) :
527         """ Retourne le fils de self de nom name s'il existe"""
528         for child in self.children:
529             if child.item.get_nom() == name: return child
530         return None
531
532     #-----------------------------------------------
533     # Méthodes d'affichage d'un noeud
534     #-----------------------------------------------
535     def draw(self,x,y):
536         """ Permet de tracer le noeud self """
537         # le début du noeud est en x,y
538         self.x = x
539         self.y = y
540         self.lasty = y
541         self.displayed = 1
542         self.id=[]
543         # choix de l'icone à afficher : + ou -
544         if self.item.IsExpandable():
545             if self.state == 'expanded':
546                 iconname = "minusnode"
547                 callback = self.collapse
548             else:
549                 iconname = "plusnode"
550                 callback = self.expand
551             image = self.geticonimage(name=iconname)
552             self.icone_id = self.canvas.create_image(self.x, self.y, image=image)
553             self.callback_id=self.canvas.tag_bind(self.icone_id, "<1>", callback)
554             self.id.append(self.icone_id)
555         # création de la ligne horizontale
556         self.ligne_id = self.canvas.create_line(self.x,self.y,self.x+10,self.y)
557         self.id.append(self.ligne_id)
558         self.canvas.tag_lower(self.ligne_id)
559         # affichage de l'icone (carre ,rond, ovale ...) de couleur
560         image = self.geticonimage()
561         if image != None :
562             self.image_id = self.canvas.create_image(self.x+15,self.y,image = image)
563             self.select_id2=self.canvas.tag_bind(self.image_id,"<1>",self.select)
564             self.popup_id2=self.canvas.tag_bind(self.image_id,"<3>",self.popup)
565             self.id.append(self.image_id)
566         else:
567             self.image_id = None
568         # affichage du texte : nom de l'objet (ETAPE ou MOT-CLE) et sa valeur
569         self.drawtext()
570         if self.state == 'expanded' :
571             if not self.children : self.build_children()
572             if len(self.children) > 0:
573                 self.drawchildren()
574                 self.lasty = self.children[-1].lasty
575    
576     def drawchildren(self):
577         """ Dessine les enfants de self """
578         y = self.y + 20
579         x = self.x + 15
580         for child in self.children:
581             child.draw(x,y)
582             nb = child.get_nb_children()
583             y = y + 20*(nb+1)
584         self.trace_ligne()
585
586     def drawtext(self):
587         """ Affiche les deux zones de texte après l'icône de couleur de l'objet """
588         if self.image_id != None :
589             textx = self.x + 30
590         else:
591             textx = self.x + 15
592         texty = self.y
593         # nom,fonte et couleur de l'objet du noeud à afficher
594         labeltext,fonte,couleur = self.item.GetLabelText()
595         if labeltext    == ''   : labeltext = '   '
596         if fonte        == None : fonte = Fonte_Standard
597         if couleur      == None : couleur = 'black'
598         # création du widget label
599         self.label = Label(self.canvas,
600                            text = labeltext,
601                            fg = couleur,
602                            bg = 'gray95',
603                            font=fonte)
604         self.label_id = self.canvas.create_window(textx,texty,window=self.label,anchor='w')
605         self.id.append(self.label_id)
606         # bindings sur le widget label
607         self.select_id=self.label.bind("<1>", self.select)
608         self.popup_id=self.label.bind("<3>", self.popup)
609         self.enter_id=self.label.bind("<Enter>",self.enter)
610         self.leave_id=self.label.bind("<Leave>",self.leave)
611         # valeur de cet objet à afficher
612         x0, y0, x1, y1 = self.canvas.bbox(self.label_id)
613         textx = max(x1, 200) + 10
614         text = self.item.GetText() or " "
615         self.text = Label(self.canvas, text=text,
616                             bd=0, padx=2, pady=2,background='gray95',
617                             font=fonte)
618         if self.selected:
619             self.highlight()
620         else:
621             self.dehighlight()
622         self.text_id = self.canvas.create_window(textx, texty,anchor="w", window=self.text)
623         self.id.append(self.text_id)
624         
625     def highlight(self,event=None):
626         """ Met en surbrillance self"""
627         if hasattr(self,'label'):
628             self.label.configure(fg='white',bg='#00008b')
629             
630     def dehighlight(self,event=None):
631         """ Rétablit l'affichage normal de self"""
632         if hasattr(self,'label'):
633             self.label.configure(fg='black',bg='gray95')
634
635     def enter(self,event=None):
636         """ Met en surbrillance self et affiche le fr de l'objet """
637         self.highlight()
638         fr = self.item.get_fr()
639         self.appli.affiche_infos(fr)
640         
641     def leave(self,event=None):
642         """ Rétablit l'affichage normal de self et efface le fr de l'objet """
643         if not self.selected :
644             self.dehighlight()
645         self.appli.affiche_infos('')
646
647     def collapse_children(self):
648         """ Collapse récursivement tous les descendants de self """
649         if not self.children : return
650         for child in self.children:
651             child.state='collapsed'
652             child.collapse_children()
653
654     def deplieReplieNode(self):           
655         if self.state == 'expanded':
656            self.collapse()
657         else :
658            self.expand_node()
659
660     def collapse(self,event = None):
661         """ Collapse self et descendants et retrace self """
662         nb = self.get_nb_children()
663         self.state = 'collapsed'
664         self.collapse_children()
665         self.redraw(-nb)
666         self.select()
667    
668     def expand_node(self,event = None):
669         """ Expanse self et le retrace """
670         if self.state == 'expanded':return
671         #if not self.item.isactif() : return
672         if not self.children : self.build_children()
673         self.state = 'expanded'
674         nb = self.get_nb_children()
675         self.redraw(nb)
676
677     def expand(self,event = None):
678         """ Expanse self et le retrace """
679         self.expand_node()
680         self.select()
681
682     def redraw(self,nb):
683         """ Redessine self :  nb est le décalage à introduire
684             en dessous de self pour le redessiner """
685         # nb = nombre d'items de décalage
686         self.move(20*nb)
687         # on efface self et on le redessine
688         self.efface()
689         self.draw(self.x,self.y)
690         # Il n'est pas nécessaire d'appeler update
691         # il suffit d'updater les coordonnees et de retracer les lignes
692         self.racine.update_coords()
693         self.racine.trace_ligne()
694         self.update_valid()
695         self.tree.resizescrollregion()
696         
697     def update_coords(self):
698         """ Permet d'updater les coordonnes de self et de tous ses enfants"""
699         if self.displayed == 0 : return
700         if self.image_id != None :
701             coords = self.canvas.coords(self.image_id)
702             self.x = coords[0]-15
703         else:
704             coords = self.canvas.coords(self.label_id)
705             self.x = coords[0]-15
706         self.y = coords[1]
707         self.lasty = self.y
708         if self.state == 'expanded' :
709             for child in self.children:
710                 if child.displayed != 0:
711                     child.update_coords()
712                     self.lasty = child.lasty
713
714     def update_icone(self):
715         """ Met à jour les icônes de tous les noeuds : teste la validité de l'objet
716         Cette méthode est très lente, trop !!"""
717         if self.image_id != None :
718             image = self.geticonimage()
719             self.canvas.itemconfig(self.image_id,image=image)
720         if self.state == 'expanded':
721             for child in self.children:
722                 if child.displayed != 0:
723                     child.update_icone()
724
725     def update_label_texte(self):
726         """ Met a jour le label du noeud et celui de tous ses fils ouverts """
727         self.update_node_label()
728         if self.state == 'expanded' :
729             for child in self.children:
730                 if child.displayed != 0 : child.update_label_texte()
731
732     def update_texte(self):
733         """ Met à jour les noms des SD et valeurs des mots-clés """
734         self.update_node_texte()
735         if self.state == 'expanded' :
736             for child in self.children:
737                 if child.displayed != 0 : child.update_texte()
738         
739     def update_node_label(self):
740         """ Met a jour le label du noeud """
741         if self.displayed == 0 : return
742         # nom,fonte et couleur de l'objet du noeud à afficher
743         labeltext,fonte,couleur = self.item.GetLabelText()
744         if labeltext    == ''   : labeltext = '   '
745         if fonte        == None : fonte = Fonte_Standard
746         if couleur      == None : couleur = 'black'
747         self.label.configure(text=labeltext,font=fonte)
748
749     def update_node_texte(self):
750         """ Met à jour les noms des SD et valeurs des mots-clés """
751         if self.displayed == 0 : return
752         text = self.item.GetText()
753         if text == None : text = ''
754         self.text.configure(text=text)
755
756     def update_node_valid(self) :
757         """Cette methode remet a jour la validite du noeud (icone)
758            Elle appelle isvalid
759         """
760         if self.displayed == 0 : return
761         if self.image_id != None :
762             image = self.geticonimage()
763             self.canvas.itemconfig(self.image_id,image=image)
764
765     def update_valid(self) :
766         """Cette methode a pour but de mettre a jour la validite du noeud
767            et de propager la demande de mise à jour à son parent
768         """
769         self.update_node_valid()
770         self.parent.update_valid()
771
772     def update(self,event=None) :
773         """ Classe Node :
774             Cette méthode est appelée pour demander l update d un noeud 
775             d'un jeu de commandes
776             Cette demande est transmise au noeud racine (le JDC) qui update
777             tout l arbre représentant le jeu de commandes
778             Pendant cette mise à jour, on appelle la méthode isvalid qui
779             fera l update de tous les objets déclarés modifiés lors des
780             actions précédentes
781             La métode isvalid est en général appelée par l intermédiaire de
782             update_icone -> geticonimage -> GetIconName
783         """
784         #print "update",self
785         #traceback.print_stack()
786         self.racine.update_coords()
787         self.racine.trace_ligne()
788         self.racine.update_icone()
789         self.racine.update_texte()
790         self.racine.update_label_texte()
791         self.tree.resizescrollregion()
792
793     def efface_node(self):
794         if self.displayed != 0:
795            self.label.unbind("<1>", self.select_id)
796            self.label.unbind("<3>", self.popup_id)
797            self.label.unbind("<Enter>",self.enter_id)
798            self.label.unbind("<Leave>",self.leave_id)
799            self.canvas.tag_unbind(self.image_id,"<1>",self.select_id2)
800            self.canvas.tag_unbind(self.image_id,"<3>",self.popup_id2)
801            if self.item.IsExpandable():
802               self.canvas.tag_unbind(self.icone_id, "<1>", self.callback_id)
803            self.label.destroy()
804            self.text.destroy()
805
806         for id in self.id :
807             self.canvas.delete(id)
808         self.id=[]
809         self.label_id=None
810         self.text_id=None
811         self.image_id=None
812         self.icone_id=None
813         self.label=None
814         self.text=None
815         self.displayed=0
816
817     def efface(self):
818         """ Efface du canvas les id associés à self : cad les siens et ceux
819             de ses enfants """
820         self.efface_node()
821         if not self.children : return
822         for child in self.children:
823             child.efface()
824
825     def move(self,dy):
826         """ Déplace de l'incrément dy tous les id en dessous de self """
827         # il faut marquer tous les suivants de self
828         bbox1 = self.canvas.bbox(ALL)
829         self.canvas.dtag(ALL,'move')
830         self.canvas.delete('line')
831         try:
832             self.canvas.addtag_overlapping('move',bbox1[0],self.y +10,bbox1[2],bbox1[3])
833         except:
834             print "Erreur dans move :"
835             print self
836             print self.item
837             print self.item.getObject()
838             print self.item.getObject().definition.label
839             print 'y=',self.y
840             print 'dy=',dy
841         # on déplace tous les items de dy
842         self.canvas.move('move',0,dy)
843
844     def trace_ligne(self):
845         """ Dessine les lignes verticales entre frères et entre père et premier fils"""
846         if self.state=='collapsed' : return
847         if len(self.children)==0 : return
848         # on est bien dans le cas d'un noeud expansé avec enfants ...
849         # il faut rechercher l'ordonnée du dernier fils de self
850         y_end = self.children[-1].y
851         ligne = self.canvas.create_line(self.x+15,self.y,self.x+15,y_end,tags='line')
852         self.canvas.tag_lower(ligne)
853         for child in self.children :
854             try:
855                 child.trace_ligne()
856             except:
857                 print "Erreur dans trace_ligne :"
858                 print child
859                 print child.item.getObject()
860
861     def last_child(self):
862         lchild=self
863         if self.state == 'expanded' and self.children:
864            lchild= self.children[-1].last_child()
865         return lchild
866
867     #------------------------------------------------------------------
868     # Méthodes de création et destruction de noeuds
869     # Certaines de ces méthodes peuvent être appelées depuis l'externe
870     #------------------------------------------------------------------
871     def append_brother(self,name,pos='after',retour='non'):
872         """
873         Permet d'ajouter un objet frère à l'objet associé au noeud self
874         par défaut on l'ajoute immédiatement après 
875         Méthode externe
876         """
877         # on veut ajouter le frère de nom name directement avant ou après self
878         index = self.parent.children.index(self)
879         if pos == 'before':
880             index = index
881         elif pos == 'after':
882             index = index +1
883         else:
884             print str(pos)," n'est pas un index valide pour append_brother"
885             return 0
886         return self.parent.append_child(name,pos=index)
887
888     def append_child(self,name,pos=None,verif='oui',retour='non'):
889         """
890            Methode pour ajouter un objet fils à l'objet associé au noeud self.
891            On peut l'ajouter en début de liste (pos='first'), en fin (pos='last')
892            ou en position intermédiaire.
893            Si pos vaut None, on le place à la position du catalogue.
894         """
895         #print "append_child",self,self.children
896         if pos == 'first':
897             index = 0
898         elif pos == 'last':
899             index = len(self.children)
900         elif type(pos) == types.IntType :
901             # position fixee
902             index = pos
903         elif type(pos) == types.InstanceType:
904             # pos est un item. Il faut inserer name apres pos
905             index = self.item.get_index(pos) +1
906         elif type(name) == types.InstanceType:
907             index = self.item.get_index_child(name.nom)
908         else:
909             index = self.item.get_index_child(name)
910         obj=self.item.additem(name,index)
911         #print obj
912         if obj is None:obj=0
913         if obj == 0:return 0
914         #print "append_child",index,self.children
915         child=self.children[index]
916         child.select()
917         return child
918
919     def delete(self):
920         """ 
921             Méthode externe pour la destruction de l'objet associé au noeud
922             La mise à jour des noeuds est faite par onSupp sur notification
923         """
924         index = self.parent.children.index(self) - 1 
925         if index < 0 : index =0
926
927         parent=self.parent
928         ret=parent.item.suppitem(self.item)
929         if ret == 0:return
930
931         brothers=parent.children
932         if brothers:
933            toselect=brothers[index]
934         else:
935            toselect=parent
936         toselect.select()
937