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