1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2007-2021 EDF R&D
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License.
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # Lesser General Public License for more details.
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 from __future__ import absolute_import
24 from Extensions.i18n import tr
25 from Extensions.eficas_exception import EficasException
27 from Noyau.N_utils import repr_float
29 from . import CONNECTOR
31 # Attention : les classes ASSD,.... peuvent etre surchargees
32 # dans le package Accas. Il faut donc prendre des precautions si
33 # on utilise les classes du Noyau pour faire des tests (isxxxx, ...)
34 # Si on veut creer des objets comme des CO avec les classes du noyau
35 # ils n'auront pas les conportements des autres packages (pb!!!)
36 # Il vaut mieux les importer d'Accas mais probleme d'import circulaire,
37 # on ne peut pas les importer au debut.
38 # On fait donc un import local quand c'est necessaire (peut occasionner
39 # des pbs de prformance).
40 from Noyau.N_ASSD import ASSD,assd
41 from Noyau.N_GEOM import GEOM,geom
42 from Noyau.N_CO import CO
43 from Accas.A_ASSD import UserASSD
44 from Accas.A_ASSD import UserASSDMultiple
48 from Extensions import parametre
49 from Extensions import param2
50 from . import I_OBJECT
51 from . import CONNECTOR
52 from .I_VALIDATOR import ValError,listProto
54 class MCSIMP(I_OBJECT.OBJECT):
57 def isValid(self,cr='non'):
58 if self.state == 'unchanged':
60 for type_permis in self.definition.type:
61 #if hasattr(type_permis, "__class__") and type_permis.__class__.__name__ == 'Matrice':
62 if hasattr(type_permis, "typElt") :
63 self.monType=type_permis
64 return self.valideMatrice(cr=cr)
65 validite=Validation.V_MCSIMP.MCSIMP.isValid(self,cr=cr)
67 if self.definition.siValide != None and validite:
68 self.definition.siValide(self)
72 def getNomConcept(self):
76 nomconcept=p.getSdname()
80 nomconcept= p.object.getSdname()
89 Retourne le texte a afficher dans l'arbre representant la valeur de l'objet
93 if self.valeur == None :
95 elif type(self.valeur) == float :
96 # traitement d'un flottant isole
97 txt = str(self.valeur)
98 clefobj=self.getNomConcept()
99 if clefobj in self.jdc.appliEficas.dict_reels :
100 if self.valeur in self.jdc.appliEficas.dict_reels[clefobj]:
101 txt=self.jdc.appliEficas.dict_reels[clefobj][self.valeur]
102 elif type(self.valeur) in (list,tuple) :
103 if self.valeur==[] or self.valeur == (): return str(self.valeur)
104 # traitement des listes
107 for val in self.valeur:
108 if type(val) == float :
109 clefobj=self.getNomConcept()
110 if clefobj in self.jdc.appliEficas.dict_reels:
111 if val in self.jdc.appliEficas.dict_reels[clefobj]:
112 txt=txt + sep +self.jdc.appliEficas.dict_reels[clefobj][val]
114 txt=txt + sep + str(val)
116 txt=txt + sep + str(val)
118 if isinstance(val,tuple):
121 if isinstance(i, bytes) or isinstance(i,str) : texteVal = texteVal +"'"+str(i)+"',"
122 else : texteVal = texteVal + str(i)+','
123 texteVal=texteVal[:-1]+')'
125 if isinstance(val,bytes) or isinstance(val,str): texteVal="'"+str(val)+"'"
126 else :texteVal=str(val)
127 txt = txt + sep+ texteVal
129 ## if len(txt) > 200:
130 ## #ligne trop longue, on tronque
134 # cas des listes de tuples de longueur 1
135 if isinstance(val,tuple) and len(self.valeur) == 1 : txt=txt+','
138 # traitement des autres cas
139 txt = str(self.valeur)
141 # txt peut etre une longue chaine sur plusieurs lignes.
142 # Il est possible de tronquer cette chaine au premier \n et
143 # de limiter la longueur de la chaine a 30 caracteres. Cependant
144 # ceci provoque une perte d'information pour l'utilisateur
145 # Pour le moment on retourne la chaine telle que
150 Retourne une chaine de caractere representant la valeur de self
153 if type(val) == float :
154 clefobj=self.getNomConcept()
155 if clefobj in self.jdc.appliEficas.dict_reels :
156 if val in self.jdc.appliEficas.appliEficas.dict_reels[clefobj] :
157 return self.jdc.appliEficas.dict_reels[clefobj][val]
158 if type(val) != tuple :
164 if val ==() or val == [] : return val
168 s=s+item.getName()+','
175 for typ in self.definition.type:
177 if typ == bool: return True
184 Methode booleenne qui retourne 1 si l'objet attend un objet ASSD
185 qui n'existe pas encore (type CO()), 0 sinon
187 for typ in self.definition.type:
188 if type(typ) == type or isinstance(typ,type):
189 if issubclass(typ,CO) :
195 Methode booleenne qui retourne 1 si le MCS attend un objet de type ASSD ou UserASSD
198 for typ in self.definition.type:
199 if type(typ) == type or isinstance(typ,type):
200 if issubclass(typ,ASSD) and not issubclass(typ,GEOM) :
204 def waitUserAssd(self):
206 Methode booleenne qui retourne 1 si le MCS attend un objet de type ASSD
209 for typ in self.definition.type:
210 if type(typ) == type or isinstance(typ,type):
211 if issubclass(typ,UserASSD) :
215 def waitUserAssdMultiple(self):
216 for typ in self.definition.type:
217 if type(typ) == type or isinstance(typ,type):
218 if issubclass(typ,UserASSDMultiple) :
222 def waitUserAssdOrAssdMultipleEnCreation(self):
223 for typ in self.definition.type:
224 if typ == 'createObject' :
229 def waitAssdOrGeom(self):
231 Retourne 1 si le mot-cle simple attend un objet de type
232 assd, ASSD, geom ou GEOM
233 Retourne 0 dans le cas contraire
235 for typ in self.definition.type:
236 if type(typ) == type or isinstance(typ,type):
237 if typ.__name__ in ("GEOM","ASSD","geom","assd") or issubclass(typ,GEOM) :
243 Retourne 1 si le mot-cle simple attend un objet de type GEOM
244 Retourne 0 dans le cas contraire
246 for typ in self.definition.type:
247 if type(typ) == type or isinstance(typ,type):
248 if issubclass(typ,GEOM) : return 1
254 Retourne 1 si le mot-cle simple attend un objet de type TXM
255 Retourne 0 dans le cas contraire
257 for typ in self.definition.type:
258 if typ == 'TXM' :return 1
262 for ss_type in self.definition.type:
263 if repr(ss_type).find('Tuple') != -1 :
267 def waitChaineAvecBlancs(self):
268 if self.definition.avecBlancs : return 1
271 def combienEltDsTuple(self):
272 for ss_type in self.definition.type:
273 if hasattr(ss_type,'ntuple'):
274 return ss_type.ntuple
277 def waitMatrice(self):
278 if hasattr(self, 'isAMatrice') : return self.isAMatrice
279 for typ in self.definition.type:
281 if hasattr(typ, 'typElt') : self.isAMatrice=1; return 1
286 def getListeValeurs(self):
289 if self.valeur == None:
291 elif type(self.valeur) == tuple:
292 return list(self.valeur)
293 elif type(self.valeur) == list:
299 return self.definition.statut=='o'
301 def isImmuable(self):
302 return self.definition.homo=='constant'
304 def isInformation(self):
305 return self.definition.homo=='information'
308 def validVal(self,valeur):
310 Verifie que la valeur passee en argument (valeur) est valide
311 sans modifier la valeur courante
313 lval=listProto.adapt(valeur)
316 mess=tr("None n'est pas une valeur autorisee")
320 self.typeProto.adapt(val)
321 self.intoProto.adapt(val)
322 self.cardProto.adapt(lval)
323 if self.definition.validators:
324 self.definition.validators.convert(lval)
326 except ValError as e:
331 def validValeur(self,new_valeur):
333 Verifie que la valeur passee en argument (new_valeur) est valide
334 sans modifier la valeur courante (evite d'utiliser setValeur et est plus performant)
336 validite,mess=self.validVal(new_valeur)
339 def validValeurPartielle(self,new_valeur):
341 Verifie que la valeur passee en argument (new_valeur) est une liste partiellement valide
342 sans modifier la valeur courante du mot cle
346 for val in new_valeur:
347 self.typeProto.adapt(val)
348 self.intoProto.adapt(val)
349 #on ne verifie pas la cardinalite
350 if self.definition.validators:
351 validite=self.definition.validators.valideListePartielle(new_valeur)
352 except ValError as e:
357 def updateConditionBloc(self):
358 """ Met a jour les blocs conditionnels dependant du mot cle simple self
360 if self.definition.position == 'global' :
361 self.etape.deepUpdateConditionBloc()
362 elif self.definition.position == 'reCalculeEtape' :
363 print ('je passe par la pour ', self.nom)
364 self.etape.deepUpdateConditionBloc()
365 self.etape.demandeRedessine()
366 elif self.definition.position == 'global_jdc' :
367 self.jdc.deepUpdateConditionBloc(self)
368 self.etape.demandeRedessine()
369 elif self.definition.position == 'inGetAttribut' :
370 self.jdc.deepUpdateConditionBloc(self)
372 self.parent.updateConditionBloc()
374 def setValeur(self,new_valeur,evaluation='oui'):
376 self.valeur = new_valeur
377 self.val = new_valeur
378 if self.valeur and self.waitUserAssd() and not(self.waitUserAssdOrAssdMultipleEnCreation()) :
379 if type(self.valeur) in (list,tuple):
380 for v in self.valeur : v.ajoutUtilisePar(self)
381 else : self.valeur.ajoutUtilisePar(self)
382 if self.isValid() and hasattr(self, 'objPyxb') and self.objPyxb : self.setValeurObjPyxb(new_valeur)
383 self.updateConditionBloc()
384 if self.definition.metAJour != None : self.updateAutresMotsClefs()
385 self.etape.modified()
389 def evalValeur(self,new_valeur):
391 Essaie d'evaluer new_valeur comme une SD, une declaration Python
392 ou un EVAL: Retourne la valeur evaluee (ou None) et le test de reussite (1 ou 0)
394 sd = self.jdc.getSdAvantEtape(new_valeur,self.etape)
395 #sd = self.jdc.getContexteAvant(self.etape).get(new_valeur,None)
398 lsd = self.jdc.chercheListAvant(self.etape,new_valeur)
403 # On veut EVAL avec tous ses comportements. On utilise Accas. Perfs ??
406 objet = eval(new_valeur,d)
409 itparam=self.chercheItemParametre(new_valeur)
413 object=eval(new_valeur.valeur,d)
416 if CONTEXT.debug : traceback.print_exc()
419 def evalVal(self,new_valeur):
421 Tente d'evaluer new_valeur comme un objet du jdc (par appel a evalValItem)
422 ou comme une liste de ces memes objets
423 Si new_valeur contient au moins un separateur (,), tente l'evaluation sur
426 if new_valeur in ('True','False') and 'TXM' in self.definition.type :
427 valeur=self.evalValItem(str(new_valeur))
429 if type(new_valeur) in (list,tuple):
431 for item in new_valeur :
432 valeurretour.append(self.evalValItem(item))
435 valeur=self.evalValItem(new_valeur)
438 def evalValItem(self,new_valeur):
440 Tente d'evaluer new_valeur comme un concept, un parametre, un objet Python ou un UserASSD
441 Si c'est impossible retourne new_valeur inchange
442 argument new_valeur : string (nom de concept, de parametre, expression ou simple chaine)
444 if new_valeur in list(self.jdc.sdsDict.keys()) and self.waitUserAssd():
445 valeur=self.jdc.sdsDict[new_valeur]
447 elif self.etape and self.etape.parent:
448 valeur=self.etape.parent.evalInContext(new_valeur,self.etape)
455 #traceback.print_exc()
459 def chercheItemParametre (self,new_valeur):
461 nomparam=new_valeur[0:new_valeur.find("[")]
462 indice=new_valeur[new_valeur.find(u"[")+1:new_valeur.find(u"]")]
463 for p in self.jdc.params:
464 if p.nom == nomparam :
465 if int(indice) < len(p.getValeurs()):
466 itparam=parametre.ITEM_PARAMETRE(p,int(indice))
472 def updateConcept(self,sd):
473 if not self.waitAssd() : return
474 if type(self.valeur) in (list,tuple) :
475 if sd in self.valeur:
477 for v in self.valeur : newVal.append(v.nom)
479 if hasattr(self, 'objPyxb') and self.objPyxb : self.setValeurObjPyxb(newVal)
482 if sd == self.valeur:
484 if hasattr(self, 'objPyxb') and self.objPyxb : self.setValeurObjPyxb(sd.nom)
487 def deleteConcept(self,sd):
492 Met a jour la valeur du mot cle simple suite a la disparition
494 Attention aux matrices
497 if type(self.valeur) == tuple :
498 if sd in self.valeur:
500 self.valeur=list(self.valeur)
501 while sd in self.valeur : self.valeur.remove(sd)
502 if hasattr(self, 'objPyxb') and self.objPyxb :
504 for v in self.valeur : newVal.append(v.nom)
505 if newVal == [] : self.delObjPyxb()
506 else : self.setValeurObjPyxb(sd.nom)
508 elif type(self.valeur) == list:
509 if sd in self.valeur:
511 while sd in self.valeur : self.valeur.remove(sd)
514 if self.valeur == sd:
518 if hasattr(self, 'objPyxb') and self.objPyxb : self.setValeurObjPyxb()
520 # Glut Horrible pour les matrices OT ???
521 #if sd.__class__.__name__== "variable":
522 # for type_permis in self.definition.type:
523 #if type(type_permis) == types.InstanceType:
525 # if type_permis.__class__.__name__ == 'Matrice' :
526 # self.state="changed"
530 def replaceConcept(self,old_sd,sd):
533 - old_sd=concept remplace
536 Met a jour la valeur du mot cle simple suite au remplacement
539 #print ("replaceConcept",old_sd,sd)
540 if type(self.valeur) == tuple :
541 if old_sd in self.valeur:
543 self.valeur=list(self.valeur)
544 i=self.valeur.index(old_sd)
547 elif type(self.valeur) == list:
548 if old_sd in self.valeur:
550 i=self.valeur.index(old_sd)
554 if self.valeur == old_sd:
560 def setValeurCo(self,nomCO):
562 Affecte a self l'objet de type CO et de nom nomCO
564 step=self.etape.parent
565 if nomCO == None or nomCO == '':
568 # Avant de creer un concept il faut s'assurer du contexte : step
570 sd= step.getSdAutourEtape(nomCO,self.etape,avec='oui')
572 # Si un concept du meme nom existe deja dans la portee de l'etape
573 # on ne cree pas le concept
574 return 0,tr("un concept de meme nom existe deja")
575 # Il n'existe pas de concept de meme nom. On peut donc le creer
576 # Il faut neanmoins que la methode NommerSdProd de step gere les
577 # contextes en mode editeur
578 # Normalement la methode du Noyau doit etre surchargee
579 # On declare l'etape du mot cle comme etape courante pour nommerSDProd
580 cs= CONTEXT.getCurrentStep()
581 CONTEXT.unsetCurrentStep()
582 CONTEXT.setCurrentStep(step)
583 step.setEtapeContext(self.etape)
584 new_objet = Accas.CO(nomCO)
585 CONTEXT.unsetCurrentStep()
586 CONTEXT.setCurrentStep(cs)
588 self.valeur = new_objet
590 # On force l'enregistrement de new_objet en tant que concept produit
591 # de la macro en appelant getType_produit avec force=1
592 self.etape.getType_produit(force=1)
595 #print "setValeurCo",new_objet
596 return 1,tr("Concept cree")
598 def verifExistenceSd(self):
600 Verifie que les structures de donnees utilisees dans self existent bien dans le contexte
601 avant etape, sinon enleve la referea ces concepts
603 #print "verifExistenceSd"
604 # Attention : possible probleme avec include
605 # A priori il n'y a pas de raison de retirer les concepts non existants
606 # avant etape. En fait il s'agit uniquement eventuellement de ceux crees par une macro
607 l_sd_avant_etape = list(self.jdc.getContexteAvant(self.etape).values())
608 if type(self.valeur) in (tuple,list) :
610 for sd in self.valeur:
611 if isinstance(sd,ASSD) :
612 if sd in l_sd_avant_etape or self.etape.getSdprods(sd.nom) is sd:
616 if len(l) < len(self.valeur):
621 if isinstance(self.valeur,ASSD) :
622 if self.valeur not in l_sd_avant_etape and self.etape.getSdprods(self.valeur.nom) is None:
627 def renommeSdCree(self,nouveauNom):
628 #print ( 'dans renommeSdCree', self.jdc.sdsDict, self.valeur)
629 if nouveauNom in self.jdc.sdsDict : return (0, 'concept deja existant')
630 if self.valeur == None : return (0, 'pb sur la valeur')
631 else : self.valeur.renomme(nouveauNom)
632 return (1, 'concept renomme')
634 def renommeSdCreeDsListe(self,objASSD, nouveauNom):
635 #print ( 'dans renommeSdCree', self.jdc.sdsDict, self.valeur, nouveauNom)
636 if nouveauNom in self.jdc.sdsDict : return (0, 'concept deja existant')
637 objASSD.renomme(nouveauNom)
638 return (1, 'concept renomme')
644 Retourne les valeurs min et max admissibles pour la valeur de self
646 return self.definition.min,self.definition.max
651 Retourne le type attendu par le mot-cle simple
653 return self.definition.type
655 def deleteMcGlobal(self):
656 """ Retire self des declarations globales
658 # on est oblige de verifier si le nom est dans etape
659 # car parfois l ordre des creations/destruction n est pas clair
660 # quand on a des blocs freres qui contiennent le meme mc global
661 # cas de NumericalMethod dans VIMMP
662 if self.definition.position == 'global' :
663 etape = self.getEtape()
664 if etape and self.nom in etape.mc_globaux:
665 if etape.mc_globaux[self.nom] == self :
666 del etape.mc_globaux[self.nom]
667 elif self.definition.position == 'reCalculeEtape' :
668 etape = self.getEtape()
670 if self.nom in etape.mc_globaux:
671 if etape.mc_globaux[self.nom] == self :
672 del etape.mc_globaux[self.nom]
673 self.etape.doitEtreRecalculee = True
674 #print ('deleteMcGlobal je mets doitEtreRecalculee = True avec', self.nom ,' pour ', etape.nom)
675 elif self.definition.position == 'global_jdc' :
676 if self.nom in self.jdc.mc_globaux:
677 try : del self.jdc.mc_globaux[self.nom]
678 except : print ('!!!!!!!! Souci delete mc_globaux')
680 def updateMcGlobal(self):
682 Met a jour les mots cles globaux enregistres dans l'etape parente
683 et dans le jdc parent.
684 Un mot cle simple peut etre global.
686 if self.definition.position == 'global' :
687 etape = self.getEtape()
689 etape.mc_globaux[self.nom]=self
690 elif self.definition.position == 'reCalculeEtape' :
691 etape = self.getEtape()
693 etape.mc_globaux[self.nom]=self
694 etape.doitEtreRecalculee=True
695 print ('je mets doitEtreRecalculee = True avec', self.nom ,' pour ', etape.nom)
696 print ('j ajoute au mc_globaux')
697 elif self.definition.position == 'global_jdc' :
699 self.jdc.mc_globaux[self.nom]=self
701 def nbrColonnes(self):
702 genea = self.getGenealogie()
703 if "VALE_C" in genea and "DEFI_FONCTION" in genea : return 3
704 if "VALE" in genea and "DEFI_FONCTION" in genea : return 2
707 def valideItem(self,item):
708 """Valide un item isole. Cet item est candidata l'ajout a la liste existante"""
712 self.typeProto.adapt(item)
713 #on verifie les choix possibles
714 self.intoProto.adapt(item)
715 #on ne verifie pas la cardinalite
716 if self.definition.validators:
717 valid=self.definition.validators.verifItem(item)
718 except ValError as e:
719 #traceback.print_exc()
723 def verifType(self,item):
724 """Verifie le type d'un item de liste"""
727 self.typeProto.adapt(item)
728 #on verifie les choix possibles
729 self.intoProto.adapt(item)
730 #on ne verifie pas la cardinalite mais on verifie les validateurs
731 if self.definition.validators:
732 valid=self.definition.validators.verifItem(item)
735 except ValError as e:
736 #traceback.print_exc()
737 comment=tr(e.__str__())
741 def valideMatrice(self,cr):
744 if self.valeur == None :
748 if self.monType.methodeCalculTaille != None :
749 MCSIMP.__dict__[self.monType.methodeCalculTaille](*(self,))
751 if len(self.valeur) == self.monType.nbLigs :
752 for i in range(len(self.valeur)):
753 if len(self.valeur[i]) != self.monType.nbCols: ok=0
759 self.cr.fatal(tr("La matrice n'est pas une matrice %(n_lign)d sur %(n_col)d", \
760 {'n_lign': self.monType.nbLigs, 'n_col': self.monType.nbCols}))
763 for i in range(self.monType.nbLigs):
764 for j in range(self.monType.nbCols):
765 val=self.valeur[i][j]
766 ok, commentaire = self.monType.verifItem(str(val),self.parent)
767 if self.monType.typElt not in ('TXM','I','R') and type(val) != self.monType.typElt :
768 ok=0; commentaire = 'mauvais type'; self.valeur=None
772 self.cr.fatal(tr(commentaire))
779 def valideMatriceOT(self,cr):
780 #Attention, la matrice contient comme dernier tuple l ordre des variables
781 if self.valideEnteteMatrice()==False :
783 if cr == "oui" : self.cr.fatal(tr("La matrice n'a pas le bon entete"))
785 if self.monType.methodeCalculTaille != None :
786 MCSIMP.__dict__[self.monType.methodeCalculTaille](*(self,))
790 if len(self.valeur) == self.monType.nbLigs +1:
792 for i in range(len(self.valeur) -1):
793 if len(self.valeur[i])!= self.monType.nbCols:
802 self.cr.fatal(tr("La matrice n'est pas une matrice %(n_lign)d sur %(n_col)d", \
803 {'n_lign': self.monType.nbLigs, 'n_col': self.monType.nbCols}))
808 def nbDeVariables(self):
809 listeVariables=self.jdc.getVariables(self.etape)
810 self.monType.nbLigs=len(listeVariables)
811 self.monType.nbCols=len(listeVariables)
813 def valideEnteteMatrice(self):
814 if self.jdc.getDistributions(self.etape) == () or self.valeur == None : return 0
815 if self.jdc.getDistributions(self.etape) != self.valeur[0] : return 0
818 def changeEnteteMatrice(self):
819 a=[self.jdc.getDistributions(self.etape),]
820 for t in self.valeur[1:]:
825 def nbDeDistributions(self):
826 listeVariables=self.jdc.getDistributions(self.etape)
827 self.monType.nbLigs=len(listeVariables)
828 self.monType.nbCols=len(listeVariables)
830 def getNomDsXML(self):
831 nomDsXML=self.parent.getNomDsXML()+"."+self.nom
835 def verifTypeIhm(self,val,cr='non'):
840 traceback.print_exc()
842 return self.verifType(val,cr)
844 def verifTypeliste(self,val,cr='non') :
847 verif=verif+self.verifTypeIhm(v,cr)
850 def initModifUp(self):
851 Validation.V_MCSIMP.MCSIMP.initModifUp(self)
852 CONNECTOR.Emit(self,"valid")
855 if self.valeur == None or self.valeur == [] : return
856 if not type(self.valeur) in (list, tuple): lesValeurs=(self.valeur,)
857 else : lesValeurs=self.valeur
858 for val in lesValeurs:
859 if self.definition.creeDesObjets :
860 val.deleteReference(self)
862 if (hasattr (val, 'enleveUtilisePar')) : val.enleveUtilisePar(self)
864 def updateAutresMotsClefs(self):
865 print ('updateAutresMotsClefs')
866 for (nomMC, Xpath) in self.definition.metAJour :
867 exp=Xpath+'.getChild("'+nomMC+'")'
869 lesMotsClefs = eval(exp)
872 if not type(lesMotsClefs) in (list, tuple): lesMotsClefs=(lesMotsClefs,)
873 if isinstance (lesMotsClefs, MCSIMP): lesMotsClefs=(lesMotsClefs,)
874 listeEtapesDejaRedessinees=[]
875 listeMotsClefsAppel=[]
876 for leMotCle in lesMotsClefs:
877 leMotCle.state='changed'
878 if not leMotCle.isValid() : leMotCle.val=None
879 if leMotCle.etape not in listeEtapesDejaRedessinees :
880 listeEtapesDejaRedessinees.append(leMotCle.etape)
881 listeMotsClefsAppel.append(leMotCle)
882 for leMotCle in listeMotsClefsAppel:
883 leMotCle.demandeRedessine()
885 print ('fin updateAutresMotsClefs')