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