...
authorPierre Ratinaud <ratinaud@univ-tlse2.fr>
Mon, 21 Jul 2014 09:48:32 +0000 (11:48 +0200)
committerPierre Ratinaud <ratinaud@univ-tlse2.fr>
Mon, 21 Jul 2014 09:48:32 +0000 (11:48 +0200)
PrintRScript.py
dialog.py
iramuteq.py
layout.py
listlex.py
tabfrequence.py
tableau.py
tree.py

index c30276f..fae8dcb 100644 (file)
@@ -580,17 +580,17 @@ def barplot(table, rownames, colnames, rgraph, tmpgraph, intxt = False) :
     txt += """
         source("%s")
         color = rainbow(nrow(di))
-        width <- 100 + (20*length(rownames(di))) + (100 * length(colnames(di)))
+        width <- 100 + (10*length(rownames(di))) + (100 * length(colnames(di)))
         height <- nrow(di) * 15
         if (height < 400) { height <- 400}
         open_file_graph("%s",width = width, height = height)
        par(mar=c(0,0,0,0))
            layout(matrix(c(1,2),1,2, byrow=TRUE),widths=c(3,lcm(7)))
-        par(mar=c(2,2,1,0))
+        par(mar=c(6,2,1,0))
         yp = ifelse(length(toinf), 0.2, 0)
         ym = ifelse(length(tominf), 0.2, 0)
         ymin <- ifelse(!length(which(di < 0)), 0, min(di) - ym)
-        coord <- barplot(as.matrix(di), beside = TRUE, col = color, space = c(0.1,0.6), ylim=c(ymin, max(di) + yp))
+        coord <- barplot(as.matrix(di), beside = TRUE, col = color, space = c(0.1,0.6), ylim=c(ymin, max(di) + yp), las = 2)
         if (length(toinf)) {
             coordinf <- coord[toinf]
             valinf <- di[toinf]
index 3805c46..cc1752e 100755 (executable)
--- a/dialog.py
+++ b/dialog.py
@@ -2559,9 +2559,9 @@ class ConcordList(wx.HtmlListBox):
     def OnGetItem(self, index):
         return self.concord[index] + '<br>'
 
-class message(wx.Dialog):
+class message(wx.Frame):
     def __init__(self, parent, items, title, size, save = True):
-        wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = title, pos = wx.DefaultPosition, size = size, style = wx.DEFAULT_DIALOG_STYLE|wx.STAY_ON_TOP )
+        wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = title, pos = wx.DefaultPosition, size = size, style = wx.DEFAULT_FRAME_STYLE )
         self.save = save    
         self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
 
@@ -2584,13 +2584,13 @@ class message(wx.Dialog):
 
     def __do_layout(self):
         sizer_2 = wx.BoxSizer(wx.VERTICAL)
-        sizer_2.Add(self.HtmlPage, 0, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        sizer_2.Add(self.HtmlPage, 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
         m_sdbSizer1 = wx.StdDialogButtonSizer()
         m_sdbSizer1.AddButton(  self.button_1 )
         if self.save :
             m_sdbSizer1.AddButton(  self.button_2 )
         m_sdbSizer1.Realize()
-        sizer_2.Add(m_sdbSizer1, 1, wx.EXPAND, 5)
+        sizer_2.Add(m_sdbSizer1, 0, wx.EXPAND, 5)
         self.SetSizer(sizer_2)
         self.Layout()
         sizer_2.Fit( self )
index 69e0bda..1b6a1e4 100644 (file)
@@ -240,7 +240,6 @@ class IraFrame(wx.Frame):
         view_menu.Append(ID_VIEWDATA, _(u"Show data").decode('utf8'))
         view_menu.Append(ID_RESULT, _(u'Show results').decode('utf8'))
         #view_menu.AppendSeparator()
-
         analyse_menu = wx.Menu()
         analyse_menu.Append(ID_Freq, _(u"Frequencies").decode('utf8'))
         analyse_menu.Append(ID_Chi2, _(u"Chi2").decode('utf8'))
@@ -251,10 +250,11 @@ class IraFrame(wx.Frame):
         analyse_menu.AppendMenu(-1, _(u"Clustering").decode('utf8'), menu_classif)
         #analyse_menu.Append(ID_AFCM, u"AFCM")
         analyse_menu.Append(ID_SIMI, _(u"Similarities Analysis").decode('utf8'))
-        analyse_menu.Append(ID_proto, _(u"Prototypical analysis").decode('utf8'))
+        analyse_menu.Append(ID_proto, _(u"Prototypical Analysis").decode('utf8'))
         ID_RCODE = wx.NewId()
-        analyse_menu.Append(ID_RCODE, u"Code R...")
-
+        analyse_menu.Append(ID_RCODE, u"Code R...") 
+        self.analyse_menu = analyse_menu
+        
         text_menu = wx.Menu()
         #text_menu.Append(ID_CHECKCORPUS, u"Vérifier le corpus")
         text_menu.Append(ID_TEXTSTAT, _(u"Statistics").decode('utf8'))
@@ -267,15 +267,16 @@ class IraFrame(wx.Frame):
         text_menu.Append(ID_SimiTxt, _(u"Similarities Analysis").decode('utf8')) 
         ID_WC = wx.NewId()
         text_menu.Append(ID_WC, _(u"WordCloud").decode('utf8'))
+        self.text_menu = text_menu
         
         help_menu = wx.Menu()
         help_menu.Append(wx.ID_ABOUT, _(u"About...").decode('utf8'))
-        help_menu.Append(wx.ID_HELP, _(u"Inline help...").decode('utf8'))
+        help_menu.Append(wx.ID_HELP, _(u"Online help...").decode('utf8'))
         
         self.mb.Append(file_menu, _(u"File").decode('utf8'))
         self.mb.Append(edit_menu, _(u"Edition").decode('utf8'))
         self.mb.Append(view_menu, _(u"View").decode('utf8'))
-        self.mb.Append(analyse_menu, _("Matrix analysis").decode('utf8'))
+        self.mb.Append(analyse_menu, _(u"Matrix analysis").decode('utf8'))
         self.mb.Append(text_menu, _(u"Text analysis").decode('utf8'))
         self.mb.Append(help_menu, _(u"Help").decode('utf8'))
         
@@ -544,39 +545,9 @@ vous devez signaler le chemin de l'éxecutable de R dans les préférences."""
         print 'onclose'
         with open(self.ConfigPath['path'], 'w') as f :
             self.PathPath.write(f)
