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