json for Matrix + fix for windows
[iramuteq] / elcategorizator.py
1 # -*- coding: utf-8 -*-
2 #Author: Pierre Ratinaud
3 #Copyright (c) 2022 Pierre Ratinaud
4 #License: GNU/GPL
5
6 import os
7 import wx
8 import wx.xrc
9 #from wx.lib.splitter import MultiSplitterWindow
10 from listlex import *
11 import pickle
12 import json
13
14
15
16 class CategoDict :
17     def __init__(self, pathout = None):
18         self.pathout = pathout
19         self.cate = self.readjson()
20         self.lenwords = len(self.cate['TOCATE']) + len([word for categorie in self.cate['CATE'] for word in self.cate['CATE'][categorie][1]])
21
22     def readjson(self):
23         if self.pathout is not None :
24             with open(self.pathout['cate.json'], 'r', encoding='utf8') as f :
25                 cate = json.load(f)
26         else :
27             cate = {'TOCATE' : {'word1': 3, 'word2' : 2, 'word3' : 5}, 'CATE': {'cat1' : [34,{'word6':30, 'word7':4}], 'cat2' : [20,{'word20':20}]}}
28         return cate
29
30     def save(self) :
31         with open(self.pathout['cate.json'], 'w', encoding='utf8') as f :
32             f.write(json.dumps(self.cate, indent=4))
33         print("json saved!")
34
35     def exportdict(self):
36         pass
37
38     def getcate(self) :
39         cate = []
40         i = 0
41         for val in self.cate['CATE'] :
42             cate.append([i, [val, self.cate['CATE'][val][0]]])
43             i += 1
44         return dict(cate)
45
46     def getwordstocate(self) :
47         words = []
48         i = 0
49         for val in self.cate['TOCATE'] :
50             words.append([i, [val, self.cate['TOCATE'][val]]])
51             i+= 1
52         return dict(words)
53
54     def getcatewords(self, cat) :
55         catewords = []
56         i = 0
57         if cat not in self.cate['CATE'] :
58             return {}
59         for val in self.cate['CATE'][cat][1] :
60             catewords.append([i, [val, self.cate['CATE'][cat][1][val]]])
61             i += 1
62         return dict(catewords)
63
64     def getwordscate(self) :
65         wc = {}
66         for word in self.cate['TOCATE'] :
67             wc[word] = word
68         for categorie in self.cate['CATE'] :
69             for word in self.cate['CATE'][categorie][1] :
70                 wc[word] = categorie
71         return wc
72
73     def addwordincate(self, categorie, word, eff) :
74         self.cate['CATE'][categorie][1][word] = eff
75         self.cate['CATE'][categorie][0] += eff
76         del(self.cate['TOCATE'][word])
77
78     def addwordinwords(self, categorie, word, eff) :
79         print(categorie, word, eff)
80         self.cate['TOCATE'][word] = eff
81         self.cate['CATE'][categorie][0] -= eff
82         del(self.cate['CATE'][categorie][1][word])
83         if self.cate['CATE'][categorie][0] == 0 :
84             del(self.cate['CATE'][categorie])
85
86     def findcatefromword(self, word) :
87         for categorie in self.cate['CATE'] :
88             if word in self.cate['CATE'][categorie][1] :
89                 return categorie
90         return None
91
92     def changewordcate(self, newcate, word, eff) :
93         oldcat = self.findcatefromword(word)
94         del(self.cate['CATE'][oldcat][1][word])
95         self.cate['CATE'][oldcat][0] -= eff
96         self.cate['CATE'][newcate][1][word] = eff
97         self.cate['CATE'][newcate][0] += eff
98         if self.cate['CATE'][oldcat][0] == 0 :
99             del(self.cate['CATE'][oldcat])
100
101     def addcatefromwordtocate(self, word, eff) :
102         if word in self.cate['CATE'] :
103             return False
104         else :
105             self.cate['CATE'][word]=[eff,{word:eff}]
106             del(self.cate['TOCATE'][word])
107             return True
108
109     def addcatefromscratch(self) :
110         i = 0
111         while "NewCategory_%i" %i in self.cate['CATE'] :
112             i += 1
113         newcate = "NewCategory_%i" %i
114         self.cate['CATE'][newcate] = [0, {}]
115
116     def addcatefromwordcate(self, word, eff) :
117         if word in self.cate['CATE'] :
118             return False
119         else :
120             oldcat = self.findcatefromword(word)
121             self.cate['CATE'][word]=[eff,{word:eff}]
122             del(self.cate['CATE'][oldcat][1][word])
123             self.cate['CATE'][oldcat][0] -= eff
124             if self.cate['CATE'][oldcat][0] == 0 :
125                 del(self.cate['CATE'][oldcat])
126             return True
127
128     def delcate(self, categorie) :
129         for word in self.cate['CATE'][categorie][1] :
130             self.cate['TOCATE'][word] = self.cate['CATE'][categorie][1][word]
131         del(self.cate['CATE'][categorie])
132
133     def loadcate(self, infile) :
134         if self.cate['CATE'] != {} :
135             print("Categories should be empty")
136             return False
137         with open(infile, 'r', encoding='utf8') as f :
138             newcate = json.load(f)
139         for categorie in newcate['CATE'] :
140             self.cate['CATE'][categorie] = [0,{}]
141             for word in newcate['CATE'][categorie][1] :
142                 if word in self.cate['TOCATE'] :
143                     self.cate['CATE'][categorie][1][word] = self.cate['TOCATE'][word]
144                     self.cate['CATE'][categorie][0] += self.cate['TOCATE'][word]
145                     del(self.cate['TOCATE'][word])
146
147     def makestat(self) :
148         totocat = sum([self.cate['TOCATE'][word] for word in self.cate['TOCATE']])
149         nbtocat = len(self.cate['TOCATE'])
150         nbcate = len(self.cate['CATE'])
151         totcate = sum([self.cate['CATE'][categorie][0] for categorie in self.cate['CATE']])
152         lenwordincate = len([word for categorie in self.cate['CATE'] for word in self.cate['CATE'][categorie][1]])
153         return nbtocat, totocat, nbcate, totcate, lenwordincate
154
155
156 #cate = CategoDict()
157
158
159 class ElCategorizator ( wx.Panel ):
160
161     def __init__( self, parent, pathout, tableau, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.TAB_TRAVERSAL, name = wx.EmptyString ):
162         wx.Panel.__init__ ( self, parent, id = id, pos = pos, size = size, style = style, name = name )
163         self.pathout = pathout
164         self.parent = parent
165         self.tableau = tableau
166
167         self.cate = CategoDict(self.pathout)
168         gsizer =  wx.BoxSizer( wx.VERTICAL )
169
170         bSizer1 = wx.BoxSizer( wx.HORIZONTAL )
171
172         self.m_listToCate = ListForWords(self, dlist = self.cate, first = ['eff'])
173         bSizer1.Add( self.m_listToCate, 2, wx.ALL|wx.EXPAND, 5 )
174
175         self.m_listCate = ListForCate(self, dlist = self.cate, first = ['eff'])
176         bSizer1.Add( self.m_listCate, 1, wx.ALL|wx.EXPAND, 5 )
177
178         self.m_listCateWords = ListForCateWords(self, dlist = self.cate, first = ['eff'])
179         bSizer1.Add( self.m_listCateWords, 1, wx.ALL|wx.EXPAND, 5 )
180
181         bSizer2 = wx.BoxSizer( wx.HORIZONTAL )
182
183         self.butsave = wx.Button( self, wx.ID_SAVE, u"Save", wx.DefaultPosition, wx.DefaultSize, 0 )
184         bSizer2.Add( self.butsave, 0, wx.ALL, 5 )
185
186         self.butcsv = wx.Button( self, wx.ID_ANY, u"Export Columns", wx.DefaultPosition, wx.DefaultSize, 0 )
187         bSizer2.Add( self.butcsv, 0, wx.ALL, 5 )
188
189         self.butdict = wx.Button( self, wx.ID_ANY, u"Export dictonary", wx.DefaultPosition, wx.DefaultSize, 0 )
190         bSizer2.Add( self.butdict, 0, wx.ALL, 5 )
191
192         self.butload = wx.Button( self, wx.ID_ANY, u"Load a categorization", wx.DefaultPosition, wx.DefaultSize, 0 )
193         bSizer2.Add( self.butload, 0, wx.ALL, 5 )
194
195         self.butaddcate = wx.Button( self, wx.ID_ANY, u"Add a category", wx.DefaultPosition, wx.DefaultSize, 0 )
196         bSizer2.Add( self.butaddcate, 0, wx.ALL, 5 )
197
198
199         bSizer3 = wx.BoxSizer( wx.HORIZONTAL )
200
201         self.nbword = """Words : {:d} ({:d}) | """
202
203         self.stat = """ Words to categorize : {:d} ({}%) - {:d} ({}%) -- Categories : {:d} - {:d} ({}%) - {:d} ({}%)"""
204 #        nbtocat, totocat, nbcate, totcate = self.cate.makestat()
205 #        lenwords = self.cate.lenwords
206 #        totwords = totocat + totcate
207 #        prtocat = repr(nbtocat/lenwords)
208 #        prtotocat = repr(totocat/totwords)
209 #        prcate = repr(totcate/totwords)
210         self.wordtxt = wx.StaticText(self, -1, "")
211         bSizer3.Add( self.wordtxt, 0, wx.ALL, 5 )
212         self.stattxt = wx.StaticText(self, -1, "")
213         bSizer3.Add( self.stattxt, 0, wx.ALL, 5 )
214
215
216         gsizer.Add( bSizer2, 0, wx.EXPAND, 5 )
217         gsizer.Add( bSizer1, 2, wx.EXPAND, 5 )
218         gsizer.Add( bSizer3, 0, wx.EXPAND, 5 )
219
220         self.butsave.Bind(wx.EVT_BUTTON, self.OnSave)
221         self.butcsv.Bind(wx.EVT_BUTTON, self.OnCSV)
222         self.butdict.Bind(wx.EVT_BUTTON, self.OnDict)
223         self.butsave.SetBackgroundColour((14, 242, 14, 255))
224         self.butload.Bind(wx.EVT_BUTTON, self.OnLoad)
225         self.butaddcate.Bind(wx.EVT_BUTTON, self.OnAddCate)
226         self.OnStat()
227         self.SetSizer( gsizer )
228         self.Layout()
229
230     def __del__( self ):
231         pass
232
233
234     def OnLoad(self, event) :
235         if len(self.cate.cate['CATE']) != 0 :
236             message = wx.MessageDialog(self, _("Categories must be empty to load a categorization."), _("Information"), wx.OK|wx.ICON_WARNING)
237             message.ShowModal()
238             message.Destroy()
239             return
240         wildcard = "json|*.json|" \
241                    "All file|*.*"
242         dlg = wx.FileDialog(
243              self, message="Choose a file",
244              defaultDir=self.pathout.dirout,
245              defaultFile="",
246              wildcard=wildcard,
247              style=wx.FD_OPEN |
248                    wx.FD_CHANGE_DIR | wx.FD_FILE_MUST_EXIST |
249                    wx.FD_PREVIEW
250              )
251
252         if dlg.ShowModal() == wx.ID_OK:
253             paths = dlg.GetPaths()
254             path = paths[0]
255             self.cate.loadcate(path)
256             self.m_listCate.RefreshData(self.cate.getcate())
257             self.m_listToCate.RefreshData(self.cate.getwordstocate())
258         dlg.Destroy()
259
260     def OnSave(self, event) :
261         self.cate.save()
262         self.butsave.SetBackgroundColour((14, 242, 14, 255))
263
264     def OnCSV(self, event) :
265         wordscate = self.cate.getwordscate()
266         newtab = [['category%i' % i for i in range(1, len(self.tableau.selected_col)+1)]]
267         for line in self.tableau.select_col(self.tableau.selected_col):
268             newline = []
269             for word in line :
270                 newline.append(wordscate.get(word,word))
271             newtab.append(newline)
272         with open(self.pathout['tableout.csv'], 'w', encoding='utf8') as f :
273             f.write('\n'.join(['\t'.join(line) for line in newtab]))
274         message = wx.MessageDialog(self, _("Export successful\n%s" % self.pathout['tableout.csv']), _("Information"), wx.OK|wx.ICON_INFORMATION)
275         message.ShowModal()
276         message.Destroy()
277
278     def OnDict(self, event):
279         with open(self.pathout['dictionnary.txt'], 'w', encoding='utf8') as f :
280             for categorie in self.cate.cate['CATE'] :
281                 f.write(categorie + ': \t' + repr(self.cate.cate['CATE'][categorie][0]) + '\n')
282                 for word in self.cate.cate['CATE'][categorie][1] :
283                     f.write('\t' + word + ': \t' + repr(self.cate.cate['CATE'][categorie][1][word]) + '\n')
284             for word in self.cate.cate['TOCATE'] :
285                 f.write(word + ':\t' + repr(self.cate.cate['TOCATE'][word]) + '\n')
286         message = wx.MessageDialog(self, _("Export successful\n%s" % self.pathout['dictionnary.txt']), _("Information"), wx.OK|wx.ICON_INFORMATION)
287         message.ShowModal()
288         message.Destroy()
289
290     def OnStat(self) :
291         nbtocat, totocat, nbcate, totcate, lenwordincate = self.cate.makestat()
292         totwords = totocat + totcate
293         prtocat = repr(round((nbtocat/self.cate.lenwords) * 100 ,2))
294         prtotocat = repr(round((totocat/totwords) * 100, 2))
295         prcate = repr(round((totcate/totwords)*100, 2))
296         prwordincate = repr(round((lenwordincate/self.cate.lenwords)*100, 2))
297         self.stattxt.SetLabel(self.stat.format(nbtocat, prtocat, totocat, prtotocat, nbcate, lenwordincate, prwordincate, totcate, prcate))
298
299     def OnAddToTable(self) :
300         wordscate = self.cate.getwordscate()
301         newtab = [['category%i' % i for i in range(1, len(self.tableau.selected_col)+1)]]
302         for line in self.tableau.select_col(self.tableau.selected_col):
303             newline = []
304             for word in line :
305                 newline.append(wordscate.get(word,word))
306             newtab.append(newline)
307
308     def OnAddCate(self, evt) :
309         print('add a category')
310         print(self.m_listCate.GetItemCount())
311         self.cate.addcatefromscratch()
312         self.m_listCate.dlist = self.cate.getcate()
313         self.m_listCate.RefreshData(self.m_listCate.dlist)
314         self.m_listCate.SetSelection(self.m_listCate.GetItemCount() - 1)
315
316
317
318 #class ListPanel(wx.Panel) :
319 #     def __init__(self, parent, gparent, List):
320 #        wx.Panel.__init__(self, parent, style=wx.BORDER_SUNKEN)
321 #        self.parent = parent
322 #        self.cate = gparent.cate
323 #        self.list = List(self, dlist = gparent.cate, first = ['eff'])
324
325
326 #class ElCategorizator ( wx.Panel ):
327 #
328 #    def __init__( self, parent, pathout, tableau, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.TAB_TRAVERSAL, name = wx.EmptyString ):
329 #        wx.Panel.__init__ ( self, parent, id = id, pos = pos, size = size, style = style, name = name )
330 #        self.pathout = pathout
331 #        self.parent = parent
332 #        self.tableau = tableau
333 #
334 #        self.cate = CategoDict(self.pathout)
335 #
336 #        splitter = MultiSplitterWindow(self, style=wx.SP_LIVE_UPDATE)
337 #        self.splitter = splitter
338 #        sizer = wx.BoxSizer(wx.HORIZONTAL)
339 #        sizer.Add(splitter, 1, wx.EXPAND)
340 #        self.SetSizer(sizer)
341 #
342 #        panelwords = ListPanel(splitter, self, ListForWords)
343 #        splitter.AppendWindow(panelwords, 150)
344 #        panelcate = ListPanel(splitter, self, ListForCate)
345 #        splitter.AppendWindow(panelcate, 150)
346 #        panelwordscate = ListPanel(splitter, self, ListForCateWords)
347 #        splitter.AppendWindow(panelwordscate, 150)
348 #        self.m_listToCate = panelwords.list
349 #        self.m_listCate = panelcate.list
350 #        self.m_listCateWords = panelwordscate.list
351 #
352 #    def __del__( self ):
353 #        pass
354 #
355 #    def OnSave(self, event) :
356 #        self.cate.save()
357 #        self.butsave.SetBackgroundColour((14, 242, 14, 255))
358 #
359 #    def OnCSV(self, event) :
360 #        wordscate = self.cate.getwordscate()
361 #        newtab = [['category%i' % i for i in range(1, len(self.tableau.selected_col)+1)]]
362 #        for line in self.tableau.select_col(self.tableau.selected_col):
363 #            newline = []
364 #            for word in line :
365 #                newline.append(wordscate.get(word,word))
366 #            newtab.append(newline)
367 #        with open(self.pathout['tableout.csv'], 'w') as f :
368 #            f.write('\n'.join(['\t'.join(line) for line in newtab]))
369 #        print("csv exported !")
370 #
371 #    def OnDict(self, event):
372 #        with open(self.pathout['dictionnary.txt'], 'w') as f :
373 #            for categorie in self.cate.cate['CATE'] :
374 #                f.write(categorie + ': \t' + repr(self.cate.cate['CATE'][categorie][0]) + '\n')
375 #                for word in self.cate.cate['CATE'][categorie][1] :
376 #                    f.write('\t' + word + ': \t' + repr(self.cate.cate['CATE'][categorie][1][word]) + '\n')
377 #            for word in self.cate.cate['TOCATE'] :
378 #                f.write(word + ':\t' + repr(self.cate.cate['TOCATE'][word]) + '\n')
379 #        print("dictionnary exported !")
380
381
382 class ListForCate(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.ColumnSorterMixin):
383
384     def __init__(self, parent, dlist = {}, first = [], usefirst = False, menu = True):
385         wx.ListCtrl.__init__( self, parent, -1, style=wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_HRULES|wx.LC_VRULES|wx.LC_EDIT_LABELS|wx.LC_SINGLE_SEL)
386         self.parent=parent
387         self.cate = self.parent.cate
388         self.dlist= self.cate.getcate()
389         self.first = first
390         self.il = wx.ImageList(20, 20)
391         a={"sm_up":"GO_UP","sm_dn":"GO_DOWN","w_idx":"WARNING","e_idx":"ERROR","i_idx":"QUESTION", "p_idx":"PLUS"}
392         for k,v in list(a.items()):
393             s="self.%s= self.il.Add(wx.ArtProvider.GetBitmap(wx.ART_%s,wx.ART_TOOLBAR,(20,20)))" % (k,v)
394             exec(s)
395         self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
396         self.attr1 = wx.ListItemAttr()
397         self.attr1.SetBackgroundColour((230, 230, 230))
398         self.attr2 = wx.ListItemAttr()
399         self.attr2.SetBackgroundColour("light blue")
400         #self.attrselected = wx.ListItemAttr()
401         #self.attrselected.SetBackgroundColour("red")
402         self.SetListFont()
403         self.selected = {}
404         i = 0
405         for name in ['Categories'] + self.first :
406             self.InsertColumn(i,name,wx.LIST_FORMAT_LEFT)
407             i += 1
408         self.itemDataMap = self.dlist
409         self.itemIndexMap = list(self.dlist.keys())
410         self.SetItemCount(len(self.dlist))
411         listmix.ListCtrlAutoWidthMixin.__init__(self)
412         listmix.ColumnSorterMixin.__init__(self, len(self.first) + 1)
413
414         #self.SortListItems(1, False)
415         self.SetColumnWidth(0, 300)
416         self.SetColumnWidth(1, wx.LIST_AUTOSIZE)
417
418         self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.StartDrag)
419         self.Bind(wx.EVT_LIST_ITEM_FOCUSED, self.ShowWords)
420         self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnBeginEdit)
421         self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEndEdit)
422         self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
423         self.Bind(wx.EVT_LIST_COL_CLICK, self.OnSortColumn)
424
425         if self.GetItemCount() != 0 :
426             #self.SetItemState(0, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
427             self.Select(0, on=1)
428
429         dt = MyListDropCate(self)
430         self.SetDropTarget(dt)
431
432     def OnSortColumn(self, evt) :
433         print(self.currentItem)
434         evt.Skip()
435
436     def SetListFont(self) :
437         self.SetFont(wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))
438
439     def OnGetItemImage(self, item):
440         return self.p_idx
441
442     def OnBeginEdit(self, event) :
443         self.EditLabel(event.GetIndex())
444         event.Skip()
445
446     def OnEndEdit(self, event) :
447         newlabel = event.GetLabel()
448         idx = event.GetIndex()
449         oldlabel = self.GetItemText(idx)
450         if newlabel not in self.cate.cate['CATE'] :
451             self.cate.cate['CATE'][newlabel] = self.cate.cate['CATE'][oldlabel]
452             del(self.cate.cate['CATE'][oldlabel])
453         self.RefreshData(self.cate.getcate())
454
455     def ShowWords(self, event) :
456         index = event.GetIndex()
457         try :
458             data = self.cate.getcatewords(self.GetItemText(index))
459             self.parent.m_listCateWords.RefreshData(data)
460             self.parent.m_listCateWords.SetSelection(0)
461         except :
462             pass
463         event.Skip()
464
465     def RefreshData(self, data):
466         try :
467             item = self.currentItem
468         except :
469             item = 0
470         self.itemDataMap = data
471         self.itemIndexMap = list(data.keys())
472         self.SetItemCount(len(data))
473         order = self._colSortFlag[self._col]
474         self.SortListItems(self._col, order)
475         #self.SetColumnWidth(0, wx.LIST_AUTOSIZE)
476         #self.SetColumnWidth(1, wx.LIST_AUTOSIZE)
477         #self.SetColumnWidth(0,300)
478         self.parent.OnStat()
479         self.Refresh()
480         try :
481             self.SetSelection(item)
482             self.Focus(item)
483         except :
484             pass
485
486     def GetListCtrl(self):
487         return self
488
489     def GetSortImages(self):
490         return (self.sm_dn, self.sm_up)
491
492     def SortItems(self, sorter=None):
493         try :
494             select = self.currentItem
495             word = self.GetItemData(select)[0]
496         except Exception as e: print('word',e)
497
498         listTemp = sorted(self.itemDataMap.items(),
499             key=lambda x:x[1][self._col], reverse= (self._colSortFlag[self._col]!=True))
500         dlist = dict([[line[0],line[1]] for line in listTemp])
501         self.itemDataMap = dlist
502         self.itemIndexMap = list(dlist.keys())
503         self.Refresh() # redraw the list
504         try :
505             formes = [self.getColumnText(i, 0) for i in range(self.GetItemCount())]
506             idx = [i for i, val in enumerate(formes) if val == word][0]
507             self.SetSelection(idx)
508             self.Focus(idx)
509         except Exception as e: print(e)
510
511     def OnGetItemText(self, item, col):
512         index=self.itemIndexMap[item]
513         s = self.itemDataMap[index][col]
514         if isinstance(s, (int, float)):
515             return str(s)
516         else:
517             return s #modification pour python 3
518
519     def OnGetItemAttr(self, item):
520 #        if self.IsSelected(index) == True :
521 #            print('selected', index)
522         index=self.itemIndexMap[item]
523         if item % 2 :
524            return self.attr1
525         else :
526            return self.attr2
527
528     def getselectedwords(self) :
529         words = [self.getColumnText(self.GetFirstSelected(), 0)]
530         last = self.GetFirstSelected()
531         while self.GetNextSelected(last) != -1:
532             last = self.GetNextSelected(last)
533             words.append(self.getColumnText(last, 0))
534         return words
535
536     def GetString(self):
537         return self.getselectedwords()[0]
538
539     def GetSelections(self):
540         return self.getselectedwords()
541
542     def getColumnText(self, index, col):
543         item = self.GetItem(index, col)
544         return item.GetText()
545
546     def GetItemData(self, item) :
547         index=self.itemIndexMap[item]
548         s = self.itemDataMap[index]
549         return s
550
551     def OnItemSelected(self, event):
552         self.currentItem = event.GetIndex() #event.m_itemIndex
553         event.Skip()
554
555     def SetSelection(self, index) :
556         for i in range(0, self.GetItemCount(), 1) :
557             self.Select(i, on=0)
558         self.Select(index, on=1)
559
560     def GetItemInfo(self, idx):
561         """
562         Collect all relevant data of a listitem, and put it in a list.
563         """
564
565         l = []
566         l.append(idx) # We need the original index, so it is easier to eventualy delete it.
567         l.append(self.GetItemData(idx)) # Itemdata.
568         l.append(self.GetItemText(idx)) # Text first column.
569         for i in range(1, self.GetColumnCount()): # Possible extra columns.
570             l.append(self.GetItem(idx, i).GetText())
571         l.append('cate')
572         return l
573
574
575     def StartDrag(self, event):
576         """
577         Put together a data object for drag-and-drop _from_ this list.
578         """
579
580         l = []
581         idx = -1
582         while True: # Find all the selected items and put them in a list.
583             idx = self.GetNextItem(idx, wx.LIST_NEXT_ALL, wx.LIST_STATE_SELECTED)
584             if idx == -1:
585                 break
586             l.append(self.GetItemInfo(idx))
587
588         # Pickle the items list.
589         itemdata = pickle.dumps(l, 1)
590         # Create our own data format and use it
591         # in a Custom data object.
592         ldata = wx.CustomDataObject("ListCtrlItems")
593         ldata.SetData(itemdata)
594         # Now make a data object for the  item list.
595         data = wx.DataObjectComposite()
596         data.Add(ldata)
597
598         # Create drop source and begin drag-and-drop.
599         dropSource = wx.DropSource(self)
600         dropSource.SetData(data)
601         res = dropSource.DoDragDrop(flags=wx.Drag_DefaultMove)
602
603         # If move, we want to remove the item from this list.
604         if res == wx.DragMove and l[0][-1] != 'cate' :
605             # It's possible we are dragging/dropping from this list to this list.
606             # In which case, the index we are removing may have changed...
607
608             # Find correct position.
609             l.reverse() # Delete all the items, starting with the last item.
610             for i in l:
611                 pos = self.FindItem(i[0], i[2])
612                 self.DeleteItem(pos)
613
614
615     def Insert(self, x, y, seq):
616         """
617         Insert text at given x, y coordinates --- used with drag-and-drop.
618         """
619
620         # Find insertion point.
621         index, flags = self.HitTest((x, y))
622
623         if index == wx.NOT_FOUND: # Not clicked on an item.
624             if flags & (wx.LIST_HITTEST_NOWHERE|wx.LIST_HITTEST_ABOVE|wx.LIST_HITTEST_BELOW): # Empty list or below last item.
625                 index = self.GetItemCount() # Append to end of list.
626             elif self.GetItemCount() > 0:
627                 if y <= self.GetItemRect(0).y: # Clicked just above first item.
628                     index = -1 # Append to top of list.
629                 else:
630                     index = self.GetItemCount() + 1 # Append to end of list.
631         else: # Clicked on an item.
632             # Get bounding rectangle for the item the user is dropping over.
633             rect = self.GetItemRect(index)
634
635             # If the user is dropping into the lower half of the rect,
636             # we want to insert _after_ this item.
637             # Correct for the fact that there may be a heading involved.
638             #if y > rect.y - self.GetItemRect(0).y + rect.height/2:
639             #    index += 1
640         print('Insert de ListForCate', index, flags)
641         word, eff = seq[0][1]
642         if seq[0][-1] == 'words' :
643             if index < self.GetItemCount() and index != -1 :
644                 for val in seq :
645                     word, eff = val[1]
646                     self.cate.addwordincate(self.GetItemData(index)[0], word, eff)
647             else :
648                 index = self.GetItemCount()
649                 if self.cate.addcatefromwordtocate(word, eff) :
650                     pass
651                 else :
652                     dial = wx.MessageDialog(self, "This category name is already used", style=wx.OK|wx.CENTRE).ShowModal()
653                     dial.Destroy()
654             self.dlist = self.cate.getcate()
655             self.RefreshData(self.dlist)
656             self.parent.m_listToCate.RefreshData(self.cate.getwordstocate())
657             self.parent.m_listCateWords.RefreshData(self.parent.cate.getcatewords(self.GetItemData(index)[0]))
658             for i in range(0, self.GetItemCount(), 1):
659                 self.Select(i, on=0)
660             self.Select(index, on=1)
661             self.parent.butsave.SetBackgroundColour((255,0,0,255))
662         if seq[0][-1] == 'catewords' :
663             if index < self.GetItemCount() and index != -1 :
664                 for val in seq :
665                     word, eff = val[1]
666                     if word not in self.cate.cate['CATE'][self.GetItemData(index)[0]][1] :
667                         self.cate.changewordcate(self.GetItemData(index)[0], word, eff)
668                         self.parent.butsave.SetBackgroundColour((255,0,0,255))
669             else :
670                 index = self.GetItemCount()
671                 if self.cate.addcatefromwordcate(word, eff) :
672                     self.parent.butsave.SetBackgroundColour((255,0,0,255))
673                 else :
674                     dial = wx.MessageDialog(self, "This category name is already used", style=wx.OK|wx.CENTRE).ShowModal()
675                 #self.cate.addwordincate(self.GetItemData(index)[0], word, eff)
676             self.dlist = self.cate.getcate()
677             self.RefreshData(self.dlist)
678             self.parent.m_listToCate.RefreshData(self.cate.getwordstocate())
679             #self.parent.m_listCateWords.RefreshData(self.parent.cate.getcatewords(self.GetItemData(index)[0]))
680             self.parent.m_listCateWords.RefreshData(self.parent.cate.getcatewords(self.GetItemData(self.currentItem)[0]))
681             #self.SetSelection(index)
682
683
684
685 #        for i in seq: # Insert the item data.
686 #            idx = self.InsertItem(index, i[2])
687 #            self.SetItemData(idx, i[1])
688 #            for j in range(1, self.GetColumnCount()):
689 #                try: # Target list can have more columns than source.
690 #                    self.SetItem(idx, j, i[2+j])
691 #                except:
692 #                    pass # Ignore the extra columns.
693 #            index += 1
694
695 class ListForWords(ListForCate) :
696     def __init__(self, parent, dlist = {}, first = []):
697         wx.ListCtrl.__init__( self, parent, -1, style=wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_HRULES|wx.LC_VRULES)
698         self.parent=parent
699         self.cate = self.parent.cate
700         self.dlist= self.cate.getwordstocate()
701         self.first = first
702         self.il = wx.ImageList(16, 16)
703         a={"sm_up":"GO_UP","sm_dn":"GO_DOWN","w_idx":"WARNING","e_idx":"ERROR","i_idx":"QUESTION"}
704         for k,v in list(a.items()):
705             s="self.%s= self.il.Add(wx.ArtProvider.GetBitmap(wx.ART_%s,wx.ART_TOOLBAR,(16,16)))" % (k,v)
706             exec(s)
707         self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
708         self.attr1 = wx.ListItemAttr()
709         self.attr1.SetBackgroundColour((230, 230, 230))
710         self.attr2 = wx.ListItemAttr()
711         self.attr2.SetBackgroundColour("light blue")
712         #self.attrselected = wx.ListItemAttr()
713         #self.attrselected.SetBackgroundColour("red")
714         self.SetListFont()
715         self.selected = {}
716         i = 0
717         for name in ['To categorize'] + self.first :
718             self.InsertColumn(i,name,wx.LIST_FORMAT_LEFT)
719             i += 1
720         self.itemDataMap = self.dlist
721         self.itemIndexMap = list(self.dlist.keys())
722         self.SetItemCount(len(self.dlist))
723         listmix.ListCtrlAutoWidthMixin.__init__(self)
724         listmix.ColumnSorterMixin.__init__(self, len(self.first) + 1)
725         self.SetColumnWidth(0, 400)
726         self.SetColumnWidth(1, wx.LIST_AUTOSIZE)
727
728         self.SortListItems(1, False)
729
730         self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.StartDrag)
731         self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnDClick)
732         self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
733
734         dt = MyListDropCate(self)
735         self.SetDropTarget(dt)
736
737     def OnDClick(self, event) :
738         idx = event.GetIndex()
739         event.Skip()
740
741     def OnItemSelected(self, event):
742         self.currentItem = event.GetIndex() #event.m_itemIndex
743         event.Skip()
744
745     def OnGetItemImage(self, item):
746         return self.i_idx
747
748     def GetItemInfo(self, idx):
749         """
750         Collect all relevant data of a listitem, and put it in a list.
751         """
752
753         l = []
754         l.append(idx) # We need the original index, so it is easier to eventualy delete it.
755         l.append(self.GetItemData(idx)) # Itemdata.
756         l.append(self.GetItemText(idx)) # Text first column.
757         for i in range(1, self.GetColumnCount()): # Possible extra columns.
758             l.append(self.GetItem(idx, i).GetText())
759         l.append('words')
760         return l
761
762
763     def StartDrag(self, event):
764         """
765         Put together a data object for drag-and-drop _from_ this list.
766         """
767
768         l = []
769         idx = -1
770         while True: # Find all the selected items and put them in a list.
771             idx = self.GetNextItem(idx, wx.LIST_NEXT_ALL, wx.LIST_STATE_SELECTED)
772             if idx == -1:
773                 break
774             l.append(self.GetItemInfo(idx))
775
776         # Pickle the items list.
777         itemdata = pickle.dumps(l, 1)
778         # Create our own data format and use it
779         # in a Custom data object.
780         ldata = wx.CustomDataObject("ListCtrlItems")
781         ldata.SetData(itemdata)
782         # Now make a data object for the  item list.
783         data = wx.DataObjectComposite()
784         data.Add(ldata)
785
786         # Create drop source and begin drag-and-drop.
787         dropSource = wx.DropSource(self)
788         dropSource.SetData(data)
789         res = dropSource.DoDragDrop(flags=wx.Drag_DefaultMove)
790
791
792         # If move, we want to remove the item from this list.
793         if res == wx.DragMove and l[0][-1] != 'words':
794             # It's possible we are dragging/dropping from this list to this list.
795             # In which case, the index we are removing may have changed...
796
797             # Find correct position.
798             l.reverse() # Delete all the items, starting with the last item.
799             for i in l:
800                 pos = self.FindItem(i[0], i[2])
801                 print('detruit : ',pos)
802                 self.DeleteItem(pos)
803
804
805     def Insert(self, x, y, seq):
806         """
807         Insert text at given x, y coordinates --- used with drag-and-drop.
808         """
809
810         # Find insertion point.
811         index, flags = self.HitTest((x, y))
812
813         if index == wx.NOT_FOUND: # Not clicked on an item.
814             if flags & (wx.LIST_HITTEST_NOWHERE|wx.LIST_HITTEST_ABOVE|wx.LIST_HITTEST_BELOW): # Empty list or below last item.
815                 index = self.GetItemCount() # Append to end of list.
816             elif self.GetItemCount() > 0:
817                 if y <= self.GetItemRect(0).y: # Clicked just above first item.
818                     index = 0 # Append to top of list.
819                 else:
820                     index = self.GetItemCount() + 1 # Append to end of list.
821         else: # Clicked on an item.
822             # Get bounding rectangle for the item the user is dropping over.
823             rect = self.GetItemRect(index)
824
825             # If the user is dropping into the lower half of the rect,
826             # we want to insert _after_ this item.
827             # Correct for the fact that there may be a heading involved.
828             if y > rect.y - self.GetItemRect(0).y + rect.height/2:
829                 index += 1
830         word, eff = seq[0][1]
831         if seq[0][-1] == 'catewords' :
832             for val in seq :
833                 word, eff = val[1]
834                 categorie = self.cate.findcatefromword(word)
835                 self.cate.addwordinwords(categorie, word, eff)
836             self.RefreshData(self.cate.getwordstocate())
837             self.parent.m_listCate.RefreshData(self.cate.getcate())
838             self.parent.m_listCateWords.RefreshData(self.cate.getcatewords(categorie))
839             self.parent.butsave.SetBackgroundColour((255,0,0,255))
840         elif seq[0][-1] == 'cate' :
841             categorie = seq[0][1][0]
842             self.cate.delcate(categorie)
843             self.RefreshData(self.cate.getwordstocate())
844             self.parent.m_listCate.RefreshData(self.cate.getcate())
845             self.parent.m_listCate.SetSelection(0)
846             self.parent.m_listCateWords.RefreshData(self.cate.getcatewords(self.parent.m_listCate.GetItemText(0)))
847             self.parent.butsave.SetBackgroundColour((255,0,0,255))
848
849
850 class ListForCateWords(ListForCate) :
851     def __init__(self, parent, dlist = {}, first = []):
852         wx.ListCtrl.__init__( self, parent, -1, style=wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_HRULES|wx.LC_VRULES)
853         self.parent=parent
854         self.cate = self.parent.cate
855         self.dlist= {}
856         self.first = first
857         self.il = wx.ImageList(16, 16)
858         a={"sm_up":"GO_UP","sm_dn":"GO_DOWN","p_idx":"TIP","e_idx":"ERROR","i_idx":"QUESTION"}
859         for k,v in list(a.items()):
860             s="self.%s= self.il.Add(wx.ArtProvider.GetBitmap(wx.ART_%s,wx.ART_TOOLBAR,(16,16)))" % (k,v)
861             exec(s)
862         self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
863         self.attr1 = wx.ListItemAttr()
864         self.attr1.SetBackgroundColour((230, 230, 230))
865         self.attr2 = wx.ListItemAttr()
866         self.attr2.SetBackgroundColour("light blue")
867         #self.attrselected = wx.ListItemAttr()
868         #self.attrselected.SetBackgroundColour("red")
869         self.SetListFont()
870         self.selected = {}
871         i = 0
872         for name in ['Contents'] + self.first :
873             self.InsertColumn(i,name,wx.LIST_FORMAT_LEFT)
874             i += 1
875         self.itemDataMap = self.dlist
876         self.itemIndexMap = list(self.dlist.keys())
877         self.SetItemCount(len(self.dlist))
878         listmix.ListCtrlAutoWidthMixin.__init__(self)
879         listmix.ColumnSorterMixin.__init__(self, len(self.first) + 1)
880         self.SetColumnWidth(0, 300)
881         self.SetColumnWidth(1, wx.LIST_AUTOSIZE)
882
883         self.SortListItems(1, False)
884
885         self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.StartDrag)
886         self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
887
888         dt = MyListDropCate(self)
889         self.SetDropTarget(dt)
890
891     def OnItemSelected(self, event):
892         self.currentItem = event.GetIndex() #event.m_itemIndex
893         event.Skip()
894
895
896     def GetItemInfo(self, idx):
897         """
898         Collect all relevant data of a listitem, and put it in a list.
899         """
900
901         l = []
902         l.append(idx) # We need the original index, so it is easier to eventualy delete it.
903         l.append(self.GetItemData(idx)) # Itemdata.
904         l.append(self.GetItemText(idx)) # Text first column.
905         for i in range(1, self.GetColumnCount()): # Possible extra columns.
906             l.append(self.GetItem(idx, i).GetText())
907         l.append('catewords')
908         return l
909
910
911     def StartDrag(self, event):
912         """
913         Put together a data object for drag-and-drop _from_ this list.
914         """
915
916         l = []
917         idx = -1
918         while True: # Find all the selected items and put them in a list.
919             idx = self.GetNextItem(idx, wx.LIST_NEXT_ALL, wx.LIST_STATE_SELECTED)
920             if idx == -1:
921                 break
922             l.append(self.GetItemInfo(idx))
923
924         # Pickle the items list.
925         itemdata = pickle.dumps(l, 1)
926         # Create our own data format and use it
927         # in a Custom data object.
928         ldata = wx.CustomDataObject("ListCtrlItems")
929         ldata.SetData(itemdata)
930         # Now make a data object for the  item list.
931         data = wx.DataObjectComposite()
932         data.Add(ldata)
933
934         # Create drop source and begin drag-and-drop.
935         dropSource = wx.DropSource(self)
936         dropSource.SetData(data)
937         res = dropSource.DoDragDrop(flags=wx.Drag_DefaultMove)
938         print('current')
939         print(self.parent.m_listCate.currentItem)
940
941         # If move, we want to remove the item from this list.
942         #if res == wx.DragMove:
943         #    # It's possible we are dragging/dropping from this list to this list.
944         #    # In which case, the index we are removing may have changed...
945
946         #    # Find correct position.
947         #    l.reverse() # Delete all the items, starting with the last item.
948         #    for i in l:
949         #        pos = self.FindItem(i[0], i[2])
950         #        self.DeleteItem(pos)
951
952
953     def Insert(self, x, y, seq):
954         """
955         Insert text at given x, y coordinates --- used with drag-and-drop.
956         """
957         pass
958         # Find insertion point.
959         index, flags = self.HitTest((x, y))
960 #
961 #        if index == wx.NOT_FOUND: # Not clicked on an item.
962 #            if flags & (wx.LIST_HITTEST_NOWHERE|wx.LIST_HITTEST_ABOVE|wx.LIST_HITTEST_BELOW): # Empty list or below last item.
963 #                index = self.GetItemCount() # Append to end of list.
964 #            elif self.GetItemCount() > 0:
965 #                if y <= self.GetItemRect(0).y: # Clicked just above first item.
966 #                    index = 0 # Append to top of list.
967 #                else:
968 #                    index = self.GetItemCount() + 1 # Append to end of list.
969 #        else: # Clicked on an item.
970 #            # Get bounding rectangle for the item the user is dropping over.
971 #            rect = self.GetItemRect(index)
972 #
973 #            # If the user is dropping into the lower half of the rect,
974 #            # we want to insert _after_ this item.
975 #            # Correct for the fact that there may be a heading involved.
976 #            if y > rect.y - self.GetItemRect(0).y + rect.height/2:
977 #                index += 1
978         print('Insert de ListForCateWords', index,flags)
979         if seq[0][-1] == 'words' :
980             for val in seq :
981                 word, eff = val[1]
982                 categorie = self.parent.m_listCate.getColumnText(self.parent.m_listCate.GetFirstSelected(),0)
983                 self.cate.addwordincate(categorie, word, eff)
984             self.dlist = self.cate.getwordstocate()
985             self.RefreshData(self.cate.getcatewords(categorie))
986             self.parent.m_listCate.RefreshData(self.cate.getcate())
987             self.parent.m_listToCate.RefreshData(self.dlist)
988             self.parent.butsave.SetBackgroundColour((255,0,0,255))
989
990
991 class MyListDropCate(wx.DropTarget):
992     """
993     Drop target for simple lists.
994     """
995     def __init__(self, source):
996         """
997         Arguments:
998         source: source listctrl.
999         """
1000         wx.DropTarget.__init__(self)
1001
1002         #------------
1003
1004         self.dv = source
1005
1006         #------------
1007
1008         # Specify the type of data we will accept.
1009         self.data = wx.CustomDataObject("ListCtrlItems")
1010         self.SetDataObject(self.data)
1011
1012     #-----------------------------------------------------------------------
1013
1014     # Called when OnDrop returns True.
1015     # We need to get the data and do something with it.
1016     def OnData(self, x, y, d):
1017         """
1018         ...
1019         """
1020
1021         # Copy the data from the drag source to our data object.
1022         if self.GetData():
1023             # Convert it back to a list and give it to the viewer.
1024             ldata = self.data.GetData()
1025             l = pickle.loads(ldata)
1026             self.dv.Insert(x, y, l)
1027
1028         # What is returned signals the source what to do
1029         # with the original data (move, copy, etc.)  In this
1030         # case we just return the suggested value given to us.
1031         return d