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(parent, 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     print encoding
278     FileReader = codecs.open(File, 'r', encoding)
279     Filecontent = FileReader.readlines()
280     FileReader.close()
281     DictProfile = {}
282     count = 0
283     rows = [row.replace('\n', '').replace("'", '').replace('\"', '').replace(',', '.').replace('\r','').split(';') for row in Filecontent]
284     rows.pop(0)
285     ClusterNb = rows[0][2]
286     rows.pop(0)
287     clusters = [row[2] for row in rows if row[0] == u'**']
288     valclusters = [row[1:4] for row in rows if row[0] == u'****']
289     lp = [i for i, line in enumerate(rows) if line[0] == u'****']
290     prof = [rows[lp[i] + 1:lp[i+1] - 1] for i in range(0, len(lp)-1)] + [rows[lp[-1] + 1:len(rows)]] 
291     if Alceste :
292         prof = [[add_type(row, dictlem) for row in pr] for pr in prof]
293         prof = [[treat_line_alceste(i,line) for i, line in enumerate(pr)] for pr in prof] 
294     else :
295         prof = [[line + [''] for line in pr] for pr in prof]
296         prof = [[treat_line_alceste(i,line) for i, line in enumerate(pr)] for pr in prof]
297     for i, cluster in enumerate(clusters):
298         DictProfile[cluster] = [valclusters[i]] + prof[i]
299     return DictProfile
300
301 def GetTxtProfile(dictprofile) :
302     proflist = []
303     for classe in range(0, len(dictprofile)) :
304         prof = dictprofile[str(classe + 1)]
305         clinfo = prof[0]
306         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])]))
307     return '\n\n'.join(proflist)
308
309 def formatExceptionInfo(maxTBlevel=5):
310          cla, exc, trbk = sys.exc_info()
311          excName = cla.__name__
312          try:
313              excArgs = exc.args[0]
314          except :
315              excArgs = "<no args>"
316          excTb = traceback.format_tb(trbk, maxTBlevel)
317          return (excName, excArgs, excTb)
318
319
320 #fonction des etudiants de l'iut
321 def decoupercharact(chaine, longueur, longueurOptimale, separateurs = None) :
322     """
323         on part du dernier caractère, et on recule jusqu'au début de la chaîne.
324         Si on trouve un '$', c'est fini.
325         Sinon, on cherche le meilleur candidat. C'est-à-dire le rapport poids/distance le plus important.
326     """
327     separateurs = [[u'.', 60.0], [u'?', 60.0], [u'!', 60.0], [u'£$£', 60], [u':', 50.0], [u';', 40.0], [u',', 10.0], [u' ', 0.1]]
328     trouve = False                 # si on a trouvé un bon séparateur
329     iDecoupe = 0                # indice du caractere ou il faut decouper
330     
331     # on découpe la chaine pour avoir au maximum 240 caractères
332     longueur = min(longueur, len(chaine) - 1)
333     chaineTravail = chaine[:longueur + 1]
334     nbCar = longueur
335     meilleur = ['', 0, 0]        # type, poids et position du meilleur separateur
336     
337     # on vérifie si on ne trouve pas un '$'
338     indice = chaineTravail.find(u'$')
339     if indice > -1:
340         trouve = True
341         iDecoupe = indice
342
343     # si on ne trouve rien, on cherche le meilleur séparateur
344     if not trouve:
345         while nbCar >= 0:
346             caractere = chaineTravail[nbCar]
347             distance = abs(longueurOptimale - nbCar) + 1
348             meilleureDistance = abs(longueurOptimale - meilleur[2]) + 1
349
350             # on vérifie si le caractére courant est une marque de ponctuation
351             for s in separateurs:
352                 if caractere == s[0]:
353                     # si c'est une ponctuation 
354                     
355                     if s[1] / distance > float(meilleur[1]) / meilleureDistance:
356                         # print nbCar, s[0]
357                         meilleur[0] = s[0]
358                         meilleur[1] = s[1]
359                         meilleur[2] = nbCar
360                         trouve = True
361                         iDecoupe = nbCar
362                         
363                     # et on termine la recherche
364                     break
365
366             # on passe au caractère précédant
367             nbCar = nbCar - 1
368     
369     # si on a trouvé
370     if trouve:
371         fin = chaine[iDecoupe + 1:]
372         retour = chaineTravail[:iDecoupe]
373         return len(retour) > 0, retour.split(), fin
374     # si on a rien trouvé
375     return False, chaine.split(), ''
376
377 def BugReport(parent, error = None):
378     for ch in parent.GetChildren():
379         if "<class 'wx._windows.ProgressDialog'>" == str(type(ch)):
380             ch.Destroy()   
381     excName, exc, excTb = formatExceptionInfo()
382     if excName == 'Exception' :
383         txt = 'Message :\n'
384     else :
385         txt = u'            !== BUG ==!       \n'
386         txt += u'*************************************\n'
387         txt += '\n'.join(excTb).replace('    ', ' ')
388         txt += excName + '\n'
389     txt += exc
390
391     dial = BugDialog(parent)
392     #for line in formatExceptionInfo():
393     #    if type(line) == type([]):
394     #        for don in line:
395     #            txt += don.replace('    ', ' ')
396     #    else:
397     #        txt += line + '\n'
398     if 'Rerror' in dir(parent) :
399         txt += parent.Rerror
400         parent.Rerror = ''
401     #if error is not None :
402     #    txt += '\n%s\n' %error
403     print formatExceptionInfo()
404     log.error(txt)
405     dial.text_ctrl_1.write(txt)
406     dial.CenterOnParent()
407     dial.ShowModal()
408     #raise Exception('Bug')
409     
410 def PlaySound(parent):
411     if parent.pref.getboolean('iramuteq', 'sound') :
412         try:
413             if "gtk2" in wx.PlatformInfo:
414                 error = Popen(['aplay','-q',os.path.join(parent.AppliPath,'son_fin.wav')])
415             else :    
416                 sound = wx.Sound(os.path.join(parent.AppliPath, 'son_fin.wav'))
417                 sound.Play(wx.SOUND_SYNC)
418         except :
419             print 'pas de son'
420
421 def ReadDicoAsDico(dicopath):
422     with codecs.open(dicopath, 'r', 'UTF8') as f:
423         content = f.readlines()
424     dico = {}
425     for line in content :
426         if line[0] != u'':
427             line = line.replace(u'\n', '').replace('"', '').split('\t')
428             dico[line[0]] = line[1:]
429     return dico
430
431 def ReadLexique(parent, lang = 'french', filein = None):
432     if lang != 'other' :
433         if filein is None :
434             parent.lexique = ReadDicoAsDico(parent.DictPath.get(lang, 'french'))
435         else :
436             parent.lexique = ReadDicoAsDico(filein)
437     else :
438         parent.lexique = {}
439
440 def ReadList(filein, encoding = sys.getdefaultencoding()):
441     #file = open(filein)
442     file = codecs.open(filein, 'r', encoding)
443     content = file.readlines()
444     file.close()
445     first = content.pop(0)
446     first = first.replace('\n', '').replace('\r','').replace('\"', '').split(';')
447     dict = {}
448     i = 0
449     for line in content:
450         line = line.replace('\n', '').replace('\r','').replace('\"', '').replace(',', '.')
451         line = line.split(';')
452         nline = [line[0]]
453         for val in line[1:]:
454             if val == u'NA' :
455                 don = ''
456             else: 
457                 try:
458                     don = int(val)
459                 except:
460                     don = float('%.5f' % float(val))
461             nline.append(don)
462         dict[i] = nline
463         i += 1
464     return dict, first
465
466 def exec_rcode(rpath, rcode, wait = True, graph = False):
467     log.info("R Script : %s" % rcode)
468     needX11 = False
469     if sys.platform == 'darwin' :
470         try :
471             macversion = platform.mac_ver()[0].split('.')
472             print macversion
473             if int(macversion[1]) < 5 :
474                 needX11 = True
475             else :
476                 needX11 = False
477         except :
478             needX11 = False
479
480     rpath = rpath.replace('\\','\\\\')
481     if not graph :
482         if wait :
483             if sys.platform == 'win32':
484                 error = call(["%s" % rpath, "--vanilla","--slave","-f", "%s" % rcode])
485             else :
486                 error = call([rpath, '--vanilla','--slave',"-f %s" % rcode])
487             return error
488         else :
489             if sys.platform == 'win32':
490                 pid = Popen(["%s" % rpath, '--vanilla','--slave','-f', "%s" % rcode])
491             else :
492                 pid = Popen([rpath, '--vanilla','--slave',"-f %s" % rcode], stderr = PIPE)
493             return pid
494     else :
495         if wait :
496             if sys.platform == 'win32':
497                 error = call(["%s" % rpath, '--vanilla','--slave','-f', "%s" % rcode])
498             elif sys.platform == 'darwin' and needX11:
499                 os.environ['DISPLAY'] = ':0.0'
500                 error = call([rpath, '--vanilla','--slave',"-f %s" % rcode])
501             else :
502                 error = call([rpath, '--vanilla','--slave',"-f %s" % rcode])
503             return error
504         else :
505             if sys.platform == 'win32':
506                 pid = Popen(["%s" % rpath, '--vanilla','--slave','-f', "%s" % rcode])
507             elif sys.platform == 'darwin' and needX11:
508                 os.environ['DISPLAY'] = ':0.0'
509                 pid = Popen([rpath, '--vanilla','--slave',"-f %s" % rcode], stderr = PIPE)
510             else :
511                 pid = Popen([rpath, '--vanilla','--slave',"-f %s" % rcode], stderr = PIPE)
512             return pid
513
514 def check_Rresult(parent, pid) :
515     if isinstance(pid, Popen) :
516         if pid.returncode != 0 :
517             error = pid.communicate()
518             error = [str(error[0]), error[1]]
519             if error[1] is None :
520                 error[1] = 'None'
521             parent.Rerror = '\n'.join([str(pid.returncode), '\n'.join(error)])
522             try :
523                 raise Exception('\n'.join(u'Erreur R', '\n'.join(error[1:])))
524             except :
525                 BugReport(parent)
526     else :
527         if pid != 0 :
528             try :
529                 raise Exception(u'Erreur R')
530             except :
531                 BugReport(parent)
532
533 def print_liste(filename,liste):
534     with open(filename,'w') as f :
535         for graph in liste :
536             f.write(';'.join(graph)+'\n')
537
538 def read_list_file(filename, encoding = sys.getdefaultencoding()):
539     with codecs.open(filename,'rU', encoding) as f :
540         content=f.readlines()
541         ncontent=[line.replace('\n','').split(';') for line in content if line.strip() != '']
542     return ncontent
543         
544 class MessageImage(wx.Dialog):
545     def __init__(self, parent,title, size):
546         wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = title, pos = wx.DefaultPosition, size = size, style = wx.DEFAULT_DIALOG_STYLE )
547         self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
548         self.imageFile = False
549         self.imagename = u"chi_classe.png"
550         self.HtmlPage = wx.html.HtmlWindow(self, -1)
551         self.HtmlPage.SetMinSize(size)
552         if "gtk2" in wx.PlatformInfo:
553             self.HtmlPage.SetStandardFonts()
554         self.HtmlPage.SetFonts('Courier', 'Courier')
555         
556         self.button_1 = wx.Button(self, wx.ID_CANCEL)
557         self.button_2 = wx.Button(self, wx.ID_SAVE)
558         self.Bind(wx.EVT_BUTTON, self.OnCloseMe, self.button_1)
559         self.Bind(wx.EVT_BUTTON, self.OnSaveImage, self.button_2)
560         self.do_layout()
561
562     def do_layout(self):
563         self.sizer_1 = wx.BoxSizer(wx.VERTICAL)
564         self.sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
565         self.sizer_1.Add(self.HtmlPage, 2, wx.EXPAND, 0)
566
567         self.m_sdbSizer1 = wx.StdDialogButtonSizer()
568         self.m_sdbSizer1.AddButton(  self.button_2 )
569         self.m_sdbSizer1.AddButton(  self.button_1 )
570         self.m_sdbSizer1.Realize()
571         self.sizer_1.Add(self.m_sdbSizer1, 0, wx.EXPAND, 5)
572         self.SetSizer(self.sizer_1)
573         self.Layout()
574         self.sizer_1.Fit( self )
575
576     def addsaveimage(self, imageFile) :
577         self.imageFile = imageFile
578         
579     def OnCloseMe(self, event):
580         self.Destroy()
581
582     def OnSaveImage(self, event) :
583         dlg = wx.FileDialog(
584             self, message="Enregistrer sous...", defaultDir=os.getcwd(),
585             defaultFile= self.imagename, wildcard="png|*.png", style=wx.SAVE | wx.OVERWRITE_PROMPT
586             )
587         dlg.SetFilterIndex(2)
588         dlg.CenterOnParent()
589         if dlg.ShowModal() == wx.ID_OK:
590             path = dlg.GetPath()
591             copyfile(self.imageFile, path)
592             
593
594 def progressbar(self, maxi) :
595     if 'parent' in dir(self) :
596         parent = self.parent
597     else :
598         parent = self
599     return wx.ProgressDialog("Traitements",
600                              "Veuillez patienter...",
601                              maximum=maxi,
602                              parent=parent,
603                              style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE | wx.PD_ELAPSED_TIME | wx.PD_CAN_ABORT
604                              )
605
606
607 def treat_var_mod(variables) :
608     var_mod = {}
609     for variable in variables :
610         if u'_' in variable :
611             forme = variable.split(u'_')
612             var = forme[0]
613             mod = forme[1]
614             if not var in var_mod :
615                 var_mod[var] = [variable]
616             else :
617                 if not mod in var_mod[var] :
618                     var_mod[var].append(variable)
619     return var_mod