e399776740e3dbda03dcee4b44bc9edf947a0a8c
[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             if int(macversion[1]) < 5 :
523                 needX11 = True
524             else :
525                 needX11 = False
526         except :
527             needX11 = False
528
529     rpath = rpath.replace('\\','\\\\')
530     env = os.environ.copy()
531     if sys.platform == 'darwin' and 'LC_ALL' not in env:
532         env['LC_ALL'] = 'en_US.UTF-8'
533     if not graph :
534         if wait :
535             if sys.platform == 'win32':
536                 error = call(["%s" % rpath, "--vanilla","--slave","-f", "%s" % rcode])
537             else :
538                 error = call([rpath, '--slave', "--vanilla", "-f %s" % rcode, "--encoding=UTF-8"], env = env)
539             return error
540         else :
541             if sys.platform == 'win32':
542                 pid = Popen(["%s" % rpath, '--vanilla','--slave','-f', "%s" % rcode])
543             else :
544                 pid = Popen([rpath, '--slave', "--vanilla", "-f %s" % rcode, "--encoding=UTF-8"], stderr = PIPE, env = env)
545             return pid
546     else :
547         if wait :
548             if sys.platform == 'win32':
549                 error = call(["%s" % rpath, '--vanilla','--slave','-f', "%s" % rcode])
550             elif sys.platform == 'darwin' and needX11:
551                 os.environ['DISPLAY'] = ':0.0'
552                 error = call([rpath, '--vanilla','--slave',"-f %s" % rcode, "--encoding=UTF-8"], env = env)
553             else :
554                 error = call([rpath, '--vanilla','--slave',"-f %s" % rcode, "--encoding=UTF-8"], env = env)
555             return error
556         else :
557             if sys.platform == 'win32':
558                 pid = Popen(["%s" % rpath, '--vanilla','--slave','-f', "%s" % rcode])
559             elif sys.platform == 'darwin' and needX11:
560                 os.environ['DISPLAY'] = ':0.0'
561                 pid = Popen([rpath, '--vanilla','--slave',"-f %s" % rcode, "--encoding=UTF-8"], stderr = PIPE, env = env)
562             else :
563                 pid = Popen([rpath, '--vanilla','--slave',"-f %s" % rcode, "--encoding=UTF-8"], stderr = PIPE, env = env)
564             return pid
565
566 def check_Rresult(parent, pid) :
567     if isinstance(pid, Popen) :
568         if pid.returncode != 0 :
569             error = pid.communicate()
570             error = [str(error[0]), error[1]]
571             if error[1] is None :
572                 error[1] = 'None'
573             parent.Rerror = '\n'.join([str(pid.returncode), '\n'.join(error)])
574             try :
575                 raise Exception('\n'.join([u'Erreur R', '\n'.join(error[1:])]))
576             except :
577                 BugReport(parent)
578             return False
579         else :
580             return True
581     else :
582         if pid != 0 :
583             try :
584                 raise Exception(u'Erreur R')
585             except :
586                 BugReport(parent)
587             return False
588         else :
589             return True
590
591 def print_liste(filename,liste):
592     with open(filename,'w') as f :
593         for graph in liste :
594             f.write(';'.join(graph)+'\n')
595
596 def read_list_file(filename, encoding = sys.getdefaultencoding()):
597     with codecs.open(filename,'rU', encoding) as f :
598         content=f.readlines()
599         ncontent=[line.replace('\n','').split(';') for line in content if line.strip() != '']
600     return ncontent
601         
602 class MessageImage(wx.Frame):
603     def __init__(self, parent,title, size):
604         wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = title, pos = wx.DefaultPosition, size = size, style = wx.DEFAULT_FRAME_STYLE )
605         self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
606         self.imageFile = False
607         self.imagename = u"chi_classe.png"
608         self.HtmlPage = wx.html.HtmlWindow(self, -1)
609         self.HtmlPage.SetMinSize(size)
610         if "gtk2" in wx.PlatformInfo:
611             self.HtmlPage.SetStandardFonts()
612         self.HtmlPage.SetFonts('Courier', 'Courier')
613         
614         self.button_1 = wx.Button(self, wx.ID_CANCEL)
615         self.button_2 = wx.Button(self, wx.ID_SAVE)
616         self.Bind(wx.EVT_BUTTON, self.OnCloseMe, self.button_1)
617         self.Bind(wx.EVT_BUTTON, self.OnSaveImage, self.button_2)
618         self.do_layout()
619
620     def do_layout(self):
621         self.sizer_1 = wx.BoxSizer(wx.VERTICAL)
622         self.sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
623         self.sizer_1.Add(self.HtmlPage, 2, wx.EXPAND, 0)
624
625         self.m_sdbSizer1 = wx.StdDialogButtonSizer()
626         self.m_sdbSizer1.AddButton(  self.button_2 )
627         self.m_sdbSizer1.AddButton(  self.button_1 )
628         self.m_sdbSizer1.Realize()
629         self.sizer_1.Add(self.m_sdbSizer1, 0, wx.EXPAND, 5)
630         self.SetSizer(self.sizer_1)
631         self.Layout()
632         self.sizer_1.Fit( self )
633
634     def addsaveimage(self, imageFile) :
635         self.imageFile = imageFile
636         
637     def OnCloseMe(self, event):
638         self.Destroy()
639
640     def OnSaveImage(self, event) :
641         dlg = wx.FileDialog(
642             self, message="Enregistrer sous...", defaultDir=os.getcwd(),
643             defaultFile= self.imagename, wildcard="png|*.png", style=wx.SAVE | wx.OVERWRITE_PROMPT
644             )
645         dlg.SetFilterIndex(2)
646         dlg.CenterOnParent()
647         if dlg.ShowModal() == wx.ID_OK:
648             path = dlg.GetPath()
649             copyfile(self.imageFile, path)
650             
651
652 def progressbar(self, maxi) :
653     if 'parent' in dir(self) :
654         parent = self.parent
655     else :
656         parent = self
657     return wx.ProgressDialog("Traitements",
658                              "Veuillez patienter...",
659                              maximum=maxi,
660                              parent=parent,
661                              style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE | wx.PD_ELAPSED_TIME | wx.PD_CAN_ABORT
662                              )
663
664
665 def treat_var_mod(variables) :
666     var_mod = {}
667     for variable in variables :
668         if u'_' in variable :
669             forme = variable.split(u'_')
670             var = forme[0]
671             mod = forme[1]
672             if not var in var_mod :
673                 var_mod[var] = [variable]
674             else :
675                 if not mod in var_mod[var] :
676                     var_mod[var].append(variable)
677     return var_mod