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