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