Salome HOME
PN : correction de bug ???
[tools/eficas.git] / Editeur / widgets.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 # ----------------------------------------------------------
22 #   Cette classe sert à définir les widgets utilisés par
23 #          EFICAS
24 # ----------------------------------------------------------
25
26 import Tkinter
27 from Tkinter import *
28 import Pmw
29 import os,sys,re,string
30 import types,fnmatch
31 from tkFileDialog import *
32 from tkMessageBox import showinfo,askyesno,showerror,askretrycancel
33
34 import fontes
35 import prefs
36 from utils import save_in_file
37 from centerwindow import centerwindow
38
39 from Noyau.N_utils import repr_float
40 from Accas import AsException
41
42 # Surcharge de la fonction askyesno qui retourne un resultat errone en Python 2.3 avec Tk 8.4
43 # et Tkinter.wantobject==1
44 import tkMessageBox
45 def askyesno(title=None, message=None, **options):
46     "Ask a question; return true if the answer is yes"
47     s = tkMessageBox._show(title, message, tkMessageBox.QUESTION, tkMessageBox.YESNO, **options)
48     if s == tkMessageBox.YES:return 1
49     if s == tkMessageBox.NO:return 0
50     if s:return 1
51     return 0
52
53     
54 class Fenetre :
55     """ Cette classe permet de créer une fenêtre Toplevel dans laquelle
56         on peut afficher un texte et qui permet de le sauver"""
57     def __init__(self,appli,titre="",texte=""):
58         self.appli=appli
59         self.fenetre = Toplevel()
60         self.fenetre.configure(width = 800,height=500)
61         self.fenetre.protocol("WM_DELETE_WINDOW", self.quit)
62         self.fenetre.title("Visualisation du "+titre)
63         self.texte = string.replace(texte,'\r\n','\n')
64         self.titre = titre
65         fonte=fontes.standardcourier10
66         # définition des frames
67         self.frame_texte = Frame(self.fenetre)
68         self.frame_boutons = Frame(self.fenetre)
69         self.frame_texte.place(relx=0,rely=0,relwidth=1,relheight=0.9)
70         self.frame_boutons.place(relheight=0.1,relx=0,rely=0.9,relwidth=1.)
71         # définition de la zone texte et du scrollbar
72         self.zone_texte = Text(self.frame_texte,font=fonte)
73         self.zone_texte.bind("<Key-Prior>", self.page_up)
74         self.zone_texte.bind("<Key-Next>", self.page_down)
75         self.zone_texte.bind("<Key-Up>", self.unit_up)
76         self.zone_texte.bind("<Key-Down>", self.unit_down)
77         self.scroll_v = Scrollbar (self.frame_texte,command = self.zone_texte.yview)
78         #self.scroll_h = Scrollbar (self.frame_texte,command = self.zone_texte.xview)
79         self.scroll_v.pack(side='right',fill ='y')
80         #self.scroll_h.pack(side='bottom',fill ='x')
81         self.zone_texte.pack(side='top',fill='both',expand=1,padx=5,pady=10)
82         self.zone_texte.configure(yscrollcommand=self.scroll_v.set)
83         # définition des boutons
84         self.but_quit = Button(self.frame_boutons,text = "Fermer",command=self.quit)
85         self.but_save = Button(self.frame_boutons,text = "sauver",command = self.save)
86         self.but_quit.place(relx=0.4,rely=0.5,anchor='center')
87         self.but_save.place(relx=0.6,rely=0.5,anchor='center')
88         # affichage du texte
89         self.affiche_texte(self.texte)
90         centerwindow(self.fenetre)
91
92     def page_up(self,event):
93         event.widget.yview_scroll(-1, "page")
94     def page_down(self,event):
95         event.widget.yview_scroll(1, "page")
96     def unit_up(self,event):
97         event.widget.yview_scroll(-1, "unit")
98     def unit_down(self,event):
99         event.widget.yview_scroll(1, "unit")
100
101     def wait(self):
102         self.fenetre.grab_set()
103         self.zone_texte.focus_set()
104         self.fenetre.wait_window(self.fenetre)
105
106     def quit(self):
107         self.fenetre.destroy()
108
109     def efface_scroll(self):
110         """ Efface le scroll lorsqu'il n'est pas nécessaire : ne marche pas"""
111         self.scroll_v.pack_forget()
112         #self.scroll_h.pack_forget()
113
114     def affiche_texte(self,texte):
115         """ Affiche le texte dans la fenêtre """
116         if texte != "" :
117             self.zone_texte.insert(END,texte)
118             try:
119                 self.fenetre.update_idletasks()
120                 x0,y0,x1,y1 = self.zone_texte.bbox(END)
121                 if (y1-y0) < 300 : self.efface_scroll()
122             except:
123                 pass
124
125     def save(self):
126         """ Permet de sauvegarder le texte dans un fichier dont on a demandé le nom
127         à l'utilisateur """
128         file = asksaveasfilename(defaultextension = '.comm',
129                                #initialdir = self.appli.CONFIGURATION.rep_user,
130                                initialdir = self.appli.CONFIGURATION.initialdir,
131                                title="Sauvegarde du "+self.titre)
132         if file :
133             if not save_in_file(file,self.texte) :
134                 showerror("Sauvegarde impossible",
135                        "Impossible de sauvegarder le texte dans le fichier spécifié\n"+
136                           "Vérifiez les droits d'écriture")
137             else:
138                 showinfo("Sauvegarde effectuée","Sauvegarde effectuée dans le fichier %s" %file)
139
140     def destroy(self):
141         try :
142            self.fenetre.destroy()
143         except :
144            pass
145
146 class FenetreYesNo(Fenetre):
147     def __init__(self,appli,titre="",texte="",yes="Yes",no="No"):
148         self.appli=appli
149         self.fenetre = Toplevel()
150         self.fenetre.configure(width = 800,height=500)
151         self.fenetre.protocol("WM_DELETE_WINDOW", self.quit)
152         self.fenetre.title(titre)
153         self.texte = string.replace(texte,'\r\n','\n')
154         self.titre = titre
155         fonte=fontes.standardcourier10
156         # définition des frames
157         self.frame_texte = Frame(self.fenetre)
158         self.frame_boutons = Frame(self.fenetre)
159         self.frame_boutons.place(relx=0,rely=0,    relwidth=1.,relheight=0.1)
160         self.frame_texte.place(  relx=0,rely=0.1,  relwidth=1, relheight=0.9)
161         # définition de la zone texte et du scrollbar
162         self.zone_texte = Text(self.frame_texte,font=fonte)
163         self.zone_texte.bind("<Key-Prior>", self.page_up)
164         self.zone_texte.bind("<Key-Next>", self.page_down)
165         self.zone_texte.bind("<Key-Up>", self.unit_up)
166         self.zone_texte.bind("<Key-Down>", self.unit_down)
167         self.scroll_v = Scrollbar (self.frame_texte,command = self.zone_texte.yview)
168         #self.scroll_h = Scrollbar (self.frame_texte,command = self.zone_texte.xview)
169         self.scroll_v.pack(side='right',fill ='y')
170         #self.scroll_h.pack(side='bottom',fill ='x')
171         self.zone_texte.pack(side='top',fill='both',expand=1,padx=5,pady=10)
172         self.zone_texte.configure(yscrollcommand=self.scroll_v.set)
173         # définition des boutons
174         self.but_yes = Button(self.frame_boutons,text = yes,command=self.yes)
175         self.but_no = Button(self.frame_boutons,text = no,command = self.no)
176         self.but_yes.place(relx=0.4,rely=0.5,anchor='center')
177         self.but_no.place(relx=0.6,rely=0.5,anchor='center')
178         # affichage du texte
179         self.affiche_texte(self.texte)
180         centerwindow(self.fenetre)
181
182     def yes(self):
183         self.result=1
184         self.quit()
185
186     def no(self):
187         self.result=0
188         self.quit()
189
190 class FenetreDeSelection(Fenetre):
191     """ Classe dérivée de Fenêtre permettant la récupération d'une zone de texte sélectionnée.
192         Cette classe est utilisée pour affecter une liste de valeurs à un mot-clé.
193     """
194     def __init__(self,panel,item,appli,titre="",texte="",cardinal=1):
195         Fenetre.__init__(self,appli,titre=titre,texte=texte)
196         self.cardinal=cardinal
197         self.fenetre.configure(width = 320,height=400)
198         centerwindow(self.fenetre)
199         self.panel = panel
200         self.item = item
201         self.fenetre.title(titre)
202         self.but_save.configure(text="Ajouter",command=self.traite_selection)
203         # séparateur par défaut
204         self.separateur = ";"
205         # création de la zone de saisie du séparateur
206         l_separateurs_autorises = self.get_separateurs_autorises()
207         self.choix_sep = Pmw.ComboBox(self.frame_boutons,
208                                       label_text = "Séparateur :",
209                                       labelpos = 'w',
210                                       listheight = 100,
211                                       selectioncommand = self.choose_separateur,
212                                       scrolledlist_items = l_separateurs_autorises)
213         self.choix_sep.component('entry').configure(width=6)
214         self.choix_sep.place(relx=0.01,rely=0.5,anchor='w')
215         self.choix_sep.selectitem(self.separateur)
216         # Replacement
217         self.but_quit.place_forget()
218         self.but_save.place_forget()
219         self.but_save.place(relx=0.6,rely=0.5,anchor='center')
220         self.but_quit.place(relx=0.8,rely=0.5,anchor='center')
221      
222
223     def get_separateurs_autorises(self):
224         """
225         Retourne la liste des séparateurs autorisés
226         """
227         return ['espace',';',',']
228
229     def choose_separateur(self,nom_sep):
230         """
231         Affecte à self.separateur le caractère séparateur correspondant à nom_sep
232         """
233         if nom_sep == 'espace' :
234             self.separateur = ' '
235         else:
236             self.separateur = nom_sep
237         
238     def traite_selection(self):
239         """ Cette méthode effectue tous les traitements nécessaires pour vérifier
240             et affecter la liste de valeurs à l'objet réprésenté par self.item
241         """
242         # Récupère la liste des chaines de caractères de la zone sélectionnée
243         message,liste = self.recupere_liste()
244         if self.test_probleme(message,"Sélectionnez des données") == 0:
245             return
246         # Vérifie que le nombre de données est dans les limites attendues
247         message = self.verif_liste(liste)
248         if self.test_probleme(message,"Vérifiez le nombre de données") == 0:
249             return
250         # Crée une liste de valeurs du type attendu
251         message,liste_valeurs = self.creation_liste_valeurs(liste)
252         if self.test_probleme(message,"Vérifiez le type des données") == 0:
253             return
254         # Vérifie que chaque valeur est dans le domaine exigé
255         message = self.verif_valeurs(liste_valeurs)
256         if self.test_probleme(message,"Vérifiez le domaine des valeurs") == 0:
257             return
258         # Ajoute les valeurs dans la liste de valeurs du mot-clé
259         if self.cardinal != 1 :
260            nb=self.cardinal
261            l_valeurs=[]
262            # a ameliorer
263            if (len(liste_valeurs)%nb != 0):
264                 message="La cardinalité n'est pas correcte"
265                 self.test_probleme(message,"On attend des tuples")
266                 return
267            for i in range(len(liste_valeurs)/nb) :
268                if (nb==2):
269                    t=(liste_valeurs[i*nb], liste_valeurs[i*nb+1])
270                elif (nb ==3):
271                    t=(liste_valeurs[i*nb], liste_valeurs[i*nb+1], liste_valeurs[i*nb+2])
272                else :
273                   print "probleme : prevenir la maintenance Eficas"
274                   return
275                l_valeurs.append(t)
276            liste_valeurs=l_valeurs
277         self.ajouter_valeurs(liste_valeurs)
278         self.appli.affiche_infos("Liste de valeurs acceptée")
279
280     def test_probleme(self, message, message_eficas):
281         """ Cette méthode affiche un message d'erreur si message != ''
282             et retourne 0, sinon retourne 1 sans rien afficher.
283         """
284         if message != "":
285             showinfo("Problème",message)
286             self.fenetre.tkraise()
287             self.appli.affiche_infos(message_eficas)
288             return 0
289         else:
290             return 1
291
292     def recupere_liste(self):
293         """ Cette méthode récupère le texte de la zone sélectionnée, construit et
294             retourne une liste avec les chaines qui se trouvent entre les séparateurs.
295             S'il n'y a pas de données selectionnées, elle retourne un message d'erreur
296             et une liste vide.
297         """
298         message = ""
299         try:
300             selection=self.fenetre.selection_get()
301         except:
302             message = "Pas de donnée sélectionnée"
303             return message,None
304         # les retours chariots doivent être interprétés comme des séparateurs
305         selection = string.replace(selection,'\n',self.separateur)
306         # on splitte la sélection suivant le caractère séparateur
307         liste_chaines = string.split(selection,self.separateur)
308         l_chaines = []
309         for chaine in liste_chaines:
310             chaine = string.strip(chaine)
311             if chaine != '' : l_chaines.append(chaine)
312         return message,l_chaines
313
314     def verif_liste(self, liste):
315         """ Cette méthode effectue des tests sur le nombre d'éléments de la liste
316             et retourne 1 si la liste est correcte, sinon 0 et le message d'erreur
317             correspondant.
318         """
319         message = ""
320         # nombre d'éléments sélectionnés
321         nombre_elements = len(liste)
322         # nombre d'éléments déja dans la liste du panel
323         nombre_in_liste = len(self.panel.Liste_valeurs.get_liste())
324         multiplicite = self.item.GetMultiplicite()
325         if (nombre_elements % multiplicite) != 0:
326             message = "Vous devez sélectionner "+str(multiplicite)+" * n données"
327             return message
328         nombre_valeurs = nombre_elements / multiplicite
329         cardinalite = self.item.GetMinMax()
330         if nombre_valeurs < cardinalite[0]:
331             message = "Vous devez sélectionner au moins "+str(cardinalite[0])+" valeurs"
332             return message
333         if cardinalite[1] != "**" and nombre_valeurs > (long(cardinalite[1])-nombre_in_liste):
334             message = "La liste ne peut avoir plus de "+str(cardinalite[1])+" valeurs"
335             return message
336
337         return message
338
339     def creation_liste_valeurs(self, liste):
340         """ Cette méthode crée et retourne une liste de valeurs du type attendu
341             par le mot-clé. La liste de valeurs est créée à partir de la liste
342             de chaines de caractères transmise.
343         """
344         type_attendu = self.item.GetType()[0]
345         if type_attendu == 'R':
346             return self.convertir(liste, f_conversion= float)
347         elif type_attendu == 'I':
348             return self.convertir(liste, f_conversion= int)
349         elif type_attendu == 'TXM':
350             return self.convertir(liste)
351         else:
352             message = "Seuls les entiers, les réels et les chaines de caractères sont convertis"
353             return message,None
354
355     def convertir(self, liste, f_conversion=None):
356         """ Cette méthode essaie de convertir les éléments de la liste avec la
357             fonction f_conversion si elle existe, et retourne la liste des
358             éléments dans le type voulu en cas de succès, sinon retourne None.
359         """
360         liste_valeurs = []
361         message = ""
362         for chaine in liste:
363             if f_conversion:
364                 try:
365                     liste_valeurs.append(f_conversion(chaine))
366                 except:
367                     message = "Impossible de convertir "+chaine+" dans le type attendu"
368                     return message,None
369             else:
370                 liste_valeurs.append(chaine)
371         return message,liste_valeurs
372
373     def verif_valeurs(self, liste_valeurs):
374         """ Cette méthode teste tous les éléments de la liste, et retourne 1 si chaque
375             élément est dans le domaine voulu.
376         """
377         message = ""
378         for valeur in liste_valeurs:
379             test = self.item.IsInIntervalle(valeur)
380             if test == 0:
381                 intervalle = str(self.item.GetIntervalle()[0])+","+str(self.item.GetIntervalle()[1])
382                 message = "La valeur "+str(valeur)+" n'est pas dans l'intervalle ["+intervalle+"]"
383                 return message
384         return message
385
386     def ajouter_valeurs(self, liste_valeurs):
387         """ Cette méthode ajoute les nouvelles valeurs à la liste existante."""
388         liste = self.panel.Liste_valeurs.get_liste()
389         liste.extend(liste_valeurs)
390         self.panel.Liste_valeurs.put_liste(liste)
391
392 class FenetreDeParametre(Fenetre) :
393     def __init__(self,parent,item,appli,texte):
394         self.parent=parent
395         self.appli=appli
396         self.fenetre = Toplevel()
397         self.fenetre.configure(width = 250,height=100)
398         self.fenetre.protocol("WM_DELETE_WINDOW", self.quit)
399         self.fenetre.title("Parametres")
400         self.titre = "Parametres"
401         self.texte = string.replace(texte,'\r\n','\n')
402         fonte=fontes.standardcourier10
403
404         # définition des frames
405         self.frame_texte = Frame(self.fenetre)
406         self.frame_texte.place(relx=0,rely=0,relwidth=1,relheight=0.9)
407         # définition de la zone texte et du scrollbar
408         self.zone_texte = Text(self.frame_texte,font=fonte)
409         self.zone_texte.bind("<Key-Prior>", self.page_up)
410         self.zone_texte.bind("<Key-Next>", self.page_down)
411         self.zone_texte.bind("<Key-Up>", self.unit_up)
412         self.zone_texte.bind("<Key-Down>", self.unit_down)
413         self.zone_texte.bind("<Double-Button-3>", self.OnButton3doubleclick)
414         self.scroll_v = Scrollbar (self.frame_texte,command = self.zone_texte.yview)
415         self.scroll_v.pack(side='right',fill ='y')
416         self.zone_texte.pack(side='top',fill='both',expand=1,padx=5,pady=10)
417         self.zone_texte.configure(yscrollcommand=self.scroll_v.set)
418         # affichage du texte
419         self.affiche_texte(self.texte)
420         self.zone_texte.config(state="disabled")
421
422     def OnButton3doubleclick(self,event):
423         try:
424             selection=self.zone_texte.selection_get()
425         except:
426             showerror("Pas de donnée sélectionnée",
427                        "Selectionner un parametre")
428         l_param = ""
429         for param in selection.splitlines():
430             nomparam=param[0:param.find("=")-1]
431             if nomparam != '' : 
432                 l_param=l_param+nomparam+','
433         self.parent.entry.delete(0,Tkinter.END)
434         self.parent.entry.insert(0,l_param[0:-1])
435         self.parent.valid_valeur()
436         self.quit()
437
438 class Formulaire:
439     """
440     Cette classe permet de créer une boîte Dialog dans laquelle
441     on affiche un formulaire à remplir par l'utilisateur
442     """
443     def __init__(self,fen_pere,obj_pere=None,titre="",texte="",items=(),mode='query',commande=None):
444         if items in ((),[]) : return
445         self.items = items
446         self.titre = titre
447         self.texte = texte
448         self.fen_pere = fen_pere
449         self.obj_pere = obj_pere
450         self.mode= mode
451         self.command = commande
452         self.display()
453
454     def display(self):
455         self.init_validateurs()
456         self.init_fenetre()
457         self.init_texte()
458         self.init_items_formulaire()
459         self.fenetre.activate(geometry='centerscreenalways')
460
461     def init_validateurs(self):
462         """
463         Crée le dictionnaire des validateurs des objets reconnus par le formulaire
464         """
465         self.d_validateurs = {}
466         self.d_validateurs['rep']  = self.repvalidator
467         self.d_validateurs['file'] = self.filevalidator
468         self.d_validateurs['cata']= self.catavalidator
469         
470     def init_fenetre(self):
471         """
472         Crée la fenêtre Dialog
473         """
474         if self.mode == 'query':
475             buttons=('Valider','Annuler')
476             defaultbutton = 'Valider'
477         elif self.mode == 'display':
478             if self.command :
479                 buttons=(self.command[0],'OK')
480                 defaultbutton = 'OK'
481             else:
482                 buttons=('OK')
483                 defaultbutton = 'OK'
484         self.fenetre = Pmw.Dialog(self.fen_pere,
485                                   buttons=buttons,
486                                   defaultbutton = defaultbutton,
487                                   title = self.titre,
488                                   command = self.execute)
489         self.fenetre.withdraw()
490         
491     def init_texte(self):
492         """
493         Crée le label qui affiche le texte à l'intérieur du panneau
494         """
495         fonte=fontes.standard
496         fr_texte = Frame(self.fenetre.interior(),height=60)
497         fr_texte.pack(side='top',fill='x',expand=1)
498         Label(fr_texte,text = self.texte, font=fonte).place(relx=0.5,rely=0.5,anchor='center')
499
500     def init_items_formulaire(self):
501         """
502         Crée et affiche les items dans la boîte de dialogue
503         """
504         self.radiobut = 0
505         self.widgets = []
506         self.item_widgets = {}
507         length_maxi = 0
508         for item in self.items:
509             if len(item[0])>length_maxi : length_maxi = len(item[0])
510         window = self.fenetre.interior()
511         for item in self.items :
512             label,nature,nom_var,defaut = item
513             # création de la frame
514             fr_item = Frame(window,height=40,width=700)
515             fr_item.pack(side='top',fill='x',expand=1)
516             # création du label
517             Label(fr_item,text = label).place(relx=0.05,rely=0.4)
518             if nature in ('rep','file','cata'):
519                 # création de l'entry
520                 e_item = Entry(fr_item) 
521                 e_item.place(relx=0.5,rely=0.4,relwidth=0.45)
522                 self.widgets.append(e_item)
523                 self.item_widgets[item] = e_item
524                 if defaut : e_item.insert(0,str(defaut))
525             elif nature == 'YesNo':
526                 # création de la StringVar
527                 var = StringVar()
528                 setattr(self,'item_'+nom_var,var)
529                 var.set(defaut)
530                 # création du radiobouton
531                 rb1 = Radiobutton(fr_item,text='OUI',variable=var,value='OUI')
532                 rb2 = Radiobutton(fr_item,text='NON',variable=var,value='NON')
533                 rb1.place(relx=0.65,rely=0.5,anchor='center')
534                 rb2.place(relx=0.80,rely=0.5,anchor='center')
535                 self.widgets.append((rb1,rb2))
536                 self.item_widgets[item] = var
537         # détermination de la méthode à appliquer sur les boutons
538         if self.mode == 'query':
539             function = self.active
540         elif self.mode == 'display':
541             function = self.inactive
542         else:
543             return
544         # on applique la méthode sur les boutons (activation ou désactivation)  
545         for widget in self.widgets :
546             if type(widget) == types.TupleType:
547                 for widg in widget :
548                     apply(function,(widg,),{})
549             else:
550                 apply(function,(widget,),{})
551
552     def active(self,widget):
553         """
554         Active le widget passé en argument
555         """
556         widget.configure(state='normal',bg='white')
557
558     def inactive(self,widget):
559         """
560         Inactive le widget passé en argument
561         """
562         if not isinstance(widget,Radiobutton) :
563             widget.configure(state='disabled',bg='gray95')
564         else :
565             widget.configure(state='disabled')
566
567 # --------------------------------------------------------------------------------
568 #       Validateurs des noms de répertoire, de fichiers et de catalogues
569 # -------------------------------------------------------------------------------
570
571     def repvalidator(self,text):
572         """
573         Teste si text peut faire référence à un répertoire ou non
574         Retourne 1 si valide, 0 sinon
575         """
576         return os.path.isdir(text),'Répertoire introuvable : %s' %text
577
578     def filevalidator(self,text):
579         """
580         Teste si text peut faire référence à un fichier ou non
581         Retourne 1 si valide, 0 sinon
582         """
583         return os.path.isfile(text),'Fichier introuvable : %s' %text
584
585     def catavalidator(self,text):
586         """
587         Teste si  text est un chemin d'accès valide à un catalogue
588         Retourne 1 si valide, 0 sinon
589         """
590         return os.path.isfile(text),"Catalogue introuvable : %s" %text
591
592 # --------------------------------------------------------------------------------
593 #       Méthodes callbacks des boutons et de fin
594 # --------------------------------------------------------------------------------
595         
596     def execute(self,txt):
597         """
598         Cette commande est activée à chaque clic sur un bouton.
599         Redirige l'action sur la bonne méthode en fonction du bouton activé
600         """
601         if txt == 'Valider':
602             self.fini()
603         elif txt in ('OK','Annuler'):
604             self.quit()
605         elif txt == 'Modifier':
606             self.resultat = apply(self.command[1],(),{})
607             self.fenetre.destroy()
608         else :
609             print "Nom de bouton inconnu"
610             self.quit()
611
612     def fini(self):
613         """
614         Commande qui termine le panneau et sauvegarde les nouvelles options
615         dans l'objet resultat (dictionnaire)
616         """
617         dico={}
618         for item,widget in self.item_widgets.items():
619             nom_var = item[2]
620             type_var = item[1]
621             valeur = widget.get()
622             if self.d_validateurs.has_key(type_var):
623                 test = self.d_validateurs[type_var](valeur)
624                 if not test :
625                     # une entrée n'est pas valide --> on la met en surbrillance et on quitte la méthode
626                     # sans tuer la fenêtre bien sûr
627                     widget.selection_range(0,END)
628                     return
629             dico[nom_var] = valeur
630         self.fenetre.destroy()    
631         self.resultat=dico
632         
633     def quit(self):
634         self.fenetre.destroy()
635         self.resultat=None
636         
637 class ListeChoix :
638     """ Cette classe est utilisée pour afficher une liste de choix passée en paramètre
639         en passant les commandes à lancer suivant différents bindings """
640     def __init__(self,parent,page,liste,liste_commandes=[],liste_marques =[],active ='oui',filtre='non',titre=''):
641         self.parent = parent
642         self.page = page
643         self.liste = liste
644         self.dico_labels={}
645         self.selection = None
646         self.liste_commandes = liste_commandes
647         self.liste_marques = liste_marques
648         self.arg_selected=''
649         self.active = active
650         self.titre = titre
651         self.filtre = filtre
652         self.init()
653
654     def init(self):        
655         self.make_label_titre()
656         self.make_entry_filtre()
657         self.make_text_box()
658         try:
659             self.entry.component('entry').focus()
660         except:
661             pass
662
663     def make_label_titre(self):
664         """ Crée le label correspondant au titre """
665         if self.titre == '' : return
666         fonte_titre = fontes.standard_gras_souligne
667         self.label = Label(self.page,
668                            text = self.titre,
669                            font = fonte_titre)
670         self.label.pack(side='top',pady=2)
671         
672     def make_entry_filtre(self):
673         """ Crée l'entry permettant à l'utilisateur d'entrer un filtre de sélection dans la liste """
674         if self.filtre != 'oui' : return
675         self.entry = Pmw.EntryField(self.page,labelpos='w',
676                                     label_text="Filtre :",
677                                     command=self.entry_changed)
678         self.entry.pack(side='top',pady=2)
679         
680     def make_text_box(self):
681         """ Crée la fenêtre texte dans laquelle sera affichée la liste """
682         self.MCbox = Text (self.page,relief='sunken',bg='gray95',bd=2)
683         self.MCscroll = Scrollbar (self.page,command = self.MCbox.yview)
684         self.MCscroll.pack(side='right',fill ='y',pady=2)
685         self.MCbox.pack(fill='y',expand=1,padx=2,pady=2)
686         self.MCbox.configure(yscrollcommand=self.MCscroll.set)
687
688     def affiche_liste(self):
689         """ Affiche la liste dans la fenêtre"""
690         liste_labels=[]
691         self.MCbox.config(state=NORMAL)
692         self.MCbox.delete(1.0,END)
693         for objet in self.liste :
694           if type(objet) == types.InstanceType:
695               try:
696                   mot = objet.nom
697               except:
698                   mot = str(objet)
699           elif type(objet) in (types.StringType,types.IntType):
700               mot = objet
701           elif type(objet) == types.FloatType :
702               #mot = repr_float(objet)
703               mot = str(objet)
704           elif type(objet) == types.TupleType :
705               mot="("
706               premier=1
707               for val in objet:
708                   if (not premier):
709                      mot=mot+"," 
710                   else:
711                      premier=0
712                   mot=mot+str(val)
713               mot=mot+")"
714           else:
715               mot=`objet`
716           label = Label(self.MCbox,
717                         text = mot,
718                         fg = 'black',bg = 'gray95',justify = 'left')
719           self.dico_labels[mot]=label
720           liste_labels.append(label)
721           self.MCbox.window_create(END,
722                                    window=label,
723                                    stretch = 1)
724           self.MCbox.insert(END,'\n')
725           if self.active == 'oui':
726               label.bind(self.liste_commandes[0][0],lambda e,s=self,c=self.liste_commandes[0][1],x=objet,l=label : s.selectitem(x,l,c))
727               label.bind(self.liste_commandes[1][0],lambda e,s=self,c=self.liste_commandes[1][1],x=objet,l=label : s.deselectitem(l,x,c))
728               label.bind(self.liste_commandes[2][0],lambda e,s=self,c=self.liste_commandes[2][1],x=objet,l=label : s.chooseitem(x,l,c))
729
730         for marque in self.liste_marques:
731            try:
732               self.markitem(liste_labels[marque])
733            except:
734               pass
735
736         self.MCbox.config(state=DISABLED)
737         self.selection = None
738
739     def chooseitem(self,mot,label,commande):
740         """ Active la méthode de choix passée en argument"""
741         try:
742            commande(mot)
743         except AsException,e:
744            raison=str(e)
745            showerror(raison.split('\n')[0],raison)
746         
747     def selectitem(self,mot,label,commande) :
748         """ Met l'item sélectionné (représenté par son label) en surbrillance
749             et lance la commande associée au double-clic"""
750         if self.selection != None :
751             self.deselectitem(self.selection[1],self.selection[0],self.selection[2],)
752         self.highlightitem(label)
753         self.selection = (mot,label,commande)
754         self.arg_selected = mot
755         commande(mot)
756
757     def highlightitem(self,label) :
758         """ Met l'item représenté par son label en surbrillance """
759         label.configure(bg='#00008b',fg='white')
760         
761     def markitem(self,label):
762         """ Met l'item (représenté par son label) en rouge """
763         label.configure(bg='gray95',fg='red')
764         
765     def deselectitem(self,label,mot='',commande=None) :
766         """ Remet l'item (représenté par son label) en noir"""
767         label.configure(bg='gray95',fg='black')
768         self.arg_selected = ''
769         if commande != None : commande(mot)
770
771     def cherche_selected_item(self):
772         index=self.MCbox.index(self.selection[1])
773         lign,col=map(int,string.split(index,'.'))
774         return lign
775
776     def remove_selected_item(self):
777         index=self.MCbox.index(self.selection[1])
778         lign,col=map(int,string.split(index,'.'))
779         del self.liste[lign-1]
780         self.affiche_liste()
781
782     def entry_changed(self,event=None):
783         """ Cette méthode est invoquée chaque fois que l'utilisateur modifie le contenu
784         de l'entry et frappe <Return>"""
785         if self.arg_selected != '' : self.deselectitem(self.dico_labels[self.arg_selected])
786         filtre = self.entry.get()+"*"
787         FILTRE = string.upper(filtre)
788         for arg in self.liste :
789             if fnmatch.fnmatch(arg,filtre) or fnmatch.fnmatch(arg,FILTRE) :
790                 self.highlightitem(self.dico_labels[arg])
791                 index = self.MCbox.index(self.dico_labels[arg])
792                 self.MCbox.see(index)
793                 self.arg_selected = arg
794                 break
795
796     def get_liste_old(self):
797         return self.liste
798
799     # PN attention à la gestion des paramétres
800     # cela retourne H = 1 , et ni H, ni 1
801     #            print repr(val)
802     #            print val.__class__.__name__
803     def get_liste(self):
804         l=[]
805         for val in self.liste:
806 #            try:
807 #                v = eval(val)
808 #               l.append(v)
809 #            except:
810                 l.append(val)
811         return l
812     
813     def put_liste(self,liste):
814         self.liste = liste
815         self.affiche_liste()
816         
817 class Affichage :
818   """ Cette classe permet d'afficher au lancement d'EFICAS le message
819       d'attente et la barre de progression"""
820   def __init__(self,master,message,barre ='oui'):
821       from Tools.foztools.foztools import Slider
822       fonte=fontes.standard12_gras
823       self.master=master
824       self.frame = Frame(self.master)
825       self.frame.pack(expand=1,fill='both')
826       self.mess = Label(self.frame,text=message,justify='center',
827                         bd=2,relief='groove',font=fonte)
828       self.mess.pack(in_ = self.frame,side='top',expand=1,fill='both')
829       self.progress = Slider(self.frame,value=0,max=100,orientation='horizontal',
830                              fillColor='#00008b',width=200,height=30,
831                              background='white',labelColor='red')
832       if barre == 'oui':
833           self.progress.frame.pack(in_=self.frame,side='top')
834       self.master.update()
835       if barre == 'oui':
836           self.progress.frame.after(1000,self.update)
837
838   def configure(self,**options):
839       if options.has_key('message'):
840           self.mess.configure(text=options['message'])
841       if options.has_key('barre'):
842           if options['barre'] == 'oui' :
843               self.progress.frame.pack(in_=self.frame,side='top')
844           elif options['barre'] == 'non' :
845               self.progress.frame.pack_forget()
846       self.master.update_idletasks()
847       
848   def quit(self):
849       self.frame.destroy()
850       self.master.update()
851
852   def update(self,event=None):
853       """ Permet de faire avancer la barre de progression """
854       try :
855           bar=self.progress
856           bar.value = bar.value+self.increment
857           bar.update()
858           self.master.after(100,self.update)
859       except:
860           pass
861
862   def configure_barre(self,nb):
863       """ Calcule l'incrément de progression de la barre en fonction
864           du nombre d'opérations à effectuer afin que le compteur
865           soit à 100% à la fin des opérations"""
866       self.increment = 100./nb
867       self.progress.update()
868
869 class Ask_Format_Fichier :
870     """
871     Cette classe permet de créer une fenêtre Toplevel dans laquelle
872     on propose le choix du format de fichier de commandes à ouvrir
873     """
874     def __init__(self,appli):
875         self.fenetre = Toplevel()
876         self.fenetre.configure(width = 250,height=150)
877         self.fenetre.protocol("WM_DELETE_WINDOW", self.quit)
878         self.fenetre.title("Choix du format du fichier de commandes")
879         # définition des frames
880         self.frame_texte = Frame(self.fenetre)
881         self.frame_radioboutons = Frame(self.fenetre)
882         self.frame_bouton_ok = Frame(self.fenetre)
883         self.frame_texte.place(relx=0,rely=0,relwidth=1,relheight=0.3)
884         self.frame_radioboutons.place(relheight=0.5,relx=0,rely=0.3,relwidth=1.)
885         self.frame_bouton_ok.place(relheight=0.2,relx=0,rely=0.8,relwidth=1.)
886         # définition de la zone texte et du scrollbar
887         zone_texte = Label(self.frame_texte,text = "Format du fichier à ouvrir :")
888         zone_texte.pack(side='top',fill='both',expand=1,padx=5,pady=10)
889         # définition des radioboutons
890         Radiobutton(self.frame_radioboutons,text='Format Aster (Code_Aster --> v5)',
891                     variable=appli.format_fichier,value='Aster').pack(anchor='n')
892         Radiobutton(self.frame_radioboutons,text='Format Python (Code_Aster v6-->)',
893                     variable=appli.format_fichier,value='Python').pack(anchor='n')
894         # création du bouton OK
895         Button(self.frame_bouton_ok,text='OK',command=self.quit).pack(anchor='n')
896         # centrage de la fenêtre
897         centerwindow(self.fenetre)
898
899     def quit(self):
900         self.fenetre.destroy()
901
902 class BARRE_K2000(Toplevel):
903     def __init__(self,master=None,text = ""):
904         Toplevel.__init__(self,master,relief='groove')
905         self.master.iconify()
906         self.geometry("250x100+0+0")
907         self.protocol("WM_DELETE_WINDOW",self.quit)
908         # frame principale dans self (= Toplevel)
909         self.frame = Frame(self)
910         self.frame.place(relwidth=1,relheight=1)
911         # frame contenant le texte à afficher 
912         self.frame_text = Frame(self.frame)
913         self.frame_text.place(relwidth=1,relheight=0.75,rely=0)
914         # frame contenant le canvas de la barre
915         self.frame_canv = Frame(self.frame)
916         self.frame_canv.place(relwidth=1,relheight=0.25,rely=0.75)
917         # canvas dans lequel sera affichée la barre K2000
918         self.canvas = Canvas(self.frame_canv)
919         self.canvas.place(relx=0.5,rely=0.5,relheight=0.8,relwidth=0.8,anchor='center')
920         # on affiche le texte et la barre
921         self.build_text(text)
922         self.build_batons()
923         #self.overrideredirect(1)
924         # on active la barre ...
925         self.master.after(1000,self.launch)
926         # on centre la fenêtre
927         centerwindow(self)
928         self.focus()
929
930     def build_text(self,text):
931         """
932         Affichage de text dans frame_text
933         """
934         self.texte_var = StringVar()
935         self.texte_var.set(text)
936         Label(self.frame_text,textvariable=self.texte_var).place(relx=0.5,rely=0.5,anchor='center')
937         
938     def build_batons(self):
939         """
940         Construit la suite de bâtons dans le canvas
941         """
942         self.l_batons=[]
943         self.black = -1
944         self.sens = 'D'
945         self.quit = 0
946         for i in range(0,40):
947             id = self.canvas.create_rectangle(i*5,0,(i+1)*5,20,fill='gray90',outline='')
948             self.l_batons.append(id)
949
950     def launch(self):
951         """
952         Active la barre K2000 en affichant les bâtons avec des couleurs en dégradé
953         """
954         if self.quit == 1 :
955             self.destroy()
956             self.master.deiconify()
957             return
958         if self.sens == 'D':
959             self.black = self.black+1
960             l_bat = self.l_batons[0:self.black+1]
961             l_bat.reverse()
962         elif self.sens == 'G':
963             self.black = self.black-1
964             l_bat = self.l_batons[self.black:]
965         i=0
966         for bat in l_bat :
967             num_color = 5+i*10
968             if num_color < 10 : color = 'black'
969             elif num_color > 90 : color = 'white'
970             else: color = 'gray'+`num_color`
971             self.canvas.itemconfigure(bat,fill=color)
972             i=i+1
973         if self.black == len(self.l_batons) :
974             self.sens = 'G'
975         if self.black == 0 and self.sens == 'G':self.sens = 'D'
976         self.after(80,self.launch)
977
978     def update_text(self,new_text):
979         """
980         Remplace le texte affiché par new_text
981         """
982         self.texte_var.set(new_text)
983         
984     def quit(self):
985         self.quit = 1        
986
987 class ListeChoixParGroupes(ListeChoix) :
988     """ 
989         Cette classe est utilisée pour afficher une liste de commandes classées par
990         groupes. L'utilisateur peut réaliser des actions de selection
991         qui déclenchent des actions spécifiées par les bindings contenus dans liste_commandes
992     """
993     def __init__(self,parent,page,liste_groupes,dict_groupes,liste_commandes=[],liste_marques =[],
994                       active ='oui',filtre='non',titre=''):
995         self.parent = parent
996         self.page = page
997         self.liste_groupes = liste_groupes
998         self.dict_groupes = dict_groupes
999         self.dico_labels={}
1000         self.selection = None
1001         self.liste_commandes = liste_commandes
1002         self.liste_marques = liste_marques
1003         self.arg_selected=''
1004         self.active = active
1005         self.titre = titre
1006         self.filtre = filtre
1007         self.init()
1008
1009     def affiche_liste(self):
1010         """ Affiche la liste dans la fenêtre"""
1011         liste_labels=[]
1012         self.MCbox.config(state=NORMAL)
1013         self.MCbox.delete(1.0,END)
1014         for grp in self.liste_groupes:
1015            # On itère sur les groupes
1016            if grp == "CACHE":continue
1017            liste_commandes=self.dict_groupes[grp]
1018            text="GROUPE<<<<<<<< "+grp+" "
1019            text=text+">"*max(0,30-len(text))
1020            label = Label(self.MCbox,
1021                         text = text,
1022                         fg = 'black',bg = 'gray95',justify = 'left')
1023            # On stocke la relation entre le nom de la commande et le label
1024            self.dico_labels[grp]=label
1025            liste_labels.append(label)
1026            self.MCbox.window_create(END,
1027                                    window=label,
1028                                    stretch = 1)
1029            self.MCbox.insert(END,'\n')
1030            for cmd in liste_commandes:
1031               label = Label(self.MCbox,
1032                         text = cmd,
1033                         fg = 'black',bg = 'gray95',justify = 'left')
1034               # On stocke la relation entre le nom de la commande et le label
1035               self.dico_labels[cmd]=label
1036               self.MCbox.window_create(END,
1037                                    window=label,
1038                                    stretch = 1)
1039               self.MCbox.insert(END,'\n')
1040               if self.active == 'oui':
1041                   label.bind(self.liste_commandes[0][0],
1042                          lambda e,s=self,c=self.liste_commandes[0][1],x=cmd,l=label : s.selectitem(x,l,c))
1043                   label.bind(self.liste_commandes[1][0],
1044                          lambda e,s=self,c=self.liste_commandes[1][1],x=cmd,l=label : s.deselectitem(l,x,c))
1045                   label.bind(self.liste_commandes[2][0],
1046                          lambda e,s=self,c=self.liste_commandes[2][1],x=cmd,l=label : s.chooseitem(x,l,c))
1047
1048         for marque in self.liste_marques:
1049            try:
1050               self.markitem(liste_labels[marque])
1051            except:
1052               pass
1053
1054         self.MCbox.config(state=DISABLED)
1055         self.selection = None
1056
1057     def entry_changed(self,event=None):
1058         """ 
1059             Cette méthode est invoquée chaque fois que l'utilisateur modifie le contenu
1060             de l'entry et frappe <Return>
1061         """
1062         if self.arg_selected != '' : self.deselectitem(self.dico_labels[self.arg_selected])
1063         filtre = self.entry.get()+"*"
1064         FILTRE = string.upper(filtre)
1065         #
1066         # On cherche d'abord dans les noms de groupe
1067         # puis dans les noms de commande groupe par groupe
1068         #
1069         for grp in self.liste_groupes:
1070             if fnmatch.fnmatch(grp,filtre) or fnmatch.fnmatch(grp,FILTRE) :
1071                 index = self.MCbox.index(self.dico_labels[grp])
1072                 self.MCbox.see(index)
1073                 # On ne selectionne pas le groupe
1074                 #self.arg_selected = grp
1075                 # On a trouve un groupe on arrete la recherche
1076                 return
1077
1078         for grp in self.liste_groupes:
1079            for cmd in self.dict_groupes[grp] :
1080               if fnmatch.fnmatch(cmd,filtre) or fnmatch.fnmatch(cmd,FILTRE) :
1081                  self.highlightitem(self.dico_labels[cmd])
1082                  index = self.MCbox.index(self.dico_labels[cmd])
1083                  self.MCbox.see(index)
1084                  self.arg_selected = cmd
1085                  # On a trouve une commande  on arrete la recherche
1086                  return
1087