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