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.8 2002/10/16 13:27:35 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
74 self.children[0].select()
75 self.resizescrollregion()
77 def deselectall(self):
78 """ déselectionne tous les éléments de l'arbre """
79 if self.node_selected :
80 self.node_selected.deselect()
83 """ Update tous les éléments de l'arbre """
84 for child in self.children:
87 def resizescrollregion(self):
88 self.scrolledcanvas.resizescrollregion()
90 def select_next(self,event):
91 self.node_selected.select_next()
93 def select_previous(self,event):
94 self.node_selected.select_previous()
96 def full_creation(self,name,index):
97 # A changer lorsqu'il y aura plusieurs jdc ouverts en même temps
98 self.children[0].full_creation(name,index)
101 for child in self.children :
102 self.verif_all_children()
105 def __init__(self,parent,item,command=None):
108 self.command = command
109 self.tree = self.parent.tree
110 self.appli = self.parent.appli
111 self.canvas = self.parent.canvas
113 #self.build_children()
116 self.state='collapsed'
119 self.x = self.y =None
123 # etape = noeud d'étape auquel appartient self
124 # = self si c'est lui-même
125 if isinstance(self.parent,Tree) :
126 # on est sur un noeud de JDC
130 elif isinstance(self.parent.parent,Tree) :
131 # on est sur un noeud d'étape
132 self.racine = self.parent
134 self.nature = 'ETAPE'
136 # on est sur un noeud de mot-clé
137 self.racine = self.parent.racine
138 self.etape=self.parent.etape
139 self.nature = 'MOTCLE'
141 def build_children(self):
142 """ Construit la liste des enfants de self """
144 sublist = self.item._GetSubList()
145 if not sublist : return
146 for item in sublist :
147 child = Node(self,item,self.command)
148 self.children.append(child)
150 #-----------------------------------------------
151 # Méthodes de sélection/déselection d'un noeud
152 #-----------------------------------------------
154 def select(self, event=None):
156 Rend le noeud courant (self) sélectionné et déselectionne
159 if not self.children : self.build_children()
160 self.tree.deselectall()
162 self.tree.node_selected = self
163 if self.command:apply(self.command,(self,))
166 def deselect(self, event=None):
167 """ Déselectionne self """
169 if self.displayed == 1 : self.dehighlight()
171 def make_visible(self):
172 """ Rend l'objet self visible cad déplace le scroll pour que self soit dans
173 la fenêtre de visu"""
174 x0,y0,x1,y1 = self.canvas.bbox(ALL)
175 self.canvas.yview("moveto",self.y/y1)
177 def select_next(self,ind=0):
178 """ on doit chercher à sélectionner dans l'ordre:
179 - son premier fils s'il est affiché
180 - son frère cadet s'il existe
181 - son oncle (benjamin de son père)
182 - ... appel récursif ...
184 if self.state=='expanded' and len(self.children) > ind:
185 self.children[ind].select()
187 index = self.parent.children.index(self) + 1
188 if isinstance(self.parent,TREE) :
190 self.children[ind].select()
192 self.children[0].select()
194 self.parent.select_next(index)
196 def select_previous(self):
197 """ on doit d'abord sélectionner(dans l'ordre) :
201 index = self.parent.children.index(self) + 1
203 self.parent.children[index].select()
206 #-----------------------------------------------
207 # Méthodes de recherche d'informations
208 #-----------------------------------------------
209 def geticonimage(self,name=None):
211 Retourne l'image qui doit être associée à self
214 name = self.item.GetIconName()
215 if not name or name == 'aucune' :
217 return images.get_image(name)
219 def get_nb_children(self):
220 """ Retourne le nombre d'enfants affichés de self """
222 if self.state =='collapsed' : return nb
223 for child in self.children :
224 nb = nb + 1 + child.get_nb_children()
227 def get_liste_id(self):
228 """ Retourne la liste de tous les id (filiation comprise) de self """
230 for child in self.children:
231 liste.extend(child.get_liste_id())
234 def get_node_fils(self,name) :
235 """ Retourne le fils de self de nom name s'il existe"""
236 for child in self.children:
237 if child.item.get_nom() == name: return child
239 #-----------------------------------------------
240 # Méthodes d'affichage d'un noeud
241 #-----------------------------------------------
243 """ Permet de tracer le noeud self """
244 # le début du noeud est en x,y
250 # choix de l'icone à afficher : + ou -
251 if self.item.IsExpandable():
252 if self.state == 'expanded':
253 iconname = "minusnode"
254 callback = self.collapse
256 iconname = "plusnode"
257 callback = self.expand
258 image = self.geticonimage(name=iconname)
259 self.icone_id = self.canvas.create_image(self.x, self.y, image=image)
260 self.canvas.tag_bind(self.icone_id, "<1>", callback)
261 self.id.append(self.icone_id)
262 # création de la ligne horizontale
263 self.ligne_id = self.canvas.create_line(self.x,self.y,self.x+10,self.y)
264 self.id.append(self.ligne_id)
265 self.canvas.tag_lower(self.ligne_id)
266 # affichage de l'icone (carre ,rond, ovale ...) de couleur
267 image = self.geticonimage()
269 self.image_id = self.canvas.create_image(self.x+15,self.y,image = image)
270 self.canvas.tag_bind(self.image_id,"<1>",self.select)
271 self.id.append(self.image_id)
274 # affichage du texte : nom de l'objet (ETAPE ou MOT-CLE) et sa valeur
276 if self.state == 'expanded' :
277 if not self.children : self.build_children()
278 if len(self.children) > 0:
280 self.lasty = self.children[-1].lasty
282 def drawchildren(self):
283 """ Dessine les enfants de self """
286 for child in self.children:
288 nb = child.get_nb_children()
293 """ Affiche les deux zones de texte après l'icône de couleur de l'objet """
294 if self.image_id != None :
299 # nom,fonte et couleur de l'objet du noeud à afficher
300 labeltext,fonte,couleur = self.item.GetLabelText()
301 if labeltext == '' : labeltext = ' '
302 if fonte == None : fonte = Fonte_Standard
303 if couleur == None : couleur = 'black'
304 # création du widget label
305 self.label = Label(self.canvas,
310 self.label_id = self.canvas.create_window(textx,texty,window=self.label,anchor='w')
311 self.id.append(self.label_id)
312 # bindings sur le widget label
313 self.label.bind("<1>", self.select)
314 self.label.bind("<Enter>",self.enter)
315 self.label.bind("<Leave>",self.leave)
316 # valeur de cet objet à afficher
317 x0, y0, x1, y1 = self.canvas.bbox(self.label_id)
318 textx = max(x1, 200) + 10
319 text = self.item.GetText() or " "
320 self.text = Label(self.canvas, text=text,
321 bd=0, padx=2, pady=2,background='gray95',
327 self.text_id = self.canvas.create_window(textx, texty,anchor="w", window=self.text)
328 self.id.append(self.text_id)
330 def highlight(self,event=None):
331 """ Met en surbrillance self"""
332 if hasattr(self,'label'):
333 self.label.configure(fg='white',bg='#00008b')
335 def dehighlight(self,event=None):
336 """ Rétablit l'affichage normal de self"""
337 if hasattr(self,'label'):
338 self.label.configure(fg='black',bg='gray95')
340 def enter(self,event=None):
341 """ Met en surbrillance self et affiche le fr de l'objet """
343 fr = self.item.get_fr()
344 self.appli.affiche_infos(fr)
346 def leave(self,event=None):
347 """ Rétablit l'affichage normal de self et efface le fr de l'objet """
348 if not self.selected :
350 self.appli.affiche_infos('')
352 def collapse_children(self):
353 """ Collapse récursivement tous les descendants de self """
354 if not self.children : return
355 for child in self.children:
356 child.state='collapsed'
358 child.collapse_children()
360 def collapse(self,event = None):
361 """ Collapse self et descendants et retrace self """
362 nb = self.get_nb_children()
363 self.state = 'collapsed'
364 self.collapse_children()
368 def expand(self,event = None):
369 """ Expanse self et le retrace """
370 if not self.item.isactif() : return
371 if not self.children : self.build_children()
372 self.state = 'expanded'
373 nb = self.get_nb_children()
378 """ Redessine self : nb est le décalage à introduire
379 en dessous de self pour le redessiner """
380 # nb = nombre d'items de décalage
382 # on efface self et on le redessine
384 self.draw(self.x,self.y)
385 # Il n'est pas nécessaire d'appeler update
386 # il suffit d'updater les coordonnees et de retracer les lignes
387 self.racine.update_coords()
388 self.racine.trace_ligne()
390 def update_coords(self):
391 """ Permet d'updater les coordonnes de self et de tous ses enfants"""
392 if self.displayed == 0 : return
393 if self.image_id != None :
394 coords = self.canvas.coords(self.image_id)
395 self.x = coords[0]-15
397 coords = self.canvas.coords(self.label_id)
398 self.x = coords[0]-15
400 if self.state == 'expanded' :
401 for child in self.children:
402 if child.displayed != 0:
403 child.update_coords()
405 def update_icone(self):
406 """ Met à jour les icônes de tous les noeuds : teste la validité de l'objet
407 Cette méthode est très lente, trop !!"""
408 if self.image_id != None :
409 image = self.geticonimage()
410 self.canvas.itemconfig(self.image_id,image=image)
411 if self.state == 'expanded':
412 for child in self.children:
413 if child.displayed != 0:
416 def update_texte(self):
417 """ Met à jour les noms des SD et valeurs des mots-clés """
418 text = self.item.GetText()
419 if text == None : text = ''
420 self.text.configure(text=text)
421 if self.state == 'expanded' :
422 for child in self.children:
423 if child.displayed != 0 : child.update_texte()
425 def update(self,event=None) :
427 Cette méthode est appelée pour demander l update d un noeud
428 d'un jeu de commandes
429 Cette demande est transmise au noeud racine (le JDC) qui update
430 tout l arbre représentant le jeu de commandes
431 Pendant cette mise à jour, on appelle la méthode isvalid qui
432 fera l update de tous les objets déclarés modifiés lors des
434 La métode isvalid est en général appelée par l intermédiaire de
435 update_icone -> geticonimage -> GetIconName
437 self.racine.update_coords()
438 self.racine.trace_ligne()
439 self.racine.update_icone()
440 self.racine.update_texte()
441 self.tree.resizescrollregion()
444 """ Efface du canvas les id associés à self : cad les siens et ceux
447 self.canvas.delete(id)
448 if not self.children : return
449 for child in self.children:
453 """ Déplace de l'incrément dy tous les id en dessous de self """
454 # il faut marquer tous les suivants de self
455 bbox1 = self.canvas.bbox(ALL)
456 self.canvas.dtag(ALL,'move')
457 self.canvas.delete('line')
459 self.canvas.addtag_overlapping('move',bbox1[0],self.y +10,bbox1[2],bbox1[3])
461 print "Erreur dans move :"
464 print self.item.object
465 print self.item.object.definition.label
468 # on déplace tous les items de dy
469 self.canvas.move('move',0,dy)
470 # il faut réactualiser la zone de scroll
471 self.tree.resizescrollregion()
473 def trace_ligne(self):
474 """ Dessine les lignes verticales entre frères et entre père et premier fils"""
475 if self.state=='collapsed' : return
476 #if self.displayed == 0 : return
477 if len(self.children)==0 : return
478 # on est bien dans le cas d'un noeud expansé avec enfants ...
479 # il faut rechercher l'ordonnée du dernier fils de self
480 y_end = self.children[-1].y
481 ligne = self.canvas.create_line(self.x+15,self.y,self.x+15,y_end,tags='line')
482 self.canvas.tag_lower(ligne)
483 for child in self.children :
487 print "Erreur dans trace_ligne :"
489 print child.item.object
491 #------------------------------------------------------------------
492 # Méthodes de création et destruction de noeuds
493 # Certaines de ces méthodes peuvent être appelées depuis l'externe
494 #------------------------------------------------------------------
495 def replace_node(self,node1,node2):
496 """ Remplace le noeud 1 par le noeud 2 dans la liste des enfants de self"""
497 index= self.children.index(node1)
498 self.delete_node_child(node1)
499 self.children.insert(index,node2)
501 def full_creation(self,name,pos=None):
503 Interface avec ACCAS : création de l'objet de nom name et
504 du noeud associé. Retourne le noeud fils ainsi créé
506 item = self.item.additem(name,pos)
507 if item == None or item == 0:
508 # impossible d'ajouter le noeud de nom : name
510 nature = item.get_nature()
511 if nature in ("COMMANDE","OPERATEUR","PROCEDURE","COMMENTAIRE",
512 "PARAMETRE","COMMANDE_COMMENTARISEE","PARAMETRE_EVAL"):
513 # on veut ajouter une commande ou un commentaire ou un paramètre
514 # il ne faut pas rechercher un même objet déjà existant
515 # à modifier : il faut tester l'attribut 'repetable'
517 elif self.item.object.isMCList():
518 # Dans ce cas on ne fait pas de remplacement. On ne cherche pas un objet de meme nom
521 enfant = self.get_node_fils(item.get_nom())
523 # un fils de même nom existe déjà : on remplace
524 # un MCFACT (ou une MCList) par une (autre) MCList
525 child = Node(self,item,self.command)
526 self.replace_node(enfant,child)
528 child = Node(self, item,self.command)
530 self.children.append(child)
532 self.children.insert(pos,child)
535 def append_brother(self,name,pos='after',retour='non'):
537 Permet d'ajouter un frère à self
538 par défaut on l'ajoute après self
541 # on veut ajouter le frère de nom name directement avant ou après self
542 index = self.parent.children.index(self)
548 print str(pos)," n'est pas un index valide pour append_brother"
550 return self.parent.append_child(name,pos=index,retour=retour)
552 def append_node_child(self,fils,pos=None,verif='oui'):
554 Fait appel à la création complète de fils et à la vérification
555 des conditions en fonction du contexte
556 Attention : fils peut être un nom ou déjà un object (cas d'une copie)
558 if not self.children : self.build_children()
560 if type(fils) == types.InstanceType:
561 pos = self.item.get_index_child(fils.nom)
563 pos = self.item.get_index_child(fils)
564 child = self.full_creation(fils,pos)
566 # on n'a pas pu créer le noeud fils
568 self.state = 'expanded'
570 if child.item.isactif():
571 child.state = 'expanded'
572 if not child.children : child.build_children()
574 test = child.item.isMCList()
576 child.children[-1].verif_condition()
578 child.verif_condition()
579 self.verif_condition()
582 def append_child(self,name,pos=None,verif='oui',retour='non'):
584 Permet d'ajouter un fils à self
585 on peut l'ajouter en fin de liste (défaut) ou en début
591 index = len(self.children)
592 elif pos != None and type(pos) == types.IntType :
593 # on donne la position depuis l'extérieur
594 # (appel de append_child par append_brother par exemple)
596 elif type(pos) == types.InstanceType:
597 # pos est un item. Il faut inserer name apres pos
598 index = self.item.get_index(pos) +1
600 if type(name) == types.InstanceType:
601 index = self.item.get_index_child(name.nom)
603 index = self.item.get_index_child(name)
604 nbold = self.get_nb_children()
605 self.state='expanded'
606 child = self.append_node_child(name,pos=index)
608 # on n'a pas pu créer le fils
610 nbnew = self.get_nb_children()
611 self.redraw(nbnew-nbold)
613 if retour == 'oui': return child
615 def delete_node_child(self,child):
616 """ Supprime child des enfants de self et les id associés """
619 self.children.remove(child)
622 def delete_child(self,child):
624 Supprime child des enfants de self, tous les id associés
627 if self.item.suppitem(child.item):
628 self.delete_node_child(child)
634 """ Méthode externe pour la destruction du noeud ET de l'objet
635 Gère l'update du canvas"""
636 if self.parent.item.isMCList():
637 pere = self.parent.parent
638 nbold = pere.get_nb_children()
639 if self.parent.delete_child(self):
640 self.parent.traite_mclist()
641 if self.item.get_position() == 'global':
642 self.etape.verif_all()
643 elif self.item.get_position() == 'global_jdc':
644 self.racine.verif_all()
646 self.parent.verif_condition()
647 nbnew = pere.get_nb_children()
650 nbold = pere.get_nb_children()
651 if self.parent.delete_child(self):
652 if self.item.get_position() == 'global':
653 self.etape.verif_all()
654 elif self.item.get_position() == 'global_jdc':
655 self.racine.verif_all()
657 self.parent.verif_condition()
659 print 'Erreur dans la destruction de ',self.item.get_nom(),' dans delete'
660 nbnew = pere.get_nb_children()
661 pere.redraw(nbnew-nbold)
664 def copynode(self,node,pos) :
665 """ node est le noeud à copier à la position pos de self ( = parent de node) """
666 objet_copie = node.item.get_copie_objet()
667 child = self.full_creation(node.item,pos)
668 child.displayed = node.displayed
669 #child.image_id = node.image_id
670 #child.label_id = node.label_id
671 if child.item.get_nature() == "MCList":
672 child.item.object[-1].mc_liste = objet_copie.mc_liste
675 child.item.object.mc_liste = objet_copie.mc_liste
677 traceback.print_exc()
678 #--------------------------------------------------------------
679 # Méthodes de vérification du contexte et de validité du noeud
680 #--------------------------------------------------------------
681 def traite_mclist_OLD(self):
682 """ Dans le cas d'une MCList il faut vérifier qu'elle n'est pas vide
683 ou réduite à un seul élément suite à une destruction
685 # self représente une MCList
686 print "on passe par traite_mclist ",len(self.item)
687 if len(self.item) == 0 :
688 # la liste est vide : il faut la supprimer
690 elif len(self.item) == 1:
691 # il ne reste plus qu'un élément dans la liste
692 # il faut supprimer la liste et créer directement l'objet
693 index = self.parent.children.index(self)
694 noeud = self.children[0]
695 if self.parent.delete_child(self):
696 self.parent.append_node_child(noeud.item,pos=index,verif='non')
698 print "destruction de self impossible !"
699 #if self.parent.delete_child(self):
700 # self.parent.copynode(self.children[0],index)
702 # print 'erreur dans la destruction de :',self.item.get_nom(),' dans traite_mclist'
706 def traite_mclist(self):
707 """ Dans le cas d'une MCList il faut vérifier qu'elle n'est pas vide
708 ou réduite à un seul élément suite à une destruction
710 # self représente une MCList
711 if len(self.item) == 0 :
712 # la liste est vide : il faut la supprimer
714 elif len(self.item) == 1:
715 # il ne reste plus qu'un élément dans la liste
716 # il faut supprimer la liste et créer directement l'objet
717 index = self.parent.children.index(self)
718 noeud = self.children[0]
719 noeud.parent = self.parent
720 self.parent.delete_node_child(self)
721 self.parent.item.replace_child(self.item,noeud.item)
722 self.parent.children.insert(index,noeud)
727 self.verif_all_children()
729 def verif_all_children(self):
730 if not self.children : self.build_children()
731 if self.nature != 'JDC' :
733 for child in self.children :
734 child.verif_all_children()
738 Lance la vérification des conditions des blocs de self et le cas
739 échéant redessine self
741 nbold = self.get_nb_children()
742 test = self.verif_condition()
743 nbnew = self.get_nb_children()
745 self.redraw(nbnew-nbold)
747 def verif_condition(self):
749 on lance la vérification des conditions de chaque bloc de self
750 on crée ou supprime les noeuds concernés
751 (self est d'un niveau inférieur ou égal à l'ETAPE)
753 if self.item.object.__class__.__name__ == 'ETAPE_NIVEAU': return 0
755 l_bloc_arajouter,l_bloc_aenlever = self.verif_condition_bloc()
756 if len(l_bloc_arajouter) > 0:
758 for mc in l_bloc_arajouter:
759 self.append_node_child(mc,verif='non')
760 if len(l_bloc_aenlever) > 0:
762 for mc in l_bloc_aenlever:
763 mocle = self.get_node_fils(mc)
764 self.delete_child(mocle)
765 l_mc_presents = self.item.get_liste_mc_presents()
766 l_mc_arajouter= self.verif_condition_regles(l_mc_presents)
767 if len(l_mc_arajouter) > 0:
769 for mc in l_mc_arajouter:
770 self.append_node_child(mc,verif='non')
771 if len(l_mc_arajouter)+len(l_bloc_arajouter)+len(l_bloc_aenlever) != 0 :
772 self.verif_condition()
775 def verif_condition_bloc(self):
776 return self.item.verif_condition_bloc()
778 def verif_condition_regles(self,l_mc_presents):
779 return self.item.verif_condition_regles(l_mc_presents)