conf
[iramuteq] / functions.py
1 #!/bin/env python
2 # -*- coding: utf-8 -*-
3 #Author: Pierre Ratinaud
4 #Copyright (c) 2008-2012 Pierre Ratinaud
5 #Lisense: GNU/GPL
6
7 import wx
8 import re
9 from ConfigParser import ConfigParser
10 from subprocess import Popen, call, PIPE
11 import thread
12 import os
13 import ast
14 import sys
15 import csv
16 import platform
17 import traceback
18 import codecs
19 import locale
20 import datetime
21 from copy import copy
22 from shutil import copyfile
23 #from dialog import BugDialog
24 import logging
25
26 log = logging.getLogger('iramuteq')
27
28
29 indices_simi = [u'cooccurrence' ,'pourcentage de cooccurrence',u'Russel',u'Jaccard', 'Kulczynski1', 'Kulczynski2', 'Mountford', 'Fager', 'simple matching', 'Hamman', 'Faith', 'Tanimoto', 'Dice', 'Phi', 'Stiles', 'Michael', 'Mozley', 'Yule', 'Yule2', 'Ochiai', 'Simpson', 'Braun-Blanquet','Chi-squared', 'Phi-squared', 'Tschuprow', 'Cramer', 'Pearson', 'binomial']
30
31
32 class History :
33     def __init__(self, filein, syscoding = 'utf8') :
34         self.filein = filein
35         self.syscoding = syscoding
36         self.corpora = {}
37         self.openedcorpus = {}
38         self.analyses = {}
39         self.history = {}
40         self.opened = {}
41         self.read()
42
43     def read(self) :
44         self.conf = DoConf(self.filein)
45         self.order = {}
46         self.ordera = {}
47         for i, section in enumerate(self.conf.conf.sections()) :
48             if self.conf.conf.has_option(section, 'corpus_name') :
49                 self.corpora[section] = self.conf.getoptions(section)
50                 self.order[len(self.order)] = section
51             else :
52                 self.analyses[section] = self.conf.getoptions(section)
53                 self.ordera[len(self.ordera)] = section
54         todel = []
55         for corpus in self.corpora :
56             self.history[corpus] = copy(self.corpora[corpus])
57         for analyse in self.analyses :
58             if self.analyses[analyse]['corpus'] in self.corpora :
59                 if 'analyses' in self.history[self.analyses[analyse]['corpus']] :
60                     self.history[self.analyses[analyse]['corpus']]['analyses'].append(self.analyses[analyse])
61                     todel.append(analyse)
62                 else :
63                     self.history[self.analyses[analyse]['corpus']]['analyses'] = [self.analyses[analyse]]
64                     todel.append(analyse)
65             else :
66                  self.history[analyse] = self.analyses[analyse]
67         #for analyse in todel :
68         #    del self.analyses[analyse]
69     
70     def write(self) :
71         sections = self.corpora.keys() + self.analyses.keys()
72         parametres = [self.corpora[key] for key in self.corpora.keys()] + [self.analyses[key] for key in self.analyses.keys()]
73         self.conf.makeoptions(sections, parametres)
74         log.info('write history')
75
76     def add(self, analyse) :
77         tosave = {'uuid' : analyse['uuid'], 'ira': analyse['ira'], 'type' : analyse['type']}
78         if analyse.get('corpus', False) :
79             tosave['corpus'] = analyse['corpus']
80             tosave['name'] = analyse['name']
81             acorpus_uuid =  analyse['corpus']
82             if acorpus_uuid in self.corpora :
83                 if 'analyses' in self.history[acorpus_uuid] :
84                     self.history[acorpus_uuid]['analyses'].append(tosave)
85                 else :
86                     self.history[acorpus_uuid]['analyses'] = [tosave]
87                 self.analyses[analyse['uuid']] = tosave
88             else :
89                 self.analyses[analyse['uuid']] = tosave
90         elif 'corpus_name' in analyse :
91             tosave['corpus_name'] = analyse['corpus_name']
92             self.history[analyse['uuid']] = tosave
93             self.corpora[analyse['uuid']] = tosave
94         self.write()
95
96     def delete(self, uuid, corpus = False) :
97         if corpus :
98             del self.corpora[uuid]
99             self.conf.conf.remove_section(uuid)
100             for analyse in self.history[uuid].get('analyses', [False]) :
101                 if analyse :
102                     del self.analyses[analyse['uuid']]
103                     self.conf.conf.remove_section(analyse['uuid'])
104         else :
105             del self.analyses[uuid]
106             self.conf.conf.remove_section(uuid)
107         self.write()
108
109     def addtab(self, analyse) :
110         self.opened[analyse['uuid']] = analyse
111
112     def rmtab(self, analyse) :
113         del self.opened[analyse['uuid']]
114     
115     def __str__(self) :
116         return str(self.history)
117
118 class DoConf :
119     def __init__(self, configfile=None, diff = None, parametres = None) :
120         self.configfile = configfile
121         self.conf = ConfigParser()
122         if configfile is not None :
123             self.conf.readfp(codecs.open(configfile, 'r', 'utf8'))
124         self.parametres = {}
125         if parametres is not None :
126             self.doparametres(parametres)
127
128     def doparametres(self, parametres) :
129         return parametres
130
131     def getsections(self) :
132         return self.conf.sections()
133
134     def getoptions(self, section = None, diff = None):
135         parametres = {}
136         if section is None :
137             section = self.conf.sections()[0]
138         for option in self.conf.options(section) :
139             if self.conf.get(section, option).isdigit() :
140                 parametres[option] = int(self.conf.get(section, option))
141             elif self.conf.get(section, option) == 'False' :
142                 parametres[option] = False
143             elif self.conf.get(section, option) == 'True' :
144                 parametres[option] = True
145             elif self.conf.get(section, option).startswith('(') and self.conf.get(section, option).endswith(')') :
146                 parametres[option] = ast.literal_eval(self.conf.get(section, option))
147             else :
148                 parametres[option] = self.conf.get(section, option)
149         if 'type' not in parametres :
150             parametres['type'] = section
151         return parametres
152             
153     def makeoptions(self, sections, parametres, outfile = None) :
154         txt = ''
155         for i, section in enumerate(sections) :
156             txt += '[%s]\n' % section
157             if not self.conf.has_section(section) :
158                 self.conf.add_section(section)
159             for option in parametres[i] :
160                 if isinstance(parametres[i][option], int) :
161                     self.conf.set(section, option, `parametres[i][option]`)
162                     txt += '%s = %i\n' % (option, parametres[i][option])
163                 elif isinstance(parametres[i][option], basestring) :
164                     self.conf.set(section, option, parametres[i][option].encode('utf8'))
165                     txt += '%s = %s\n' % (option, parametres[i][option].encode('utf8'))
166                 elif isinstance(parametres[i][option], wx.Colour) :
167                     self.conf.set(section, option, str(parametres[i][option]))
168                     txt += '%s = %s' % (option, str(parametres[i][option]))
169                 elif option == 'analyses' :
170                     pass
171                 else :
172                     self.conf.set(section, option, `parametres[i][option]`)
173                     txt += '%s = %s' % (option, `parametres[i][option]`)
174         if outfile is None :
175             outfile = self.configfile
176         with codecs.open(outfile, 'w', 'utf8') as f :
177             f.write(txt)
178             #self.conf.write(f)
179
180     def totext(self, parametres) :
181         #txt = ['Corpus']
182         txt = []
183         for val in parametres :
184             if isinstance(parametres[val], int) :
185                 txt.append(' \t\t: '.join([val, `parametres[val]`]))
186             else :
187                 txt.append(' \t\t: '.join([val, parametres[val]]))
188         return '\n'.join(txt)
189
190
191 def write_tab(tab, fileout) :
192         writer = csv.writer(open(fileout, 'wb'), delimiter=';', quoting = csv.QUOTE_NONNUMERIC)
193         writer.writerows(tab)
194
195 class BugDialog(wx.Dialog):
196     def __init__(self, *args, **kwds):
197         # begin wxGlade: MyDialog.__init__
198         kwds["style"] = wx.DEFAULT_DIALOG_STYLE
199         kwds["size"] = wx.Size(500, 200)
200         wx.Dialog.__init__(self, *args, **kwds)
201         self.text_ctrl_1 = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE)
202         self.button_1 = wx.Button(self, wx.ID_OK, "")
203
204         self.__set_properties()
205         self.__do_layout()
206         # end wxGlade
207
208     def __set_properties(self):
209         # begin wxGlade: MyDialog.__set_properties
210         self.SetTitle("Bug")
211         self.SetMinSize(wx.Size(500, 200))
212         self.text_ctrl_1.SetMinSize(wx.Size(500, 200))
213         
214         # end wxGlade
215
216     def __do_layout(self):
217         # begin wxGlade: MyDialog.__do_layout
218         sizer_1 = wx.BoxSizer(wx.VERTICAL)
219         sizer_1.Add(self.text_ctrl_1, 1, wx.EXPAND, 0)
220         sizer_1.Add(self.button_1, 0, wx.ALIGN_CENTER_HORIZONTAL, 0)
221         self.SetSizer(sizer_1)
222         sizer_1.Fit(self)
223         self.Layout()
224
225
226 def CreateIraFile(DictPathOut, clusternb, corpname='corpus_name', section = 'analyse'):
227     AnalyseConf = ConfigParser()
228     AnalyseConf.read(DictPathOut['ira'])
229     AnalyseConf.add_section(section)
230     date = datetime.datetime.now().ctime()
231     AnalyseConf.set(section, 'date', str(date))
232     AnalyseConf.set(section, 'clusternb', clusternb)
233     AnalyseConf.set(section, 'corpus_name', corpname)
234
235     fileout = open(DictPathOut['ira'], 'w')
236     AnalyseConf.write(fileout)
237     fileout.close()
238
239 def sortedby(list, direct, *indices):
240
241     """
242         sortedby: sort a list of lists (e.g. a table) by one or more indices
243                   (columns of the table) and return the sorted list
244
245         e.g.
246          for list = [[2,3],[1,2],[3,1]]:
247          sortedby(list,1) will return [[3, 1], [1, 2], [2, 3]],
248          sortedby(list,0) will return [[1, 2], [2, 3], [3, 1]]
249     """
250
251     nlist = map(lambda x, indices=indices: 
252                  map(lambda i, x=x: x[i], indices) + [x],
253                  list)
254     if direct == 1:
255         nlist.sort()
256     elif direct == 2:
257         nlist.sort(reverse=True)
258     return map(lambda l: l[-1], nlist)
259
260 def add_type(line, dictlem):
261     if line[4] in dictlem:
262         line.append(dictlem[line[4]])
263     else :
264         line.append('')
265     return line
266
267 def treat_line_alceste(i, line) :
268     if line[0] == '*' or line[0] == '*****' :
269         return line + ['']
270     if line[5] == 'NA':
271         print 'NA', line[5]
272         pass
273     elif float(line[5].replace(',', '.')) < 0.0001:
274         line[5] = '< 0,0001'
275     elif float(line[5].replace(',', '.')) > 0.05:
276         line[5] = 'NS (%s)' % str(float(line[5].replace(',', '.')))[0:7]
277     else:
278         line[5] = str(float(line[5].replace(',', '.')))[0:7]
279     return [i, int(line[0]), int(line[1]), float(line[2]), float(line[3]), line[6], line[4], line[5]]
280
281 def ReadProfileAsDico(parent, File, Alceste=False, encoding = sys.getdefaultencoding()):
282     #print 'lecture des profils : ReadProfileAsDico'
283     #if Alceste :
284     #    print 'lecture du dictionnaire de type'
285     #    dictlem = {}
286     #    for line in parent.corpus.lem_type_list :
287     #        dictlem[line[0]] = line[1]
288     dictlem = {}
289     print 'lecture des profiles'
290     #encoding = sys.getdefaultencoding()
291     print encoding
292     FileReader = codecs.open(File, 'r', encoding)
293     Filecontent = FileReader.readlines()
294     FileReader.close()
295     DictProfile = {}
296     count = 0
297     rows = [row.replace('\n', '').replace("'", '').replace('\"', '').replace(',', '.').replace('\r','').split(';') for row in Filecontent]
298     rows.pop(0)
299     ClusterNb = rows[0][2]
300     rows.pop(0)
301     clusters = [row[2] for row in rows if row[0] == u'**']
302     valclusters = [row[1:4] for row in rows if row[0] == u'****']
303     lp = [i for i, line in enumerate(rows) if line[0] == u'****']
304     prof = [rows[lp[i] + 1:lp[i+1] - 1] for i in range(0, len(lp)-1)] + [rows[lp[-1] + 1:len(rows)]] 
305     if Alceste :
306         prof = [[add_type(row, dictlem) for row in pr] for pr in prof]
307         prof = [[treat_line_alceste(i,line) for i, line in enumerate(pr)] for pr in prof] 
308     else :
309         prof = [[line + [''] for line in pr] for pr in prof]
310         prof = [[treat_line_alceste(i,line) for i, line in enumerate(pr)] for pr in prof]
311     for i, cluster in enumerate(clusters):
312         DictProfile[cluster] = [valclusters[i]] + prof[i]
313     return DictProfile
314
315 def GetTxtProfile(dictprofile) :
316     proflist = []
317     for classe in range(0, len(dictprofile)) :
318         prof = dictprofile[str(classe + 1)]
319         clinfo = prof[0]
320         proflist.append('\n'.join([' '.join(['classe %i' % (classe + 1), '-', '%s uce sur %s - %s%%' % (clinfo[0], clinfo[1], clinfo[2])]), '\n'.join(['%5s|%5s|%6s|%6s|%8s|%8s|%20s\t%10s' % tuple([str(val) for val in line]) for line in prof if len(line)==8])]))
321     return '\n\n'.join(proflist)
322
323 def formatExceptionInfo(maxTBlevel=5):
324          cla, exc, trbk = sys.exc_info()
325          excName = cla.__name__
326          try:
327              excArgs = exc.args[0]
328          except :
329              excArgs = "<no args>"
330          excTb = traceback.format_tb(trbk, maxTBlevel)
331          return (excName, excArgs, excTb)
332
333
334 #fonction des etudiants de l'iut
335 def decoupercharact(chaine, longueur, longueurOptimale, separateurs = None) :
336     """
337         on part du dernier caractère, et on recule jusqu'au début de la chaîne.
338         Si on trouve un '$', c'est fini.
339         Sinon, on cherche le meilleur candidat. C'est-à-dire le rapport poids/distance le plus important.
340     """
341     separateurs = [[u'.', 60.0], [u'?', 60.0], [u'!', 60.0], [u'£$£', 60], [u':', 50.0], [u';', 40.0], [u',', 10.0], [u' ', 0.1]]
342     trouve = False                 # si on a trouvé un bon séparateur
343     iDecoupe = 0                # indice du caractere ou il faut decouper
344     
345     # on découpe la chaine pour avoir au maximum 240 caractères
346     longueur = min(longueur, len(chaine) - 1)
347     chaineTravail = chaine[:longueur + 1]
348     nbCar = longueur
349     meilleur = ['', 0, 0]        # type, poids et position du meilleur separateur
350     
351     # on vérifie si on ne trouve pas un '$'
352     indice = chaineTravail.find(u'$')
353     if indice > -1:
354         trouve = True
355         iDecoupe = indice
356
357     # si on ne trouve rien, on cherche le meilleur séparateur
358     if not trouve:
359         while nbCar >= 0:
360             caractere = chaineTravail[nbCar]
361             distance = abs(longueurOptimale - nbCar) + 1
362             meilleureDistance = abs(longueurOptimale - meilleur[2]) + 1
363
364             # on vérifie si le caractére courant est une marque de ponctuation
365             for s in separateurs:
366                 if caractere == s[0]:
367                     # si c'est une ponctuation 
368                     
369                     if s[1] / distance > float(meilleur[1]) / meilleureDistance:
370                         # print nbCar, s[0]
371                         meilleur[0] = s[0]
372                         meilleur[1] = s[1]
373                         meilleur[2] = nbCar
374                         trouve = True
375                         iDecoupe = nbCar
376                         
377                     # et on termine la recherche
378                     break
379
380             # on passe au caractère précédant
381             nbCar = nbCar - 1
382     
383     # si on a trouvé
384     if trouve:
385         fin = chaine[iDecoupe + 1:]
386         retour = chaineTravail[:iDecoupe]
387         return len(retour) > 0, retour.split(), fin
388     # si on a rien trouvé
389     return False, chaine.split(), ''
390
391 def BugReport(parent, error = None):
392     for ch in parent.GetChildren():
393         if "<class 'wx._windows.ProgressDialog'>" == str(type(ch)):
394             ch.Destroy()   
395     excName, exc, excTb = formatExceptionInfo()
396     if excName == 'Exception' :
397         txt = 'Message :\n'
398     else :
399         txt = u'            !== BUG ==!       \n'
400         txt += u'*************************************\n'
401         txt += '\n'.join(excTb).replace('    ', ' ')
402         txt += excName + '\n'
403     txt += exc
404
405     dial = BugDialog(parent)
406     #for line in formatExceptionInfo():
407     #    if type(line) == type([]):
408     #        for don in line:
409     #            txt += don.replace('    ', ' ')
410     #    else:
411     #        txt += line + '\n'
412     if 'Rerror' in dir(parent) :
413         txt += parent.Rerror
414         parent.Rerror = ''
415     #if error is not None :
416     #    txt += '\n%s\n' %error
417     print formatExceptionInfo()
418     log.error(txt)
419     dial.text_ctrl_1.write(txt)
420     dial.CenterOnParent()
421     dial.ShowModal()
422     #raise Exception('Bug')
423     
424 def PlaySound(parent):
425     if parent.pref.getboolean('iramuteq', 'sound') :
426         try:
427             if "gtk2" in wx.PlatformInfo:
428                 error = Popen(['aplay','-q',os.path.join(parent.AppliPath,'son_fin.wav')])
429             else :    
430                 sound = wx.Sound(os.path.join(parent.AppliPath, 'son_fin.wav'))
431                 sound.Play(wx.SOUND_SYNC)
432         except :
433             print 'pas de son'
434
435 def ReadDicoAsDico(dicopath):
436     with codecs.open(dicopath, 'r', 'UTF8') as f:
437         content = f.readlines()
438     dico = {}
439     for line in content :
440         if line[0] != u'':
441             line = line.replace(u'\n', '').replace('"', '').split('\t')
442             dico[line[0]] = line[1:]
443     return dico
444
445 def ReadLexique(parent, lang = 'french'):
446     if lang != 'other' :
447         parent.lexique = ReadDicoAsDico(parent.DictPath.get(lang, 'french'))
448     else :
449         parent.lexique = {}
450
451 def ReadList(filein, encoding = sys.getdefaultencoding()):
452     #file = open(filein)
453     file = codecs.open(filein, 'r', encoding)
454     content = file.readlines()
455     file.close()
456     first = content.pop(0)
457     first = first.replace('\n', '').replace('\r','').replace('\"', '').split(';')
458     dict = {}
459     i = 0
460     for line in content:
461         line = line.replace('\n', '').replace('\r','').replace('\"', '').replace(',', '.')
462         line = line.split(';')
463         nline = [line[0]]
464         for val in line[1:]:
465             if val == u'NA' :
466                 don = ''
467             else: 
468                 try:
469                     don = int(val)
470                 except:
471                     don = float('%.5f' % float(val))
472             nline.append(don)
473         dict[i] = nline
474         i += 1
475     return dict, first
476
477 def exec_rcode(rpath, rcode, wait = True, graph = False):
478     print rpath, rcode
479     needX11 = False
480     if sys.platform == 'darwin' :
481         try :
482             macversion = platform.mac_ver()[0].split('.')
483             print macversion
484             if int(macversion[1]) < 5 :
485                 needX11 = True
486             else :
487                 needX11 = False
488         except :
489             needX11 = False
490
491     rpath = rpath.replace('\\','\\\\')
492     if not graph :
493         if wait :
494             if sys.platform == 'win32':
495                 error = call(["%s" % rpath, "--vanilla","--slave","-f", "%s" % rcode])
496             else :
497                 error = call([rpath, '--vanilla','--slave',"-f %s" % rcode])
498             return error
499         else :
500             if sys.platform == 'win32':
501                 pid = Popen(["%s" % rpath, '--vanilla','--slave','-f', "%s" % rcode])
502             else :
503                 pid = Popen([rpath, '--vanilla','--slave',"-f %s" % rcode], stderr = PIPE)
504             return pid
505     else :
506         if wait :
507             if sys.platform == 'win32':
508                 error = call(["%s" % rpath, '--vanilla','--slave','-f', "%s" % rcode])
509             elif sys.platform == 'darwin' and needX11:
510                 os.environ['DISPLAY'] = ':0.0'
511                 error = call([rpath, '--vanilla','--slave',"-f %s" % rcode])
512             else :
513                 error = call([rpath, '--vanilla','--slave',"-f %s" % rcode])
514             return error
515         else :
516             if sys.platform == 'win32':
517                 pid = Popen(["%s" % rpath, '--vanilla','--slave','-f', "%s" % rcode])
518             elif sys.platform == 'darwin' and needX11:
519                 os.environ['DISPLAY'] = ':0.0'
520                 pid = Popen([rpath, '--vanilla','--slave',"-f %s" % rcode], stderr = PIPE)
521             else :
522                 pid = Popen([rpath, '--vanilla','--slave',"-f %s" % rcode], stderr = PIPE)
523             return pid
524
525 def check_Rresult(parent, pid) :
526     if isinstance(pid, Popen) :
527         if pid.returncode != 0 :
528             error = pid.communicate()
529             error = [str(error[0]), error[1]]
530             if error[1] is None :
531                 error[1] = 'None'
532             parent.Rerror = '\n'.join([str(pid.returncode), '\n'.join(error)])
533             try :
534                 raise Exception('\n'.join(u'Erreur R', '\n'.join(error[1:])))
535             except :
536                 BugReport(parent)
537     else :
538         if pid != 0 :
539             try :
540                 raise Exception(u'Erreur R')
541             except :
542                 BugReport(parent)
543
544 def print_liste(filename,liste):
545     with open(filename,'w') as f :
546         for graph in liste :
547             f.write(';'.join(graph)+'\n')
548
549 def read_list_file(filename, encoding = sys.getdefaultencoding()):
550     with codecs.open(filename,'rU', encoding) as f :
551         content=f.readlines()
552         ncontent=[line.replace('\n','').split(';') for line in content if line.strip() != '']
553     return ncontent
554         
555 class MessageImage(wx.Frame):
556     def __init__(self, *args, **kwds):
557         # begin wxGlade: MyFrame.__init__
558         kwds["style"] = wx.DEFAULT_FRAME_STYLE
559         wx.Frame.__init__(self, *args, **kwds)
560         #self.text_ctrl_1 = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE)
561         self.imageFile = False
562         self.imagename = u"chi_classe.png"
563         self.HtmlPage = wx.html.HtmlWindow(self, -1)
564         if "gtk2" in wx.PlatformInfo:
565             self.HtmlPage.SetStandardFonts()
566         self.HtmlPage.SetFonts('Courier', 'Courier')
567         
568         self.button_1 = wx.Button(self, -1, u"Fermer")
569         self.Bind(wx.EVT_BUTTON, self.OnCloseMe, self.button_1)
570         self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
571         self.__do_layout()
572         # end wxGlade
573
574     def __do_layout(self):
575         # begin wxGlade: MyFrame.__do_layout
576         sizer_1 = wx.BoxSizer(wx.VERTICAL)
577         self.sizer_2 = wx.BoxSizer(wx.VERTICAL)
578         self.sizer_2.Add(self.HtmlPage, 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0)
579         self.sizer_2.Add(self.button_1, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ADJUST_MINSIZE, 0)
580         sizer_1.Add(self.sizer_2, 1, wx.EXPAND, 0)
581         self.SetAutoLayout(True)
582         self.SetSizer(sizer_1)
583         # end wxGlade
584
585     def addsaveimage(self, imageFile) :
586         self.imageFile = imageFile
587         self.button_2 = wx.Button(self, -1, u"Enregistrer l'image...")
588         self.Bind(wx.EVT_BUTTON, self.OnSaveImage, self.button_2)
589         self.sizer_2.Add(self.button_2, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ADJUST_MINSIZE, 0)
590         self.Layout()
591
592     def OnCloseMe(self, event):
593         self.Close(True)
594
595     def OnCloseWindow(self, event):
596         self.Destroy()
597
598     def OnSaveImage(self, event) :
599         dlg = wx.FileDialog(
600             self, message="Enregistrer sous...", defaultDir=os.getcwd(),
601             defaultFile= self.imagename, wildcard="png|*.png", style=wx.SAVE | wx.OVERWRITE_PROMPT
602             )
603         dlg.SetFilterIndex(2)
604         dlg.CenterOnParent()
605         if dlg.ShowModal() == wx.ID_OK:
606             path = dlg.GetPath()
607             copyfile(self.imageFile, path)
608             
609
610 def progressbar(self, maxi) :
611     if 'parent' in dir(self) :
612         parent = self.parent
613     else :
614         parent = self
615     return wx.ProgressDialog("Traitements",
616                              "Veuillez patienter...",
617                              maximum=maxi,
618                              parent=parent,
619                              style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE | wx.PD_ELAPSED_TIME | wx.PD_CAN_ABORT
620                              )
621
622
623 def treat_var_mod(variables) :
624     var_mod = {}
625     for variable in variables :
626         if u'_' in variable :
627             forme = variable.split(u'_')
628             var = forme[0]
629             mod = forme[1]
630             if not var in var_mod :
631                 var_mod[var] = [variable]
632             else :
633                 if not mod in var_mod[var] :
634                     var_mod[var].append(variable)
635     return var_mod