Salome HOME
PN bug notation scientifique
[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='',optionReturn=None):
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.optionReturn = optionReturn
684         self.init()
685
686     def init(self):        
687         self.make_label_titre()
688         self.make_entry_filtre()
689         self.make_text_box()
690         try:
691             self.entry.component('entry').focus()
692         except:
693             pass
694
695     def make_label_titre(self):
696         """ Crée le label correspondant au titre """
697         if self.titre == '' : return
698         fonte_titre = fontes.standard_gras_souligne
699         self.label = Label(self.page,
700                            text = self.titre,
701                            font = fonte_titre)
702         self.label.pack(side='top',pady=2)
703         
704     def make_entry_filtre(self):
705         """ Crée l'entry permettant à l'utilisateur d'entrer un filtre de sélection dans la liste """
706         if self.filtre != 'oui' : return
707         self.entry = Pmw.EntryField(self.page,labelpos='w',
708                                     label_text="Filtre :",
709                                     command=self.entry_changed)
710         self.entry.pack(side='top',pady=2)
711         
712     def make_text_box(self):
713         """ Crée la fenêtre texte dans laquelle sera affichée la liste """
714         self.MCbox = Text (self.page,relief='sunken',bg='gray95',bd=2)
715         self.MCscroll = Scrollbar (self.page,command = self.MCbox.yview)
716         self.MCscroll.pack(side='right',fill ='y',pady=2)
717         self.MCbox.pack(fill='y',expand=1,padx=2,pady=2)
718         self.MCbox.configure(yscrollcommand=self.MCscroll.set)
719
720
721     def affiche_liste(self):
722         """ Affiche la liste dans la fenêtre"""
723         liste_labels=[]
724         self.MCbox.config(state=NORMAL)
725         self.MCbox.delete(1.0,END)
726         for objet in self.liste :
727           if type(objet) == types.InstanceType:
728               try:
729                   mot = objet.nom
730               except:
731                   mot = str(objet)
732           elif type(objet) in (types.StringType,types.IntType):
733               mot = objet
734           elif type(objet) == types.FloatType :
735               mot = self.parent.get_valeur_texte(objet)
736               if mot == "" :
737                  mot = str(objet)
738           elif type(objet) == types.TupleType :
739               mot="("
740               premier=1
741               for val in objet:
742                  if (not premier):
743                    mot=mot+"," 
744                  else:
745                    premier=0
746                  valtexte = self.parent.get_valeur_texte(val)
747                  if valtexte != "" :
748                     mot=mot+valtexte
749                  else:
750                     mot=mot+str(val)
751               mot=mot+")"
752           else:
753               mot=`objet`
754           label = Label(self.MCbox,
755                         text = mot,
756                         fg = 'black',bg = 'gray95',justify = 'left')
757           self.dico_labels[mot]=label
758           liste_labels.append(label)
759           self.MCbox.window_create(END,
760                                    window=label,
761                                    stretch = 1)
762           self.MCbox.insert(END,'\n')
763           if self.optionReturn != None :
764               label.bind("<Return>",lambda e,s=self,c=self.liste_commandes[2][1],x=objet,l=label : s.chooseitemsurligne(x,l,c))
765           label.bind("<Key-Right>",lambda e,s=self,x=objet,l=label : s.selectNextItem(x,l))
766           if self.active == 'oui':
767               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))
768               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))
769               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))
770
771         for marque in self.liste_marques:
772            try:
773               self.markitem(liste_labels[marque])
774            except:
775               pass
776
777         self.MCbox.config(state=DISABLED)
778         self.selection = None
779
780     def clear_marque(self):
781         try:
782           self.dico_labels[self.arg_selected].configure(bg='gray95',fg='black')
783           self.arg_selected = ''
784         except :
785           pass
786
787     def surligne(self,marque):
788         try :
789            self.highlightitem(self.dico_labels[marque])
790            self.arg_selected = marque
791         except:
792            pass
793
794     def chooseitemsurligne(self,mot,label,commande):
795         """ Active la méthode de choix passée en argument"""
796         try:
797            mot=self.arg_selected
798            commande(mot)
799         except AsException,e:
800            raison=str(e)
801            showerror(raison.split('\n')[0],raison)
802
803     def chooseitem(self,mot,label,commande):
804         """ Active la méthode de choix passée en argument"""
805         try:
806            commande(mot)
807         except AsException,e:
808            raison=str(e)
809            showerror(raison.split('\n')[0],raison)
810         
811     def selectNextItem(self,mot,label):
812         try :
813            index=self.liste.index(mot)
814            indexsuivant=index+1
815            motsuivant=self.liste[indexsuivant]
816            labelsuivant=self.dico_labels[motsuivant]
817            self.clear_marque()
818            if self.selection != None :
819               self.deselectitem(self.selection[1],self.selection[0],self.selection[2],)
820               self.selection = (mot,label,self.selection[2])
821            self.highlightitem(labelsuivant)
822            self.arg_selected=motsuivant
823            labelsuivant.focus_set()
824         # PN il faut faire quelque chose pour être dans la fenetre
825         except:
826            pass
827            
828         
829     def selectitem(self,mot,label,commande) :
830         """ Met l'item sélectionné (représenté par son label) en surbrillance
831             et lance la commande associée au double-clic"""
832         self.clear_marque()
833         if self.selection != None :
834             self.deselectitem(self.selection[1],self.selection[0],self.selection[2],)
835         self.highlightitem(label)
836         self.selection = (mot,label,commande)
837         self.arg_selected = mot
838         commande(mot)
839
840     def highlightitem(self,label) :
841         """ Met l'item représenté par son label en surbrillance """
842         label.configure(bg='#00008b',fg='white')
843         
844     def markitem(self,label):
845         """ Met l'item (représenté par son label) en rouge """
846         label.configure(bg='gray95',fg='red')
847         
848     def deselectitem(self,label,mot='',commande=None) :
849         """ Remet l'item (représenté par son label) en noir"""
850         label.configure(bg='gray95',fg='black')
851         self.arg_selected = ''
852         if commande != None : commande(mot)
853
854     def cherche_selected_item(self):
855         index=self.MCbox.index(self.selection[1])
856         lign,col=map(int,string.split(index,'.'))
857         return lign
858
859     def remove_selected_item(self):
860         index=self.MCbox.index(self.selection[1])
861         lign,col=map(int,string.split(index,'.'))
862         del self.liste[lign-1]
863         self.affiche_liste()
864
865     def entry_changed(self,event=None):
866         """ Cette méthode est invoquée chaque fois que l'utilisateur modifie le contenu
867         de l'entry et frappe <Return>"""
868         if self.arg_selected != '' : self.deselectitem(self.dico_labels[self.arg_selected])
869         filtre = self.entry.get()+"*"
870         FILTRE = string.upper(filtre)
871         for arg in self.liste :
872             if fnmatch.fnmatch(arg,filtre) or fnmatch.fnmatch(arg,FILTRE) :
873                 self.highlightitem(self.dico_labels[arg])
874                 index = self.MCbox.index(self.dico_labels[arg])
875                 self.MCbox.see(index)
876                 self.arg_selected = arg
877                 break
878         try :
879           self.dico_labels[self.arg_selected].focus_set()
880         except :
881           pass
882
883     def get_liste_old(self):
884         return self.liste
885
886     # PN attention à la gestion des paramétres
887     # cela retourne H = 1 , et ni H, ni 1
888     #            print repr(val)
889     #            print val.__class__.__name__
890     def get_liste(self):
891         l=[]
892         for val in self.liste:
893 #            try:
894 #                v = eval(val)
895 #               l.append(v)
896 #            except:
897                 l.append(val)
898         return l
899     
900     def put_liste(self,liste):
901         self.liste = liste
902         self.affiche_liste()
903
904 class Affichage :
905   """ Cette classe permet d'afficher au lancement d'EFICAS le message
906       d'attente et la barre de progression"""
907   def __init__(self,master,message,barre ='oui'):
908       from Tools.foztools.foztools import Slider
909       fonte=fontes.standard12_gras
910       self.master=master
911       self.frame = Frame(self.master)
912       self.frame.pack(expand=1,fill='both')
913       self.mess = Label(self.frame,text=message,justify='center',
914                         bd=2,relief='groove',font=fonte)
915       self.mess.pack(in_ = self.frame,side='top',expand=1,fill='both')
916       self.progress = Slider(self.frame,value=0,max=100,orientation='horizontal',
917                              fillColor='#00008b',width=200,height=30,
918                              background='white',labelColor='red')
919       if barre == 'oui':
920           self.progress.frame.pack(in_=self.frame,side='top')
921       self.master.update()
922       if barre == 'oui':
923           self.progress.frame.after(1000,self.update)
924
925   def configure(self,**options):
926       if options.has_key('message'):
927           self.mess.configure(text=options['message'])
928       if options.has_key('barre'):
929           if options['barre'] == 'oui' :
930               self.progress.frame.pack(in_=self.frame,side='top')
931           elif options['barre'] == 'non' :
932               self.progress.frame.pack_forget()
933       self.master.update_idletasks()
934       
935   def quit(self):
936       self.frame.destroy()
937       self.master.update()
938
939   def update(self,event=None):
940       """ Permet de faire avancer la barre de progression """
941       try :
942           bar=self.progress
943           bar.value = bar.value+self.increment
944           bar.update()
945           self.master.after(100,self.update)
946       except:
947           pass
948
949   def configure_barre(self,nb):
950       """ Calcule l'incrément de progression de la barre en fonction
951           du nombre d'opérations à effectuer afin que le compteur
952           soit à 100% à la fin des opérations"""
953       self.increment = 100./nb
954       self.progress.update()
955
956 class Ask_Format_Fichier :
957     """
958     Cette classe permet de créer une fenêtre Toplevel dans laquelle
959     on propose le choix du format de fichier de commandes à ouvrir
960     """
961     def __init__(self,appli):
962         self.fenetre = Toplevel()
963         self.fenetre.configure(width = 250,height=150)
964         self.fenetre.protocol("WM_DELETE_WINDOW", self.quit)
965         self.fenetre.title("Choix du format du fichier de commandes")
966         # définition des frames
967         self.frame_texte = Frame(self.fenetre)
968         self.frame_radioboutons = Frame(self.fenetre)
969         self.frame_bouton_ok = Frame(self.fenetre)
970         self.frame_texte.place(relx=0,rely=0,relwidth=1,relheight=0.3)
971         self.frame_radioboutons.place(relheight=0.5,relx=0,rely=0.3,relwidth=1.)
972         self.frame_bouton_ok.place(relheight=0.2,relx=0,rely=0.8,relwidth=1.)
973         # définition de la zone texte et du scrollbar
974         zone_texte = Label(self.frame_texte,text = "Format du fichier à ouvrir :")
975         zone_texte.pack(side='top',fill='both',expand=1,padx=5,pady=10)
976         # définition des radioboutons
977         Radiobutton(self.frame_radioboutons,text='Format Aster (Code_Aster --> v5)',
978                     variable=appli.format_fichier,value='Aster').pack(anchor='n')
979         Radiobutton(self.frame_radioboutons,text='Format Python (Code_Aster v6-->)',
980                     variable=appli.format_fichier,value='Python').pack(anchor='n')
981         # création du bouton OK
982         Button(self.frame_bouton_ok,text='OK',command=self.quit).pack(anchor='n')
983         # centrage de la fenêtre
984         centerwindow(self.fenetre)
985
986     def quit(self):
987         self.fenetre.destroy()
988
989 class BARRE_K2000(Toplevel):
990     def __init__(self,master=None,text = ""):
991         Toplevel.__init__(self,master,relief='groove')
992         self.master.iconify()
993         self.geometry("250x100+0+0")
994         self.protocol("WM_DELETE_WINDOW",self.quit)
995         # frame principale dans self (= Toplevel)
996         self.frame = Frame(self)
997         self.frame.place(relwidth=1,relheight=1)
998         # frame contenant le texte à afficher 
999         self.frame_text = Frame(self.frame)
1000         self.frame_text.place(relwidth=1,relheight=0.75,rely=0)
1001         # frame contenant le canvas de la barre
1002         self.frame_canv = Frame(self.frame)
1003         self.frame_canv.place(relwidth=1,relheight=0.25,rely=0.75)
1004         # canvas dans lequel sera affichée la barre K2000
1005         self.canvas = Canvas(self.frame_canv)
1006         self.canvas.place(relx=0.5,rely=0.5,relheight=0.8,relwidth=0.8,anchor='center')
1007         # on affiche le texte et la barre
1008         self.build_text(text)
1009         self.build_batons()
1010         #self.overrideredirect(1)
1011         # on active la barre ...
1012         self.master.after(1000,self.launch)
1013         # on centre la fenêtre
1014         centerwindow(self)
1015         self.focus()
1016
1017     def build_text(self,text):
1018         """
1019         Affichage de text dans frame_text
1020         """
1021         self.texte_var = StringVar()
1022         self.texte_var.set(text)
1023         Label(self.frame_text,textvariable=self.texte_var).place(relx=0.5,rely=0.5,anchor='center')
1024         
1025     def build_batons(self):
1026         """
1027         Construit la suite de bâtons dans le canvas
1028         """
1029         self.l_batons=[]
1030         self.black = -1
1031         self.sens = 'D'
1032         self.quit = 0
1033         for i in range(0,40):
1034             id = self.canvas.create_rectangle(i*5,0,(i+1)*5,20,fill='gray90',outline='')
1035             self.l_batons.append(id)
1036
1037     def launch(self):
1038         """
1039         Active la barre K2000 en affichant les bâtons avec des couleurs en dégradé
1040         """
1041         if self.quit == 1 :
1042             self.destroy()
1043             self.master.deiconify()
1044             return
1045         if self.sens == 'D':
1046             self.black = self.black+1
1047             l_bat = self.l_batons[0:self.black+1]
1048             l_bat.reverse()
1049         elif self.sens == 'G':
1050             self.black = self.black-1
1051             l_bat = self.l_batons[self.black:]
1052         i=0
1053         for bat in l_bat :
1054             num_color = 5+i*10
1055             if num_color < 10 : color = 'black'
1056             elif num_color > 90 : color = 'white'
1057             else: color = 'gray'+`num_color`
1058             self.canvas.itemconfigure(bat,fill=color)
1059             i=i+1
1060         if self.black == len(self.l_batons) :
1061             self.sens = 'G'
1062         if self.black == 0 and self.sens == 'G':self.sens = 'D'
1063         self.after(80,self.launch)
1064
1065     def update_text(self,new_text):
1066         """
1067         Remplace le texte affiché par new_text
1068         """
1069         self.texte_var.set(new_text)
1070         
1071     def quit(self):
1072         self.quit = 1        
1073
1074 class ListeChoixParGroupes(ListeChoix) :
1075     """ 
1076         Cette classe est utilisée pour afficher une liste de commandes classées par
1077         groupes. L'utilisateur peut réaliser des actions de selection
1078         qui déclenchent des actions spécifiées par les bindings contenus dans liste_commandes
1079     """
1080     def __init__(self,parent,page,liste_groupes,dict_groupes,liste_commandes=[],liste_marques =[],
1081                       active ='oui',filtre='non',titre=''):
1082         self.parent = parent
1083         self.page = page
1084         self.liste_groupes = liste_groupes
1085         self.dict_groupes = dict_groupes
1086         self.dico_labels={}
1087         self.selection = None
1088         self.liste_commandes = liste_commandes
1089         self.liste_marques = liste_marques
1090         self.arg_selected=''
1091         self.active = active
1092         self.titre = titre
1093         self.filtre = filtre
1094         self.init()
1095
1096     def affiche_liste(self):
1097         """ Affiche la liste dans la fenêtre"""
1098         liste_labels=[]
1099         self.MCbox.config(state=NORMAL)
1100         self.MCbox.delete(1.0,END)
1101         for grp in self.liste_groupes:
1102            # On itère sur les groupes
1103            if grp == "CACHE":continue
1104            liste_commandes=self.dict_groupes[grp]
1105            text="GROUPE<<<<<<<< "+grp+" "
1106            text=text+">"*max(0,30-len(text))
1107            label = Label(self.MCbox,
1108                         text = text,
1109                         fg = 'black',bg = 'gray95',justify = 'left')
1110            # On stocke la relation entre le nom de la commande et le label
1111            self.dico_labels[grp]=label
1112            liste_labels.append(label)
1113            self.MCbox.window_create(END,
1114                                    window=label,
1115                                    stretch = 1)
1116            self.MCbox.insert(END,'\n')
1117            for cmd in liste_commandes:
1118               label = Label(self.MCbox,
1119                         text = cmd,
1120                         fg = 'black',bg = 'gray95',justify = 'left')
1121               # On stocke la relation entre le nom de la commande et le label
1122               self.dico_labels[cmd]=label
1123               self.MCbox.window_create(END,
1124                                    window=label,
1125                                    stretch = 1)
1126               self.MCbox.insert(END,'\n')
1127               if self.active == 'oui':
1128                   label.bind(self.liste_commandes[0][0],
1129                          lambda e,s=self,c=self.liste_commandes[0][1],x=cmd,l=label : s.selectitem(x,l,c))
1130                   label.bind(self.liste_commandes[1][0],
1131                          lambda e,s=self,c=self.liste_commandes[1][1],x=cmd,l=label : s.deselectitem(l,x,c))
1132                   label.bind(self.liste_commandes[2][0],
1133                          lambda e,s=self,c=self.liste_commandes[2][1],x=cmd,l=label : s.chooseitem(x,l,c))
1134                   label.bind("<Key-Down>", self.selectNextItem(event))
1135
1136         for marque in self.liste_marques:
1137            try:
1138               self.markitem(liste_labels[marque])
1139            except:
1140               pass
1141
1142         self.MCbox.config(state=DISABLED)
1143         self.selection = None
1144
1145     def entry_changed(self,event=None):
1146         """ 
1147             Cette méthode est invoquée chaque fois que l'utilisateur modifie le contenu
1148             de l'entry et frappe <Return>
1149         """
1150         if self.arg_selected != '' : self.deselectitem(self.dico_labels[self.arg_selected])
1151         filtre = self.entry.get()+"*"
1152         FILTRE = string.upper(filtre)
1153         #
1154         # On cherche d'abord dans les noms de groupe
1155         # puis dans les noms de commande groupe par groupe
1156         #
1157         for grp in self.liste_groupes:
1158             if fnmatch.fnmatch(grp,filtre) or fnmatch.fnmatch(grp,FILTRE) :
1159                 index = self.MCbox.index(self.dico_labels[grp])
1160                 self.MCbox.see(index)
1161                 # On ne selectionne pas le groupe
1162                 #self.arg_selected = grp
1163                 # On a trouve un groupe on arrete la recherche
1164                 return
1165
1166         for grp in self.liste_groupes:
1167            for cmd in self.dict_groupes[grp] :
1168               if fnmatch.fnmatch(cmd,filtre) or fnmatch.fnmatch(cmd,FILTRE) :
1169                  self.highlightitem(self.dico_labels[cmd])
1170                  index = self.MCbox.index(self.dico_labels[cmd])
1171                  self.MCbox.see(index)
1172                  self.arg_selected = cmd
1173                  # On a trouve une commande  on arrete la recherche
1174                  return
1175