1 # CONFIGURATION MANAGEMENT OF EDF VERSION
2 # ======================================================================
3 # COPYRIGHT (C) 1991 - 2002 EDF R&D WWW.CODE-ASTER.ORG
4 # THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
5 # IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
6 # THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
7 # (AT YOUR OPTION) ANY LATER VERSION.
9 # THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
10 # WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
11 # MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
12 # GENERAL PUBLIC LICENSE FOR MORE DETAILS.
14 # YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
15 # ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
16 # 1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
19 # ======================================================================
20 import os,sys,string,re,types,traceback
28 __version__="$Name: $"
29 __Id__="$Id: treewidget.py,v 1.7 2002/09/10 15:59:37 eficas Exp $"
32 Fonte_Standard = fontes.standard
35 def __init__(self,appli,jdc_item,scrolledcanvas,command = None):
37 self.scrolledcanvas = scrolledcanvas
38 self.canvas = self.scrolledcanvas.component('canvas')
39 self.canvas.bind("<Key-Prior>", self.page_up)
40 self.canvas.bind("<Key-Next>", self.page_down)
41 self.canvas.bind("<Key-Up>", self.unit_up)
42 self.canvas.bind("<Key-Down>", self.unit_down)
44 self.command = command
48 self.node_selected = None
51 def page_up(self,event):
52 event.widget.yview_scroll(-1, "page")
53 def page_down(self,event):
54 event.widget.yview_scroll(1, "page")
55 def unit_up(self,event):
56 event.widget.yview_scroll(-1, "unit")
57 def unit_down(self,event):
58 event.widget.yview_scroll(1, "unit")
60 def build_children(self):
61 """ Construit la liste des enfants de self """
63 child = Node(self,self.item,self.command)
64 self.children.append(child)
65 child.state='expanded'
68 """ Dessine l'arbre """
71 for child in self.children:
73 lasty = child.lasty + 15
76 self.children[0].select()
77 self.resizescrollregion()
79 def deselectall(self):
80 """ déselectionne tous les éléments de l'arbre """
81 if self.node_selected :
82 self.node_selected.deselect()
85 """ Update tous les éléments de l'arbre """
86 for child in self.children:
89 def resizescrollregion(self):
90 self.scrolledcanvas.resizescrollregion()
92 def select_next(self,event):
93 self.node_selected.select_next()
95 def select_previous(self,event):
96 self.node_selected.select_previous()
98 def full_creation(self,name,index):
99 # A changer lorsqu'il y aura plusieurs jdc ouverts en même temps
100 self.children[0].full_creation(name,index)
103 for child in self.children :
104 self.verif_all_children()
107 def __init__(self,parent,item,command=None):
110 self.command = command
111 self.tree = self.parent.tree
112 self.appli = self.parent.appli
113 self.canvas = self.parent.canvas
115 #self.build_children()
118 self.state='collapsed'
121 self.x = self.y =None
125 # etape = noeud d'étape auquel appartient self
126 # = self si c'est lui-même
127 if isinstance(self.parent,Tree) :
128 # on est sur un noeud de JDC
132 elif isinstance(self.parent.parent,Tree) :
133 # on est sur un noeud d'étape
134 self.racine = self.parent
136 self.nature = 'ETAPE'
138 # on est sur un noeud de mot-clé
139 self.racine = self.parent.racine
140 self.etape=self.parent.etape
141 self.nature = 'MOTCLE'
143 def build_children(self):
144 """ Construit la liste des enfants de self """
146 sublist = self.item._GetSubList()
147 if not sublist : return
148 for item in sublist :
149 child = Node(self,item,self.command)
150 self.children.append(child)
152 #-----------------------------------------------
153 # Méthodes de sélection/déselection d'un noeud
154 #-----------------------------------------------
156 def select(self, event=None):
158 Rend le noeud courant (self) sélectionné et déselectionne
161 if not self.children : self.build_children()
162 self.tree.deselectall()
164 self.tree.node_selected = self
165 if self.command:apply(self.command,(self,))
168 def deselect(self, event=None):
169 """ Déselectionne self """
171 if self.displayed == 1 : self.dehighlight()
173 def make_visible(self):
174 """ Rend l'objet self visible cad déplace le scroll pour que self soit dans
175 la fenêtre de visu"""
176 x0,y0,x1,y1 = self.canvas.bbox(ALL)
177 self.canvas.yview("moveto",self.y/y1)
179 def select_next(self,ind=0):
180 """ on doit chercher à sélectionner dans l'ordre:
181 - son premier fils s'il est affiché
182 - son frère cadet s'il existe
183 - son oncle (benjamin de son père)
184 - ... appel récursif ...
186 if self.state=='expanded' and len(self.children) > ind:
187 self.children[ind].select()
189 index = self.parent.children.index(self) + 1
190 if isinstance(self.parent,TREE) :
192 self.children[ind].select()
194 self.children[0].select()
196 self.parent.select_next(index)
198 def select_previous(self):
199 """ on doit d'abord sélectionner(dans l'ordre) :
203 index = self.parent.children.index(self) + 1
205 self.parent.children[index].select()
208 #-----------------------------------------------
209 # Méthodes de recherche d'informations
210 #-----------------------------------------------
211 def geticonimage(self,name=None):
213 Retourne l'image qui doit être associée à self
216 name = self.item.GetIconName()
217 if not name or name == 'aucune' :
219 return images.get_image(name)
221 def get_nb_children(self):
222 """ Retourne le nombre d'enfants affichés de self """
224 if self.state =='collapsed' : return nb
225 for child in self.children :
226 nb = nb + 1 + child.get_nb_children()
229 def get_liste_id(self):
230 """ Retourne la liste de tous les id (filiation comprise) de self """
232 for child in self.children:
233 liste.extend(child.get_liste_id())
236 def get_node_fils(self,name) :
237 """ Retourne le fils de self de nom name s'il existe"""
238 for child in self.children:
239 if child.item.get_nom() == name: return child
241 #-----------------------------------------------
242 # Méthodes d'affichage d'un noeud
243 #-----------------------------------------------
245 """ Permet de tracer le noeud self """
246 # le début du noeud est en x,y
252 # choix de l'icone à afficher : + ou -
253 if self.item.IsExpandable():
254 if self.state == 'expanded':
255 iconname = "minusnode"
256 callback = self.collapse
258 iconname = "plusnode"
259 callback = self.expand
260 image = self.geticonimage(name=iconname)
261 self.icone_id = self.canvas.create_image(self.x, self.y, image=image)
262 self.canvas.tag_bind(self.icone_id, "<1>", callback)
263 self.id.append(self.icone_id)
264 # création de la ligne horizontale
265 self.ligne_id = self.canvas.create_line(self.x,self.y,self.x+10,self.y)
266 self.id.append(self.ligne_id)
267 self.canvas.tag_lower(self.ligne_id)
268 # affichage de l'icone (carre ,rond, ovale ...) de couleur
269 image = self.geticonimage()
271 self.image_id = self.canvas.create_image(self.x+15,self.y,image = image)
272 self.canvas.tag_bind(self.image_id,"<1>",self.select)
273 self.id.append(self.image_id)
276 # affichage du texte : nom de l'objet (ETAPE ou MOT-CLE) et sa valeur
278 if self.state == 'expanded' :
279 if not self.children : self.build_children()
280 if len(self.children) > 0:
282 self.lasty = self.children[-1].lasty
284 def drawchildren(self):
285 """ Dessine les enfants de self """
288 for child in self.children:
290 nb = child.get_nb_children()
294 """ Affiche les deux zones de texte après l'icône de couleur de l'objet """
295 if self.image_id != None :
300 # nom,fonte et couleur de l'objet du noeud à afficher
301 labeltext,fonte,couleur = self.item.GetLabelText()
302 if labeltext == '' : labeltext = ' '
303 if fonte == None : fonte = Fonte_Standard
304 if couleur == None : couleur = 'black'
305 # création du widget label
306 self.label = Label(self.canvas,
311 self.label_id = self.canvas.create_window(textx,texty,window=self.label,anchor='w')
312 self.id.append(self.label_id)
313 # bindings sur le widget label
314 self.label.bind("<1>", self.select)
315 self.label.bind("<Enter>",self.enter)
316 self.label.bind("<Leave>",self.leave)
317 # valeur de cet objet à afficher
318 x0, y0, x1, y1 = self.canvas.bbox(self.label_id)
319 textx = max(x1, 200) + 10
320 text = self.item.GetText() or " "
321 self.text = Label(self.canvas, text=text,
322 bd=0, padx=2, pady=2,background='gray95',
328 self.text_id = self.canvas.create_window(textx, texty,anchor="w", window=self.text)
329 self.id.append(self.text_id)
331 def highlight(self,event=None):
332 """ Met en surbrillance self"""
333 if hasattr(self,'label'):
334 self.label.configure(fg='white',bg='#00008b')
336 def dehighlight(self,event=None):
337 """ Rétablit l'affichage normal de self"""
338 if hasattr(self,'label'):
339 self.label.configure(fg='black',bg='gray95')
341 def enter(self,event=None):
342 """ Met en surbrillance self et affiche le fr de l'objet """
344 fr = self.item.get_fr()
345 self.appli.affiche_infos(fr)
347 def leave(self,event=None):
348 """ Rétablit l'affichage normal de self et efface le fr de l'objet """
349 if not self.selected :
351 self.appli.affiche_infos('')
353 def collapse_children(self):
354 """ Collapse récursivement tous les descendants de self """
355 if not self.children : return
356 for child in self.children:
357 child.state='collapsed'
359 child.collapse_children()
361 def collapse(self,event = None):
362 """ Collapse self et descendants et retrace self """
363 nb = self.get_nb_children()
364 self.state = 'collapsed'
365 self.collapse_children()
371 self.draw(self.x,self.y)
375 def expand(self,event = None):
376 """ Expanse self et le retrace """
377 if not self.item.isactif() : return
378 if not self.children : self.build_children()
379 self.state = 'expanded'
380 nb = self.get_nb_children()
383 self.draw(self.x,self.y)
388 """ Redessine self : nb est le décalage à introduire
389 en dessous de self pour le redessiner """
390 # nb = nombre d'items de décalage
392 # on efface self et on le redessine
394 self.draw(self.x,self.y)
397 def update_coords(self):
398 """ Permet d'updater les coordonnes de self et de tous ses enfants"""
399 if self.displayed == 0 : return
400 if self.image_id != None :
401 coords = self.canvas.coords(self.image_id)
402 self.x = coords[0]-15
404 coords = self.canvas.coords(self.label_id)
405 self.x = coords[0]-15
407 if self.state == 'expanded' :
408 for child in self.children:
409 if child.displayed != 0:
410 child.update_coords()
412 def update_icone(self):
413 """ Met à jour les icônes de tous les noeuds : teste la validité de l'objet
414 Cette méthode est très lente, trop !!"""
415 if self.image_id != None :
416 image = self.geticonimage()
417 self.canvas.itemconfig(self.image_id,image=image)
418 if self.state == 'expanded':
419 for child in self.children:
420 if child.displayed != 0:
423 def update_texte(self):
424 """ Met à jour les noms des SD et valeurs des mots-clés """
425 text = self.item.GetText()
426 if text == None : text = ''
427 self.text.configure(text=text)
428 if self.state == 'expanded' :
429 for child in self.children:
430 if child.displayed != 0 : child.update_texte()
432 def update(self,event=None) :
434 Cette méthode est appelée pour demander l update d un noeud
435 d'un jeu de commandes
436 Cette demande est transmise au noeud racine (le JDC) qui update
437 tout l arbre représentant le jeu de commandes
438 Pendant cette mise à jour, on appelle la méthode isvalid qui
439 fera l update de tous les objets déclarés modifiés lors des
441 La métode isvalid est en général appelée par l intermédiaire de
442 update_icone -> geticonimage -> GetIconName
444 self.racine.update_coords()
445 self.racine.trace_ligne()
446 self.racine.update_icone()
447 self.racine.update_texte()
448 self.tree.resizescrollregion()
451 """ Efface du canvas les id associés à self : cad les siens et ceux
454 self.canvas.delete(id)
455 if not self.children : return
456 for child in self.children:
460 """ Déplace de l'incrément dy tous les id en dessous de self """
461 # il faut marquer tous les suivants de self
462 bbox1 = self.canvas.bbox(ALL)
463 self.canvas.dtag(ALL,'move')
464 self.canvas.delete('line')
466 self.canvas.addtag_overlapping('move',bbox1[0],self.y +10,bbox1[2],bbox1[3])
468 print "Erreur dans move :"
471 print self.item.object
472 print self.item.object.definition.label
475 # on déplace tous les items de dy
476 self.canvas.move('move',0,dy)
477 # il faut réactualiser la zone de scroll
478 self.tree.resizescrollregion()
480 def trace_ligne(self):
481 """ Dessine les lignes verticales entre frères et entre père et premier fils"""
482 if self.state=='collapsed' : return
483 #if self.displayed == 0 : return
484 if len(self.children)==0 : return
485 # on est bien dans le cas d'un noeud expansé avec enfants ...
486 # il faut rechercher l'ordonnée du dernier fils de self
487 y_end = self.children[-1].y
488 ligne = self.canvas.create_line(self.x+15,self.y,self.x+15,y_end,tags='line')
489 self.canvas.tag_lower(ligne)
490 for child in self.children :
494 print "Erreur dans trace_ligne :"
496 print child.item.object
498 def make_visible_OBSOLETE(self,nb):
499 """ Cette méthode a pour but de rendre le noeud self (avec tous ses descendants
500 affichés) visible dans le canvas """
501 x = self.canvas.canvasx(self.canvas.cget('width'))
502 y = self.canvas.canvasy(self.canvas.cget('height'))
504 x0,y0,x1,y1 = self.canvas.bbox(ALL)
505 #print 'x0,y1=',x0,y1
507 nb = self.get_nb_children()
508 y_fin = y_deb + 20*nb
509 #print 'y_deb,y_fin=',y_deb,y_fin
511 #------------------------------------------------------------------
512 # Méthodes de création et destruction de noeuds
513 # Certaines de ces méthodes peuvent être appelées depuis l'externe
514 #------------------------------------------------------------------
515 def replace_node(self,node1,node2):
516 """ Remplace le noeud 1 par le noeud 2 dans la liste des enfants de self"""
517 index= self.children.index(node1)
518 self.delete_node_child(node1)
519 self.children.insert(index,node2)
521 def full_creation(self,name,pos=None):
523 Interface avec ACCAS : création de l'objet de nom name et
524 du noeud associé. Retourne le noeud fils ainsi créé
526 item = self.item.additem(name,pos)
527 if item == None or item == 0:
528 # impossible d'ajouter le noeud de nom : name
530 nature = item.get_nature()
531 if nature in ("COMMANDE","OPERATEUR","PROCEDURE","COMMENTAIRE",
532 "PARAMETRE","COMMANDE_COMMENTARISEE","PARAMETRE_EVAL"):
533 # on veut ajouter une commande ou un commentaire ou un paramètre
534 # il ne faut pas rechercher un même objet déjà existant
535 # à modifier : il faut tester l'attribut 'repetable'
537 elif self.item.object.isMCList():
538 # Dans ce cas on ne fait pas de remplacement. On ne cherche pas un objet de meme nom
541 enfant = self.get_node_fils(item.get_nom())
543 # un fils de même nom existe déjà : on remplace
544 # un MCFACT (ou une MCList) par une (autre) MCList
545 child = Node(self,item,self.command)
546 self.replace_node(enfant,child)
548 child = Node(self, item,self.command)
550 self.children.append(child)
552 self.children.insert(pos,child)
555 def append_brother(self,name,pos='after',retour='non'):
557 Permet d'ajouter un frère à self
558 par défaut on l'ajoute après self
561 # on veut ajouter le frère de nom name directement avant ou après self
562 index = self.parent.children.index(self)
568 print str(pos)," n'est pas un index valide pour append_brother"
570 return self.parent.append_child(name,pos=index,retour=retour)
572 def append_node_child(self,fils,pos=None,verif='oui'):
574 Fait appel à la création complète de fils et à la vérification
575 des conditions en fonction du contexte
576 Attention : fils peut être un nom ou déjà un object (cas d'une copie)
578 if not self.children : self.build_children()
580 #pos = len(self.children)
581 if type(fils) == types.InstanceType:
582 pos = self.item.get_index_child(fils.nom)
584 pos = self.item.get_index_child(fils)
585 child = self.full_creation(fils,pos)
587 # on n'a pas pu créer le noeud fils
590 self.state = 'expanded'
592 if not child.children : child.build_children()
593 test = child.item.isMCList()
595 child.children[-1].verif_condition()
597 child.verif_condition()
598 self.verif_condition()
601 def append_child(self,name,pos=None,verif='oui',retour='non'):
603 Permet d'ajouter un fils à self
604 on peut l'ajouter en fin de liste (défaut) ou en début
610 index = len(self.children)
611 elif pos != None and type(pos) == types.IntType :
612 # on donne la position depuis l'extérieur
613 # (appel de append_child par append_brother par exemple)
615 elif type(pos) == types.InstanceType:
616 # pos est un item. Il faut inserer name apres pos
617 index = self.item.get_index(pos) +1
619 if type(name) == types.InstanceType:
620 index = self.item.get_index_child(name.nom)
622 index = self.item.get_index_child(name)
623 nbold = self.get_nb_children()
624 self.state='expanded'
625 child = self.append_node_child(name,pos=index)
627 # on n'a pas pu créer le fils
629 nbnew = self.get_nb_children()
630 self.redraw(nbnew-nbold)
633 #child.make_visible()
634 if retour == 'oui': return child
636 def delete_node_child(self,child):
637 """ Supprime child des enfants de self et les id associés """
640 self.children.remove(child)
643 def delete_child(self,child):
645 Supprime child des enfants de self, tous les id associés
648 if self.item.suppitem(child.item):
649 self.delete_node_child(child)
655 """ Méthode externe pour la destruction du noeud ET de l'objet
656 Gère l'update du canvas"""
657 if self.parent.item.isMCList():
658 pere = self.parent.parent
659 nbold = pere.get_nb_children()
660 if self.parent.delete_child(self):
661 self.parent.traite_mclist()
662 if self.item.get_position() == 'global':
663 self.etape.verif_all()
664 elif self.item.get_position() == 'global_jdc':
665 self.racine.verif_all()
667 self.parent.verif_condition()
668 nbnew = pere.get_nb_children()
671 nbold = pere.get_nb_children()
672 if self.parent.delete_child(self):
673 if self.item.get_position() == 'global':
674 self.etape.verif_all()
675 elif self.item.get_position() == 'global_jdc':
676 self.racine.verif_all()
678 self.parent.verif_condition()
680 print 'Erreur dans la destruction de ',self.item.get_nom(),' dans delete'
681 nbnew = pere.get_nb_children()
682 pere.redraw(nbnew-nbold)
685 def copynode(self,node,pos) :
686 """ node est le noeud à copier à la position pos de self ( = parent de node) """
687 objet_copie = node.item.get_copie_objet()
688 child = self.full_creation(node.item,pos)
689 child.displayed = node.displayed
690 #child.image_id = node.image_id
691 #child.label_id = node.label_id
692 if child.item.get_nature() == "MCList":
693 child.item.object[-1].mc_liste = objet_copie.mc_liste
696 child.item.object.mc_liste = objet_copie.mc_liste
698 traceback.print_exc()
699 #--------------------------------------------------------------
700 # Méthodes de vérification du contexte et de validité du noeud
701 #--------------------------------------------------------------
702 def traite_mclist_OLD(self):
703 """ Dans le cas d'une MCList il faut vérifier qu'elle n'est pas vide
704 ou réduite à un seul élément suite à une destruction
706 # self représente une MCList
707 print "on passe par traite_mclist ",len(self.item)
708 if len(self.item) == 0 :
709 # la liste est vide : il faut la supprimer
711 elif len(self.item) == 1:
712 # il ne reste plus qu'un élément dans la liste
713 # il faut supprimer la liste et créer directement l'objet
714 index = self.parent.children.index(self)
715 noeud = self.children[0]
716 if self.parent.delete_child(self):
717 self.parent.append_node_child(noeud.item,pos=index,verif='non')
719 print "destruction de self impossible !"
720 #if self.parent.delete_child(self):
721 # self.parent.copynode(self.children[0],index)
723 # print 'erreur dans la destruction de :',self.item.get_nom(),' dans traite_mclist'
727 def traite_mclist(self):
728 """ Dans le cas d'une MCList il faut vérifier qu'elle n'est pas vide
729 ou réduite à un seul élément suite à une destruction
731 # self représente une MCList
732 if len(self.item) == 0 :
733 # la liste est vide : il faut la supprimer
735 elif len(self.item) == 1:
736 # il ne reste plus qu'un élément dans la liste
737 # il faut supprimer la liste et créer directement l'objet
738 index = self.parent.children.index(self)
739 noeud = self.children[0]
740 noeud.parent = self.parent
741 self.parent.delete_node_child(self)
742 self.parent.item.replace_child(self.item,noeud.item)
743 self.parent.children.insert(index,noeud)
748 self.verif_all_children()
750 def verif_all_children(self):
751 if not self.children : self.build_children()
752 if self.nature != 'JDC' :
754 for child in self.children :
755 child.verif_all_children()
759 Lance la vérification des conditions des blocs de self et le cas
760 échéant redessine self
762 nbold = self.get_nb_children()
763 test = self.verif_condition()
764 nbnew = self.get_nb_children()
766 self.redraw(nbnew-nbold)
768 def verif_condition(self):
770 on lance la vérification des conditions de chaque bloc de self
771 on crée ou supprime les noeuds concernés
772 (self est d'un niveau inférieur ou égal à l'ETAPE)
774 if self.item.object.__class__.__name__ == 'ETAPE_NIVEAU': return 0
776 l_bloc_arajouter,l_bloc_aenlever = self.verif_condition_bloc()
777 if len(l_bloc_arajouter) > 0:
779 for mc in l_bloc_arajouter:
780 self.append_node_child(mc,verif='non')
781 if len(l_bloc_aenlever) > 0:
783 for mc in l_bloc_aenlever:
784 mocle = self.get_node_fils(mc)
785 self.delete_child(mocle)
786 l_mc_presents = self.item.get_liste_mc_presents()
787 l_mc_arajouter= self.verif_condition_regles(l_mc_presents)
788 if len(l_mc_arajouter) > 0:
790 for mc in l_mc_arajouter:
791 self.append_node_child(mc,verif='non')
792 if len(l_mc_arajouter)+len(l_bloc_arajouter)+len(l_bloc_aenlever) != 0 :
793 self.verif_condition()
796 def verif_condition_bloc(self):
797 return self.item.verif_condition_bloc()
799 def verif_condition_regles(self,l_mc_presents):
800 return self.item.verif_condition_regles(l_mc_presents)