installation textometrieR
[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     if 'Rerror' in dir(parent) :
392         txt += parent.Rerror
393         parent.Rerror = ''
394     log.info(txt)
395     dial.text_ctrl_1.write(txt)
396     dial.CenterOnParent()
397     dial.ShowModal()
398     dial.Destroy()
399     
400 def PlaySound(parent):
401     if parent.pref.getboolean('iramuteq', 'sound') :
402         try:
403             if "gtk2" in wx.PlatformInfo:
404                 error = Popen(['aplay','-q',os.path.join(parent.AppliPath,'son_fin.wav')])
405             else :    
406                 sound = wx.Sound(os.path.join(parent.AppliPath, 'son_fin.wav'))
407                 sound.Play(wx.SOUND_SYNC)
408         except :
409             print 'pas de son'
410
411 def ReadDicoAsDico(dicopath):
412     with codecs.open(dicopath, 'r', 'UTF8') as f:
413         content = f.readlines()
414     dico = {}
415     for line in content :
416         if line[0] != u'':
417             line = line.replace(u'\n', '').replace('"', '').split('\t')
418             dico[line[0]] = line[1:]
419     return dico
420
421 def ReadLexique(parent, lang = 'french', filein = None):
422     if lang != 'other' :
423         if filein is None :
424             parent.lexique = ReadDicoAsDico(parent.DictPath.get(lang, 'french'))
425         else :
426             parent.lexique = ReadDicoAsDico(filein)
427     else :
428         parent.lexique = {}
429
430 def ReadList(filein, encoding = sys.getdefaultencoding()):
431     #file = open(filein)
432     file = codecs.open(filein, 'r', encoding)
433     content = file.readlines()
434     file.close()
435     first = content.pop(0)
436     first = first.replace('\n', '').replace('\r','').replace('\"', '').split(';')
437     dict = {}
438     i = 0
439     for line in content:
440         line = line.replace('\n', '').replace('\r','').replace('\"', '').replace(',', '.')
441         line = line.split(';')
442         nline = [line[0]]
443         for val in line[1:]:
444             if val == u'NA' :
445                 don = ''
446             else: 
447                 try:
448                     don = int(val)
449                 except:
450                     don = float('%.5f' % float(val))
451             nline.append(don)
452         dict[i] = nline
453         i += 1
454     return dict, first
455
456 def exec_RCMD(rpath, command) :
457     log.info('R CMD INSTALL %s' % command)
458     rpath = rpath.replace('\\','\\\\')
459     error = call(["%s" % rpath, 'CMD', 'INSTALL', "%s" % command])
460     return error
461
462 def exec_rcode(rpath, rcode, wait = True, graph = False):
463     log.info("R Script : %s" % rcode)
464     needX11 = False
465     if sys.platform == 'darwin' :
466         try :
467             macversion = platform.mac_ver()[0].split('.')
468             print macversion
469             if int(macversion[1]) < 5 :
470                 needX11 = True
471             else :
472                 needX11 = False
473         except :
474             needX11 = False
475
476     rpath = rpath.replace('\\','\\\\')
477     if not graph :
478         if wait :
479             if sys.platform == 'win32':
480                 error = call(["%s" % rpath, "--vanilla","--slave","-f", "%s" % rcode])
481             else :
482                 error = call([rpath, '--vanilla','--slave',"-f %s" % rcode])
483             return error
484         else :
485             if sys.platform == 'win32':
486                 pid = Popen(["%s" % rpath, '--vanilla','--slave','-f', "%s" % rcode])
487             else :
488                 pid = Popen([rpath, '--vanilla','--slave',"-f %s" % rcode], stderr = PIPE)
489             return pid
490     else :
491         if wait :
492             if sys.platform == 'win32':
493                 error = call(["%s" % rpath, '--vanilla','--slave','-f', "%s" % rcode])
494             elif sys.platform == 'darwin' and needX11:
495                 os.environ['DISPLAY'] = ':0.0'
496                 error = call([rpath, '--vanilla','--slave',"-f %s" % rcode])
497             else :
498                 error = call([rpath, '--vanilla','--slave',"-f %s" % rcode])
499             return error
500         else :
501             if sys.platform == 'win32':
502                 pid = Popen(["%s" % rpath, '--vanilla','--slave','-f', "%s" % rcode])
503             elif sys.platform == 'darwin' and needX11:
504                 os.environ['DISPLAY'] = ':0.0'
505                 pid = Popen([rpath, '--vanilla','--slave',"-f %s" % rcode], stderr = PIPE)
506             else :
507                 pid = Popen([rpath, '--vanilla','--slave',"-f %s" % rcode], stderr = PIPE)
508             return pid
509
510 def check_Rresult(parent, pid) :
511     if isinstance(pid, Popen) :
512         if pid.returncode != 0 :
513             error = pid.communicate()
514             error = [str(error[0]), error[1]]
515             if error[1] is None :
516                 error[1] = 'None'
517             parent.Rerror = '\n'.join([str(pid.returncode), '\n'.join(error)])
518             #try :
519             raise Exception('\n'.join([u'Erreur R', '\n'.join(error[1:])]))
520             return False
521             #except :
522             #    BugReport(parent)
523     else :
524         if pid != 0 :
525             #try :
526             raise Exception(u'Erreur R')
527             return False
528             #except :
529             #    BugReport(parent)
530
531 def print_liste(filename,liste):
532     with open(filename,'w') as f :
533         for graph in liste :
534             f.write(';'.join(graph)+'\n')
535
536 def read_list_file(filename, encoding = sys.getdefaultencoding()):
537     with codecs.open(filename,'rU', encoding) as f :
538         content=f.readlines()
539         ncontent=[line.replace('\n','').split(';') for line in content if line.strip() != '']
540     return ncontent
541         
542 class MessageImage(wx.Frame):
543     def __init__(self, parent,title, size):
544         wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = title, pos = wx.DefaultPosition, size = size, style = wx.DEFAULT_FRAME_STYLE )
545         self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
546         self.imageFile = False
547         self.imagename = u"chi_classe.png"
548         self.HtmlPage = wx.html.HtmlWindow(self, -1)
549         self.HtmlPage.SetMinSize(size)
550         if "gtk2" in wx.PlatformInfo:
551             self.HtmlPage.SetStandardFonts()
552         self.HtmlPage.SetFonts('Courier', 'Courier')
553         
554         self.button_1 = wx.Button(self, wx.ID_CANCEL)
555         self.button_2 = wx.Button(self, wx.ID_SAVE)
556         self.Bind(wx.EVT_BUTTON, self.OnCloseMe, self.button_1)
557         self.Bind(wx.EVT_BUTTON, self.OnSaveImage, self.button_2)
558         self.do_layout()
559
560     def do_layout(self):
561         self.sizer_1 = wx.BoxSizer(wx.VERTICAL)
562         self.sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
563         self.sizer_1.Add(self.HtmlPage, 2, wx.EXPAND, 0)
564
565         self.m_sdbSizer1 = wx.StdDialogButtonSizer()
566         self.m_sdbSizer1.AddButton(  self.button_2 )
567         self.m_sdbSizer1.AddButton(  self.button_1 )
568         self.m_sdbSizer1.Realize()
569         self.sizer_1.Add(self.m_sdbSizer1, 0, wx.EXPAND, 5)
570         self.SetSizer(self.sizer_1)
571         self.Layout()
572         self.sizer_1.Fit( self )
573
574     def addsaveimage(self, imageFile) :
575         self.imageFile = imageFile
576         
577     def OnCloseMe(self, event):
578         self.Destroy()
579
580     def OnSaveImage(self, event) :
581         dlg = wx.FileDialog(
582             self, message="Enregistrer sous...", defaultDir=os.getcwd(),
583             defaultFile= self.imagename, wildcard="png|*.png", style=wx.SAVE | wx.OVERWRITE_PROMPT
584             )
585         dlg.SetFilterIndex(2)
586         dlg.CenterOnParent()
587         if dlg.ShowModal() == wx.ID_OK:
588             path = dlg.GetPath()
589             copyfile(self.imageFile, path)
590             
591
592 def progressbar(self, maxi) :
593     if 'parent' in dir(self) :
594         parent = self.parent
595     else :
596         parent = self
597     return wx.ProgressDialog("Traitements",
598                              "Veuillez patienter...",
599                              maximum=maxi,
600                              parent=parent,
601                              style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE | wx.PD_ELAPSED_TIME | wx.PD_CAN_ABORT
602                              )
603
604
605 def treat_var_mod(variables) :
606     var_mod = {}
607     for variable in variables :
608         if u'_' in variable :
609             forme = variable.split(u'_')
610             var = forme[0]
611             mod = forme[1]
612             if not var in var_mod :
613                 var_mod[var] = [variable]
614             else :
615                 if not mod in var_mod[var] :
616                     var_mod[var].append(variable)
617     return var_mod