-        if self.DictTab != {} :
-            savestates = [self.DictTab[item][0] for item in self.DictTab]
-            if False in savestates :
-                notsave = [item for item in self.DictTab if self.DictTab[item][0] == False] 
-                msg = u"""
- Certains résultats ne sont pas enregistrés.
- Voulez-vous fermer quand même ?"""
-                dlg = wx.MessageDialog(self, msg, "Sauvegarde",
-                       wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
-                dlg.CenterOnParent()
-                if dlg.ShowModal() in [wx.ID_NO, wx.ID_CANCEL]:
-                    event.Veto()
-                    dlg.Destroy()
-                else:
-                    dlg.Destroy()
-                    for item in notsave :
-                        for tmpfile in self.DictTab[item][1:] :
-                            os.remove(tmpfile)
-                            print 'remove : ' + tmpfile
-                    self._mgr.UnInit()
-                    del self._mgr
-                    self.Destroy()
-            else :
-                self._mgr.UnInit()
-                del self._mgr
-                self.Destroy()
-        else :
-            self._mgr.UnInit()
-            del self._mgr
-            self.Destroy()
-            #if sys.platform == 'win32' :
-            #    os.system("taskkill /im iramuteq.exe /f")
-            #    print 'meurtre de process'
+        self._mgr.UnInit()
+        del self._mgr
+        self.Destroy()
 
     def OnOpenData(self, event):
         inputname, self.input_path = OnOpen(self, "Data")
@@ -839,7 +810,7 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, États-Unis."""
 ################################################################
 #debut des analyses
 ################################################################
-    def analyse_matrix(self, evt, analyse, analyse_type = '', matrix = None, dlgnb = 1):
+    def analyse_matrix(self, analyse, analyse_type = '', matrix = None, dlgnb = 1):
         if matrix is None :
             matrix = self.tree.getmatrix()
         #try :
@@ -848,7 +819,7 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, États-Unis."""
         #    BugReport(self)           
 
     def OnFreq(self, event, matrix = None):
-        self.analyse_matrix(event, Frequences, analyse_type = 'freq', matrix = matrix, dlgnb = 3)
+        self.analyse_matrix(Frequences, analyse_type = 'freq', matrix = matrix, dlgnb = 3)
         #if matrix is None :
         #    matrix = self.tree.getmatrix()
         #try:
@@ -858,7 +829,7 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, États-Unis."""
 
     def OnChi2(self, event, matrix = None):
         #try:
-        self.analyse_matrix(event, ChiSquare, matrix = matrix, analyse_type = 'chi2', dlgnb = 3) 
+        self.analyse_matrix(ChiSquare, matrix = matrix, analyse_type = 'chi2', dlgnb = 3) 
         #except:
         #    BugReport(self)
 
@@ -1212,7 +1183,10 @@ class MySplashScreen(wx.SplashScreen):
             self.ShowMain()
 
     def ShowMain(self):
-        frame = IraFrame(None, -1, "IRaMuTeQ " + ConfigGlob.get('DEFAULT', 'version'), size=(1100, 800))
+        displaySize = wx.DisplaySize()
+        w = displaySize[0]/1.2
+        h = displaySize[1]/1.2
+        frame = IraFrame(None, -1, "IRaMuTeQ " + ConfigGlob.get('DEFAULT', 'version'), size=(w, h))
         frame.Show()
         frame.finish_init()
         frame.Upgrade()
index aa09840..eb1616e 100644 (file)
--- a/layout.py
+++ b/layout.py
@@ -8,6 +8,8 @@ import os
 import wx
 import wx.lib.hyperlink as hl
 import wx.lib.agw.aui as aui
+import wx.lib.agw.labelbook as LB
+from wx.lib.agw.fmresources import *
 from chemins import ConstructPathOut, ChdTxtPathOut, FFF, ffr, PathOut, StatTxtPathOut, simipath
 from ConfigParser import ConfigParser
 from functions import ReadProfileAsDico, GetTxtProfile, read_list_file, ReadList, exec_rcode, print_liste, BugReport, DoConf, indices_simi, check_Rresult, progressbar
@@ -375,6 +377,14 @@ class OpenCHDS():
         DictProfile = ReadProfileAsDico(Profile, Alceste, self.encoding)
         self.DictProfile = DictProfile
         self.cluster_size = []
+        clusternames = {}
+        for i in range(0, clnb) :
+            clusternames[i] = _(u'%i Cluster %i' % (i + 1, i + 1)).decode('utf8')
+        if os.path.exists(self.pathout['classes_names.txt']) :
+            with codecs.open(self.pathout['classes_names.txt'], 'r', self.parent.syscoding) as f :
+                clusternames_ = f.read()
+            clusternames_ =  dict([[i, ' '.join([`i + 1`, line])] for i, line in enumerate(clusternames_.splitlines())])
+            clusternames.update(clusternames_)
         #print 'lecture des antiprofils'
         #DictAnti = ReadProfileAsDico(self, AntiProfile, Alceste, self.encoding)
 
@@ -414,6 +424,7 @@ class OpenCHDS():
         #self.TabChdSim = wx.aui.AuiNotebook(self.parent.nb, -1, wx.DefaultPosition)
         notebook_flags =  aui.AUI_NB_DEFAULT_STYLE | aui.AUI_NB_TAB_EXTERNAL_MOVE | aui.AUI_NB_TAB_MOVE | aui.AUI_NB_TAB_FLOAT| wx.NO_BORDER
         panel.TabChdSim = aui.AuiNotebook(panel, -1, wx.DefaultPosition)
+        #panel.TabChdSim = LB.LabelBook(panel, -1, agwStyle = INB_TOP|INB_SHOW_ONLY_TEXT|INB_FIT_LABELTEXT)
         panel.TabChdSim.SetAGWWindowStyleFlag(notebook_flags)
         panel.TabChdSim.SetArtProvider(aui.ChromeTabArt())
         sizer1.Add(panel.TabChdSim,10, wx.EXPAND, 5)
@@ -439,7 +450,12 @@ class OpenCHDS():
             panel.TabChdSim.AddPage(CHD,'CHD')
                
         panel.ProfNB = aui.AuiNotebook(panel, -1, wx.DefaultPosition)
-        panel.ProfNB.SetArtProvider(aui.ChromeTabArt())
+        notebook_flags |= aui.AUI_NB_WINDOWLIST_BUTTON
+        panel.ProfNB.SetAGWWindowStyleFlag(notebook_flags)
+        #panel.ProfNB.SetArtProvider(aui.ChromeTabArt())
+        #panel.ProfNB = LB.LabelBook(panel, -1, agwStyle = INB_LEFT|INB_SHOW_ONLY_TEXT|INB_FIT_LABELTEXT)
+        #panel.ProfNB = wx.Listbook(self.parent, -1, style = wx.BK_DEFAULT)
+        #panel.ProfNB = wx.Treebook(self.parent, -1, style = wx.BK_DEFAULT)
         #self.ProfNB.SetTabCtrlHeight(100)
         #panel.AntiProfNB = aui.AuiNotebook(panel, -1, wx.DefaultPosition)
         if os.path.exists(DictPathOut['prof_seg']) :
@@ -450,15 +466,18 @@ class OpenCHDS():
             if isinstance(self.corpus, Corpus) :
                 DictProfile[str(i + 1)][1:] = [val[0:5] + [getlemgram(self.corpus, val)] + val[6:] for val in DictProfile[str(i + 1)][1:]]
             dlg.Update(3+i, 'Classe %i' %(i+1))
-            ind = '/'.join(DictProfile[str(i + 1)][0][0:2])
-            indpour = ' - '.join([ind, DictProfile[str(i + 1)][0][2]])
+            ind = '/'.join(DictProfile[str(i + 1)][0][0:2]).strip()
+            indpour = '\n'.join([ind, DictProfile[str(i + 1)][0][2]])
             self.tabprofile = ProfListctrlPanel(self.parent, self.panel, DictProfile[str(i + 1)], Alceste, i + 1)
             #self.tabantiprofile = ProfListctrlPanel(self.parent, self, DictAnti[str(i + 1)], Alceste, i + 1)
-            panel.ProfNB.AddPage(self.tabprofile, _(u"Cluster").decode('utf8') + ' %s %s(%s%%)' % (str(i + 1), sep, indpour))
+            panel.ProfNB.AddPage(self.tabprofile, clusternames[i] + '\n%s%%' % indpour, True)
+            panel.ProfNB.SetPageTextColour(i, '#890909')
+            panel.ProfNB.SetRenamable(i, True)
             #panel.AntiProfNB.AddPage(self.tabantiprofile, 'classe %s' % str(i + 1))
             if os.path.exists(DictPathOut['prof_seg']) :
                 self.tab_prof_seg = ProfListctrlPanel(self.parent, self, prof_seg[str(i + 1)], False, i + 1)
                 self.prof_seg_nb.AddPage(self.tab_prof_seg, _(u"Cluster").decode('utf8') + ' %i' % (i + 1))
+        panel.ProfNB.SetSelection(0)
 
         if clnb > 2 :
             self.TabAFC = aui.AuiNotebook(panel.TabChdSim, -1, wx.DefaultPosition)
index 7664614..5f22405 100644 (file)
@@ -313,7 +313,7 @@ class ListForSpec(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.ColumnSor
             last = self.GetNextSelected(last)
             data = self.GetItemData(last)
             datas += [data]
-        colnames = self.etoiles
+        colnames = self.first
         table = [[self.getinf(val) for val in line[1:]] for line in datas]
         rownames = [val[0] for val in datas]
         tmpgraph = tempfile.mktemp(dir=self.parent.TEMPDIR)
index 563e5b4..f8961bf 100644 (file)
@@ -102,16 +102,21 @@ class Frequences(AnalyseMatrix) :
             content = f.read().splitlines()
         content.pop(0)
         content.pop(0)
+        content = ['\t'.join(line.split('\t')[1:]).replace('"','') for line in content]
+        content = '\n'.join(content)
+        content = content.split(u'***\t****\t****')
+        content = [[line.split('\t') for line in tab.splitlines() if line.split('\t') != ['']] for tab in content]
+        listtab = [tab for tab in content if tab != []]
         texte = ''
-        for ligne in content:
-            ligne = ligne.replace('"', '')
-            ligne = ligne.split('\t')
-            if ligne[1] == u'***' :
-                if tab != []:
-                    listtab.append(tab)
-                tab = []
-            else :
-                tab.append(ligne)
+        #for ligne in content:
+        #    ligne = ligne.replace('"', '')
+        #    ligne = ligne.split('\t')
+        #    if ligne[1] == u'***' :
+        #        if tab != []:
+        #            listtab.append(tab)
+        #        tab = []
+        #    else :
+        #        tab.append(ligne)
         pretexte = u'''<html>
         <meta http-equiv="content-Type" content="text/html; charset=%s" />
         <body>\n<h1>Fréquences</h1>
@@ -128,7 +133,7 @@ class Frequences(AnalyseMatrix) :
                 texte += '<tr>'
                 texte += """
                 <td>%s</td><td align=center>%s</td><td align=center>%s %%</td>
-                """ % (line[3], line[1], line[2])
+                """ % (line[2], line[0], line[1])
                 texte += '</tr>'
             texte += '</table></td>'
             texte += """
index 6fa6abc..d2044d0 100644 (file)
@@ -71,7 +71,7 @@ class Tableau() :
     def __init__(self, parent, filename = '', filetype = 'csv', encodage = 'utf-8', parametres = None) :
         self.parent = parent
         if parametres is None :
-            self.parametres = DoConf(os.path.join(self.parent.UserConfigPath,'matrix.cfg')).getoptions('matrix')
+            self.parametres = DoConf(self.parent.ConfigPath['matrix']).getoptions('matrix')
             self.parametres['pathout'] = PathOut(filename, 'matrix').mkdirout()
             self.parametres['originalpath'] = filename
             self.parametres['filetype'] = filetype
@@ -169,7 +169,7 @@ class Tableau() :
         #datafile = xlrd.open_workbook(self.parametre['filename'], encoding_override="azerazerazer")
         datafile = xlrd.open_workbook(self.parametres['originalpath'])
         datatable = datafile.sheet_by_index(self.parametres['sheetnb']-1)
-        self.linecontent = [[str(datatable.cell_value(rowx = i, colx = j)).replace(u'"','').replace(u';','').replace(u'\n',' ').strip() for j in range(datatable.ncols)] for i in range(datatable.nrows)]
+        self.linecontent = [[str(datatable.cell_value(rowx = i, colx = j)).replace(u'"','').replace(u';',' ').replace(u'\n',' ').replace('\r', ' ').replace('\t', ' ').strip() for j in range(datatable.ncols)] for i in range(datatable.nrows)]
 
     def read_ods(self) :
         doc = ooolib.Calc(opendoc=self.parametres['originalpath'])
@@ -180,7 +180,7 @@ class Tableau() :
             for col in range(1, cols + 1):
                 data = doc.get_cell_value(col, row)
                 if data is not None :
-                    ligne.append(unescape(data[1].replace(u'"','').replace(u';','').replace(u'\n', ' ').strip()))
+                    ligne.append(unescape(data[1].replace(u'"','').replace(u';',' ').replace(u'\n', ' ').replace('\t', ' ').strip()))
                 else :
                     ligne.append('')
             self.linecontent.append(ligne)
@@ -189,7 +189,7 @@ class Tableau() :
         with codecs.open(self.parametres['originalpath'], 'r', self.parametres['encodage']) as f :
             content = f.read() 
         self.linecontent = [line.split(self.parametres['colsep']) for line in content.splitlines()]
-        self.linecontent = [[val.replace(u'"','').strip() for val in line] for line in self.linecontent]
+        self.linecontent = [[val.replace(u'"','').replace(u';',' ').replace('\t', ' ').strip() for val in line] for line in self.linecontent]
 
     def write_csvfile(self) :
         with open(self.parametres['csvfile'], 'w') as f :
@@ -222,6 +222,20 @@ class Tableau() :
             self.csvtable = [line.split('\t') for line in f.read().splitlines()]
         self.linecontent = [line[1:] for line in self.csvtable]
         self.linecontent.pop(0)
+        
+    def extractfrommod(self, col, val):
+        return ([''] + self.colnames) + [line for line in self.csvtable[1:] if line[col + 1] == val]
+
+    def splitfromvar(self, col, var):
+        newtabs = {}
+        for line in self.csvtable[1:] :
+            mod = line[col]
+            if mod in newtabs :
+                newtabs[mod].append(line)
+            else :
+                newtabs[mod] = [line]
+        return ([''] + self.colnames) + newtab
+
 
     def check_rownames(self) :
         if len(self.rownames) == len(list(set(self.rownames))) :
@@ -245,6 +259,9 @@ class Tableau() :
         dc = dict(zip(listcol, listcol))
         selcol = [[val for i, val in enumerate(row) if i in dc] for row in self.linecontent]
         return selcol
+    
+    def countmultiple(self, liscol):
+        return self.make_dico(self.select_col(liscol))
 
     def getactlistfromselection(self, listact) :
         selcol = self.select_col(listact)
diff --git a/tree.py b/tree.py
index 76808aa..80515fa 100644 (file)
--- a/tree.py
+++ b/tree.py
@@ -407,18 +407,25 @@ class LeftTree(CT.CustomTreeCtrl):
                 self.Bind(wx.EVT_MENU, self.OnSubTextFromMeta, subcorpusfrommeta)
                 self.Bind(wx.EVT_MENU, self.OnSubTextFromTheme, subcorpusfromtheme)
             elif 'matrix_name' in pydata :
-                freq = menu.Append(wx.ID_ANY, _(u"Frequency").decode('utf8'))
-                chi2 = menu.Append(wx.ID_ANY, _(u"Chi square").decode('utf8'))
-                chdreinert = menu.Append(wx.ID_ANY, _(u"Reinert clustering").decode('utf8'))
-                simi = menu.Append(wx.ID_ANY, _(u"Similarity analysis").decode('utf8'))
+                for i in range(self.parent.matrix_menu.GetMenuItemCount()) :
+                    item = self.parent.matrix_menu.FindItemByPosition(i)
+                    itemid = item.GetId()
+                    itemtext = item.GetText()
+                    menu.Append(itemid, itemtext)
+                    #print item, itemid, itemtext
+                #menu = self.parent.matrix_menu
+                #freq = menu.Append(wx.ID_ANY, _(u"Frequency").decode('utf8'))
+                #chi2 = menu.Append(wx.ID_ANY, _(u"Chi square").decode('utf8'))
+                #chdreinert = menu.Append(wx.ID_ANY, _(u"Reinert clustering").decode('utf8'))
+                #simi = menu.Append(wx.ID_ANY, _(u"Similarity analysis").decode('utf8'))
                 menu.AppendSeparator()
-                self.Bind(wx.EVT_MENU, self.OnFreq, freq)
-                self.Bind(wx.EVT_MENU, self.OnChiSquare, chi2)
-                self.Bind(wx.EVT_MENU, self.OnSimiTab, simi)
-                self.Bind(wx.EVT_MENU, self.OnCHDReinert, chdreinert)
+                #self.Bind(wx.EVT_MENU, self.OnFreq, freq)
+                #self.Bind(wx.EVT_MENU, self.OnChiSquare, chi2)
+                #self.Bind(wx.EVT_MENU, self.OnSimiTab, simi)
+                #self.Bind(wx.EVT_MENU, self.OnCHDReinert, chdreinert)
             elif pydata.get('type', False) == 'alceste' and pydata['uuid'] in self.parent.history.opened :
                 openmenu = wx.Menu()
-                antipro = openmenu.Append(wx.ID_ANY, _(u"antiprofiles").decode('utf8'))
+                antipro = openmenu.Append(wx.ID_ANY, _(u"Antiprofiles").decode('utf8'))
                 menu.AppendMenu(wx.ID_ANY, _(u"Open ...").decode('utf8'), openmenu)
     
                 profsr = menu.Append(wx.ID_ANY, _(u"Repeated segments profiles").decode('utf8'))
@@ -428,7 +435,7 @@ class LeftTree(CT.CustomTreeCtrl):
                 navig = menu.Append(wx.ID_ANY, _(u"Navigator").decode('utf8'))
                 statclasse = menu.Append(wx.ID_ANY, _(u"Clusters statistics").decode('utf8'))
                 rapport = menu.Append(wx.ID_ANY, _(u"Report").decode('utf8'))
-                export_classes = menu.Append(wx.ID_ANY, _(u"Exports Clusters").decode('utf8'))
+                export_classes = menu.Append(wx.ID_ANY, _(u"Export Clusters").decode('utf8'))
                 menu.AppendSeparator()
                 self.Bind(wx.EVT_MENU, self.OpenAntipro, antipro)
                 self.Bind(wx.EVT_MENU, self.OnProfSR, profsr)