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.5 2002/05/15 15:31:59 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_old(self):
80 """ déselectionne tous les éléments de l'arbre """
81 for child in self.children:
84 def deselectall(self):
85 """ déselectionne tous les éléments de l'arbre """
86 if self.node_selected :
87 self.node_selected.deselect()
90 """ Update tous les éléments de l'arbre """
91 for child in self.children:
94 def resizescrollregion(self):
95 self.scrolledcanvas.resizescrollregion()
97 def select_next(self,event):
98 self.node_selected.select_next()
100 def select_previous(self,event):
101 self.node_selected.select_previous()
103 def full_creation(self,name,index):
104 # A changer lorsqu'il y aura plusieurs jdc ouverts en même temps
105 self.children[0].full_creation(name,index)
108 for child in self.children :
109 self.verif_all_children()
112 def __init__(self,parent,item,command=None):
115 self.command = command
116 self.tree = self.parent.tree
117 self.appli = self.parent.appli
118 self.canvas = self.parent.canvas
120 #self.build_children()
123 self.state='collapsed'
126 self.x = self.y =None
130 # etape = noeud d'étape auquel appartient self
131 # = self si c'est lui-même
132 if isinstance(self.parent,Tree) :
133 # on est sur un noeud de JDC
137 elif isinstance(self.parent.parent,Tree) :
138 # on est sur un noeud d'étape
139 self.racine = self.parent
141 self.nature = 'ETAPE'
143 # on est sur un noeud de mot-clé
144 self.racine = self.parent.racine
145 self.etape=self.parent.etape
146 self.nature = 'MOTCLE'
148 def build_children(self):
149 """ Construit la liste des enfants de self """
151 sublist = self.item._GetSubList()
152 if not sublist : return
153 for item in sublist :
154 child = Node(self,item,self.command)
155 self.children.append(child)
157 #-----------------------------------------------
158 # Méthodes de sélection/déselection d'un noeud
159 #-----------------------------------------------
161 def select(self, event=None):
163 Rend le noeud courant (self) sélectionné et déselectionne
166 if not self.children : self.build_children()
167 self.tree.deselectall()
169 self.tree.node_selected = self
170 if self.command:apply(self.command,(self,))
172 self.canvas.focus_force()
175 def deselect_old(self, event=None):
176 """ Déselectionne self """
178 if self.displayed == 1:
180 for child in self.children:
183 def deselect(self, event=None):
184 """ Déselectionne self """
186 if self.displayed == 1 : self.dehighlight()
188 def make_visible(self):
189 """ Rend l'objet self visible cad déplace le scroll pour que self soit dans
190 la fenêtre de visu"""
191 x0,y0,x1,y1 = self.canvas.bbox(ALL)
192 self.canvas.yview("moveto",self.y/y1)
194 def select_next(self,ind=0):
195 """ on doit chercher à sélectionner dans l'ordre:
196 - son premier fils s'il est affiché
197 - son frère cadet s'il existe
198 - son oncle (benjamin de son père)
199 - ... appel récursif ...
201 if self.state=='expanded' and len(self.children) > ind:
202 self.children[ind].select()
204 index = self.parent.children.index(self) + 1
205 if isinstance(self.parent,TREE) :
207 self.children[ind].select()
209 self.children[0].select()
211 self.parent.select_next(index)
213 def select_previous(self):
214 """ on doit d'abord sélectionner(dans l'ordre) :
218 index = self.parent.children.index(self) + 1
220 self.parent.children[index].select()
223 #-----------------------------------------------
224 # Méthodes de recherche d'informations
225 #-----------------------------------------------
226 def geticonimage(self,name=None):
228 Retourne l'image qui doit être associée à self
231 name = self.item.GetIconName()
232 if not name or name == 'aucune' :
234 return images.get_image(name)
236 def get_nb_children(self):
237 """ Retourne le nombre d'enfants affichés de self """
239 if self.state =='collapsed' : return nb
240 for child in self.children :
241 nb = nb + 1 + child.get_nb_children()
244 def get_liste_id(self):
245 """ Retourne la liste de tous les id (filiation comprise) de self """
247 for child in self.children:
248 liste.extend(child.get_liste_id())
251 def get_node_fils(self,name) :
252 """ Retourne le fils de self de nom name s'il existe"""
253 for child in self.children:
254 if child.item.get_nom() == name: return child
256 #-----------------------------------------------
257 # Méthodes d'affichage d'un noeud
258 #-----------------------------------------------
260 """ Permet de tracer le noeud self """
261 # le début du noeud est en x,y
267 # choix de l'icone à afficher : + ou -
268 if self.item.IsExpandable():
269 if self.state == 'expanded':
270 iconname = "minusnode"
271 callback = self.collapse
273 iconname = "plusnode"
274 callback = self.expand
275 image = self.geticonimage(name=iconname)
276 self.icone_id = self.canvas.create_image(self.x, self.y, image=image)
277 self.canvas.tag_bind(self.icone_id, "<1>", callback)
278 self.id.append(self.icone_id)
279 # création de la ligne horizontale
280 self.ligne_id = self.canvas.create_line(self.x,self.y,self.x+10,self.y)
281 self.id.append(self.ligne_id)
282 self.canvas.tag_lower(self.ligne_id)
283 # affichage de l'icone (carre ,rond, ovale ...) de couleur
284 image = self.geticonimage()
286 self.image_id = self.canvas.create_image(self.x+15,self.y,image = image)
287 self.canvas.tag_bind(self.image_id,"<1>",self.select)
288 self.id.append(self.image_id)
291 # affichage du texte : nom de l'objet (ETAPE ou MOT-CLE) et sa valeur
293 if self.state == 'expanded' :
294 if not self.children : self.build_children()
295 if len(self.children) > 0:
297 self.lasty = self.children[-1].lasty
299 def drawchildren(self):
300 """ Dessine les enfants de self """
303 for child in self.children:
305 nb = child.get_nb_children()
309 """ Affiche les deux zones de texte après l'icône de couleur de l'objet """
310 if self.image_id != None :
315 # nom,fonte et couleur de l'objet du noeud à afficher
316 labeltext,fonte,couleur = self.item.GetLabelText()
317 if labeltext == '' : labeltext = ' '
318 if fonte == None : fonte = Fonte_Standard
319 if couleur == None : couleur = 'black'
320 # création du widget label
321 self.label = Label(self.canvas,
326 self.label_id = self.canvas.create_window(textx,texty,window=self.label,anchor='w')
327 self.id.append(self.label_id)
328 # bindings sur le widget label
329 self.label.bind("<1>", self.select)
330 self.label.bind("<Enter>",self.enter)
331 self.label.bind("<Leave>",self.leave)
332 # valeur de cet objet à afficher
333 x0, y0, x1, y1 = self.canvas.bbox(self.label_id)
334 textx = max(x1, 200) + 10
335 text = self.item.GetText() or " "
336 self.text = Label(self.canvas, text=text,
337 bd=0, padx=2, pady=2,background='gray95',
343 self.text_id = self.canvas.create_window(textx, texty,anchor="w", window=self.text)
344 self.id.append(self.text_id)
345 # Redefinit la region de scroll du canvas pour inclure tous les items créés
346 self.tree.scrolledcanvas.resizescrollregion()
348 def highlight(self,event=None):
349 """ Met en surbrillance self"""
350 if hasattr(self,'label'):
351 self.label.configure(fg='white',bg='#00008b')
353 def dehighlight(self,event=None):
354 """ Rétablit l'affichage normal de self"""
355 if hasattr(self,'label'):
356 self.label.configure(fg='black',bg='gray95')
358 def enter(self,event=None):
359 """ Met en surbrillance self et affiche le fr de l'objet """
361 fr = self.item.get_fr()
362 self.appli.affiche_infos(fr)
364 def leave(self,event=None):
365 """ Rétablit l'affichage normal de self et efface le fr de l'objet """
366 if not self.selected :
368 self.appli.affiche_infos('')
370 def collapse_children(self):
371 """ Collapse récursivement tous les descendants de self """
372 if not self.children : return
373 for child in self.children:
374 child.state='collapsed'
376 child.collapse_children()
378 def collapse(self,event = None):
379 """ Collapse self et descendants et retrace self """
380 nb = self.get_nb_children()
381 self.state = 'collapsed'
382 self.collapse_children()
388 self.draw(self.x,self.y)
392 def expand(self,event = None):
393 """ Expanse self et le retrace """
394 if not self.item.isactif() : return
395 if not self.children : self.build_children()
396 self.state = 'expanded'
397 nb = self.get_nb_children()
400 self.draw(self.x,self.y)
405 """ Redessine self : nb est le décalage à introduire
406 en dessous de self pour le redessiner """
407 # nb = nombre d'items de décalage
409 # on efface self et on le redessine
411 self.draw(self.x,self.y)
414 def update_coords(self):
415 """ Permet d'updater les coordonnes de self et de tous ses enfants"""
416 if self.displayed == 0 : return
417 if self.image_id != None :
418 coords = self.canvas.coords(self.image_id)
419 self.x = coords[0]-15
421 coords = self.canvas.coords(self.label_id)
422 self.x = coords[0]-15
424 if self.state == 'expanded' :
425 for child in self.children:
426 if child.displayed != 0:
427 child.update_coords()
429 def update_icone(self):
430 """ Met à jour les icônes de tous les noeuds : teste la validité de l'objet
431 Cette méthode est très lente, trop !!"""
432 if self.image_id != None :
433 image = self.geticonimage()
434 self.canvas.itemconfig(self.image_id,image=image)
435 if self.state == 'expanded':
436 for child in self.children:
437 if child.displayed != 0:
440 def update_texte(self):
441 """ Met à jour les noms des SD et valeurs des mots-clés """
442 text = self.item.GetText()
443 if text == None : text = ''
444 self.text.configure(text=text)
445 if self.state == 'expanded' :
446 for child in self.children:
447 if child.displayed != 0 : child.update_texte()
449 def update(self,event=None) :
451 Cette méthode est appelée pour demander l update d un noeud
452 d'un jeu de commandes
453 Cette demande est transmise au noeud racine (le JDC) qui update
454 tout l arbre représentant le jeu de commandes
455 Pendant cette mise à jour, on appelle la méthode isvalid qui
456 fera l update de tous les objets déclarés modifiés lors des
458 La métode isvalid est en général appelée par l intermédiaire de
459 update_icone -> geticonimage -> GetIconName
461 self.racine.update_coords()
462 self.racine.trace_ligne()
463 self.racine.update_icone()
464 self.racine.update_texte()
465 self.tree.resizescrollregion()
468 """ Efface du canvas les id associés à self : cad les siens et ceux
471 self.canvas.delete(id)
472 if not self.children : return
473 for child in self.children:
477 """ Déplace de l'incrément dy tous les id en dessous de self """
478 # il faut marquer tous les suivants de self
479 bbox1 = self.canvas.bbox(ALL)
480 self.canvas.dtag(ALL,'move')
481 self.canvas.delete('line')
483 self.canvas.addtag_overlapping('move',bbox1[0],self.y +10,bbox1[2],bbox1[3])
485 print "Erreur dans move :"
488 print self.item.object
489 print self.item.object.definition.label
492 # on déplace tous les items de dy
493 self.canvas.move('move',0,dy)
494 # il faut réactualiser la zone de scroll
495 self.tree.resizescrollregion()
497 def trace_ligne(self):
498 """ Dessine les lignes verticales entre frères et entre père et premier fils"""
499 if self.state=='collapsed' : return
500 #if self.displayed == 0 : return
501 if len(self.children)==0 : return
502 # on est bien dans le cas d'un noeud expansé avec enfants ...
503 # il faut rechercher l'ordonnée du dernier fils de self
504 y_end = self.children[-1].y
505 ligne = self.canvas.create_line(self.x+15,self.y,self.x+15,y_end,tags='line')
506 self.canvas.tag_lower(ligne)
507 for child in self.children :
511 print "Erreur dans trace_ligne :"
513 print child.item.object
515 def make_visible_OBSOLETE(self,nb):
516 """ Cette méthode a pour but de rendre le noeud self (avec tous ses descendants
517 affichés) visible dans le canvas """
518 x = self.canvas.canvasx(self.canvas.cget('width'))
519 y = self.canvas.canvasy(self.canvas.cget('height'))
521 x0,y0,x1,y1 = self.canvas.bbox(ALL)
522 #print 'x0,y1=',x0,y1
524 nb = self.get_nb_children()
525 y_fin = y_deb + 20*nb
526 #print 'y_deb,y_fin=',y_deb,y_fin
528 #------------------------------------------------------------------
529 # Méthodes de création et destruction de noeuds
530 # Certaines de ces méthodes peuvent être appelées depuis l'externe
531 #------------------------------------------------------------------
532 def replace_node(self,node1,node2):
533 """ Remplace le noeud 1 par le noeud 2 dans la liste des enfants de self"""
534 index= self.children.index(node1)
535 self.delete_node_child(node1)
536 self.children.insert(index,node2)
538 def full_creation(self,name,pos=None):
540 Interface avec ACCAS : création de l'objet de nom name et
541 du noeud associé. Retourne le noeud fils ainsi créé
543 item = self.item.additem(name,pos)
544 if item == None or item == 0:
545 # impossible d'ajouter le noeud de nom : name
547 nature = item.get_nature()
548 if nature in ("COMMANDE","OPERATEUR","PROCEDURE","COMMENTAIRE",
549 "PARAMETRE","COMMANDE_COMMENTARISEE","PARAMETRE_EVAL"):
550 # on veut ajouter une commande ou un commentaire ou un paramètre
551 # il ne faut pas rechercher un même objet déjà existant
552 # à modifier : il faut tester l'attribut 'repetable'
554 elif self.item.object.isMCList():
555 # Dans ce cas on ne fait pas de remplacement. On ne cherche pas un objet de meme nom
558 enfant = self.get_node_fils(item.get_nom())
560 # un fils de même nom existe déjà : on remplace
561 # un MCFACT (ou une MCList) par une (autre) MCList
562 child = Node(self,item,self.command)
563 self.replace_node(enfant,child)
565 child = Node(self, item,self.command)
567 self.children.append(child)
569 self.children.insert(pos,child)
572 def append_brother(self,name,pos='after',retour='non'):
574 Permet d'ajouter un frère à self
575 par défaut on l'ajoute après self
578 # on veut ajouter le frère de nom name directement avant ou après self
579 index = self.parent.children.index(self)
585 print str(pos)," n'est pas un index valide pour append_brother"
587 return self.parent.append_child(name,pos=index,retour=retour)
589 def append_node_child(self,fils,pos=None,verif='oui'):
591 Fait appel à la création complète de fils et à la vérification
592 des conditions en fonction du contexte
593 Attention : fils peut être un nom ou déjà un object (cas d'une copie)
595 if not self.children : self.build_children()
597 #pos = len(self.children)
598 if type(fils) == types.InstanceType:
599 pos = self.item.get_index_child(fils.nom)
601 pos = self.item.get_index_child(fils)
602 child = self.full_creation(fils,pos)
604 # on n'a pas pu créer le noeud fils
607 self.state = 'expanded'
609 if not child.children : child.build_children()
610 test = child.item.isMCList()
612 child.children[-1].verif_condition()
614 child.verif_condition()
615 self.verif_condition()
618 def append_child(self,name,pos=None,verif='oui',retour='non'):
620 Permet d'ajouter un fils à self
621 on peut l'ajouter en fin de liste (défaut) ou en début
627 index = len(self.children)
628 elif pos != None and type(pos) == types.IntType :
629 # on donne la position depuis l'extérieur
630 # (appel de append_child par append_brother par exemple)
632 elif type(pos) == types.InstanceType:
633 # pos est un item. Il faut inserer name apres pos
634 index = self.item.get_index(pos) +1
636 if type(name) == types.InstanceType:
637 index = self.item.get_index_child(name.nom)
639 index = self.item.get_index_child(name)
640 nbold = self.get_nb_children()
641 self.state='expanded'
642 child = self.append_node_child(name,pos=index)
644 # on n'a pas pu créer le fils
646 nbnew = self.get_nb_children()
647 self.redraw(nbnew-nbold)
650 #child.make_visible()
651 if retour == 'oui': return child
653 def delete_node_child(self,child):
654 """ Supprime child des enfants de self et les id associés """
657 self.children.remove(child)
660 def delete_child(self,child):
662 Supprime child des enfants de self, tous les id associés
665 if self.item.suppitem(child.item):
666 self.delete_node_child(child)
672 """ Méthode externe pour la destruction du noeud ET de l'objet
673 Gère l'update du canvas"""
674 if self.parent.item.isMCList():
675 pere = self.parent.parent
676 nbold = pere.get_nb_children()
677 if self.parent.delete_child(self):
678 self.parent.traite_mclist()
679 if self.item.get_position() == 'global':
680 self.etape.verif_all()
681 elif self.item.get_position() == 'global_jdc':
682 self.racine.verif_all()
684 self.parent.verif_condition()
685 nbnew = pere.get_nb_children()
688 nbold = pere.get_nb_children()
689 if self.parent.delete_child(self):
690 if self.item.get_position() == 'global':
691 self.etape.verif_all()
692 elif self.item.get_position() == 'global_jdc':
693 self.racine.verif_all()
695 self.parent.verif_condition()
697 print 'Erreur dans la destruction de ',self.item.get_nom(),' dans delete'
698 nbnew = pere.get_nb_children()
699 pere.redraw(nbnew-nbold)
702 def copynode(self,node,pos) :
703 """ node est le noeud à copier à la position pos de self ( = parent de node) """
704 objet_copie = node.item.get_copie_objet()
705 child = self.full_creation(node.item,pos)
706 child.displayed = node.displayed
707 #child.image_id = node.image_id
708 #child.label_id = node.label_id
709 if child.item.get_nature() == "MCList":
710 child.item.object[-1].mc_liste = objet_copie.mc_liste
713 child.item.object.mc_liste = objet_copie.mc_liste
715 traceback.print_exc()
716 #--------------------------------------------------------------
717 # Méthodes de vérification du contexte et de validité du noeud
718 #--------------------------------------------------------------
719 def traite_mclist_OLD(self):
720 """ Dans le cas d'une MCList il faut vérifier qu'elle n'est pas vide
721 ou réduite à un seul élément suite à une destruction
723 # self représente une MCList
724 print "on passe par traite_mclist ",len(self.item)
725 if len(self.item) == 0 :
726 # la liste est vide : il faut la supprimer
728 elif len(self.item) == 1:
729 # il ne reste plus qu'un élément dans la liste
730 # il faut supprimer la liste et créer directement l'objet
731 index = self.parent.children.index(self)
732 noeud = self.children[0]
733 if self.parent.delete_child(self):
734 self.parent.append_node_child(noeud.item,pos=index,verif='non')
736 print "destruction de self impossible !"
737 #if self.parent.delete_child(self):
738 # self.parent.copynode(self.children[0],index)
740 # print 'erreur dans la destruction de :',self.item.get_nom(),' dans traite_mclist'
744 def traite_mclist(self):
745 """ Dans le cas d'une MCList il faut vérifier qu'elle n'est pas vide
746 ou réduite à un seul élément suite à une destruction
748 # self représente une MCList
749 if len(self.item) == 0 :
750 # la liste est vide : il faut la supprimer
752 elif len(self.item) == 1:
753 # il ne reste plus qu'un élément dans la liste
754 # il faut supprimer la liste et créer directement l'objet
755 index = self.parent.children.index(self)
756 noeud = self.children[0]
757 noeud.parent = self.parent
758 self.parent.delete_node_child(self)
759 self.parent.item.replace_child(self.item,noeud.item)
760 self.parent.children.insert(index,noeud)
765 self.verif_all_children()
767 def verif_all_children(self):
768 if not self.children : self.build_children()
769 if self.nature != 'JDC' :
771 for child in self.children :
772 child.verif_all_children()
776 Lance la vérification des conditions des blocs de self et le cas
777 échéant redessine self
779 nbold = self.get_nb_children()
780 test = self.verif_condition()
781 nbnew = self.get_nb_children()
783 self.redraw(nbnew-nbold)
785 def verif_condition(self):
787 on lance la vérification des conditions de chaque bloc de self
788 on crée ou supprime les noeuds concernés
789 (self est d'un niveau inférieur ou égal à l'ETAPE)
791 if self.item.object.__class__.__name__ == 'ETAPE_NIVEAU': return 0
793 l_bloc_arajouter,l_bloc_aenlever = self.verif_condition_bloc()
794 if len(l_bloc_arajouter) > 0:
796 for mc in l_bloc_arajouter:
797 self.append_node_child(mc,verif='non')
798 if len(l_bloc_aenlever) > 0:
800 for mc in l_bloc_aenlever:
801 mocle = self.get_node_fils(mc)
802 self.delete_child(mocle)
803 l_mc_presents = self.item.get_liste_mc_presents()
804 l_mc_arajouter= self.verif_condition_regles(l_mc_presents)
805 if len(l_mc_arajouter) > 0:
807 for mc in l_mc_arajouter:
808 self.append_node_child(mc,verif='non')
809 if len(l_mc_arajouter)+len(l_bloc_arajouter)+len(l_bloc_aenlever) != 0 :
810 self.verif_condition()
813 def verif_condition_bloc(self):
814 return self.item.verif_condition_bloc()
816 def verif_condition_regles(self,l_mc_presents):
817 return self.item.verif_condition_regles(l_mc_presents)