root/05/release-0.5g/kombilo.py

Revision 1, 254.4 kB (checked in by ugz, 5 years ago)

Initial repository layout

  • Property svn:executable set to
Line 
1 #!/usr/bin/python
2 # File: kombilo.py
3
4 ##   Copyright (C) 2001-3 Ulrich Goertz (u@g0ertz.de)
5
6 ##   Kombilo 0.5g is a go database program.
7
8 ##   This program is free software; you can redistribute it and/or modify
9 ##   it under the terms of the GNU General Public License as published by
10 ##   the Free Software Foundation; either version 2 of the License, or
11 ##   (at your option) any later version.
12
13 ##   This program is distributed in the hope that it will be useful,
14 ##   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 ##   GNU General Public License for more details.
17
18 ##   You should have received a copy of the GNU General Public License
19 ##   along with this program (see doc/license.txt); if not, write to the Free Software
20 ##   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 ##   The GNU GPL is also currently available at
22 ##   http://www.gnu.org/copyleft/gpl.html
23
24 from Tkinter import *
25 from tkMessageBox import *
26 from ScrolledText import ScrolledText
27 import tkFileDialog
28 from tkCommonDialog import Dialog
29 from tkSimpleDialog import askstring
30
31 try:
32     import Pmw
33 except:
34     root=Tk()
35     t = Text(root, wrap=WORD, width=70, height=12)
36     t.pack()
37     t.insert(END, 'Kombilo 0.5g\n\n The Python Megawidgets (Pmw) library was not found. It is needed by Kombilo, ' + \
38              'so you have to install it before you can use Kombilo. See the Kombilo tutorial at ' + \
39              'http://www.u-go.net/kombilo/tutorial#installation or the Pmw web site at ' + \
40              'http://pmw.sourceforge.net/ for more information.')
41     root.mainloop()
42     sys.exit()
43
44 try: import Image
45 except: pass
46
47 import encodings.utf_8
48 import time
49 import os
50 import sys
51 import cPickle
52 from copy import copy, deepcopy
53 from string import split, find, join, strip, replace, digits, maketrans, translate, lower
54 import glob
55 import re
56 from array import *
57 import webbrowser
58
59 from board1 import *
60 import v
61
62 try:
63     from sgfpars import Node, Cursor, SGFError, SGFescape
64 except:
65     from sgfparser import Node, Cursor, SGFError, SGFescape
66    
67 try:
68     import matchC
69     CimportSucceeded = 1
70 except:
71     CimportSucceeded = 0
72
73 # ---------------------------------------------------------------------------------------
74
75 class BoardWC(Board):
76     """ Board with support for wildcards and selection
77         of search-relevant region. Furthermore, snapshot returns a dictionary which
78         describes the current board position. It can then be restored with restore."""
79
80    
81     def __init__(self, master, boardSize, canvasSize, fuzzy, labelFontsize, fixedColor, smartFixedColor,
82                  boardImg, blackImg, whiteImg):
83         Board.__init__(self, master, boardSize, canvasSize, fuzzy, labelFontsize, 1, None,
84                        boardImg, blackImg, whiteImg)
85
86         self.wildcards = {}
87
88         self.selection = ((1,1),(19,19))
89
90         self.fixedColor = fixedColor
91         self.smartFixedColor = smartFixedColor
92        
93         self.bound3 = self.bind('<Button-3>', self.selStart)
94         self.bound3m = self.bind('<B3-Motion>', self.selDrag)
95         self.bounds1 = self.bind('<Shift-1>', self.wildcard)
96
97         self.invertSelection = IntVar()
98
99
100     def resize(self, event = None):
101         """ Resize the board. Take care of wildcards and selection here. """
102        
103         Board.resize(self, event)
104         for x,y in self.wildcards.keys():
105             x1, x2, y1, y2 = self.getPixelCoord((x,y),1)
106             if self.canvasSize[1]<=7: margin = 5
107             else: margin = 4
108             self.wildcards[(x,y)] = self.create_oval(x1+margin, x2+margin, y1-margin, y2-margin, fill = 'green',
109                                                      tags=('wildcard','non-bg'))
110
111         self.delete('selection')
112         if self.selection != ((1,1),(19,19)) and self.selection[1] != (0,0):
113             p0 = self.getPixelCoord(self.selection[0],1)
114             p1 = self.getPixelCoord((self.selection[1][0]+1, self.selection[1][1]+1), 1)
115             min = self.getPixelCoord((1,1), 1)[0]+1
116             max = self.getPixelCoord((self.boardSize+1,self.boardSize+1),1)[1]-1
117             if self.canvasSize[1] <= 7:
118                 self.create_rectangle(p0[0], p0[1], p1[0], p1[1],
119                                       tags=('selection', 'non-bg'))
120             elif self.invertSelection.get():
121                 self.create_rectangle(p0[0], p0[1], p1[0], p1[1], fill='brown', stipple='gray50', outline='',
122                                       tags='selection')
123             else:
124                 if p0[1] > min:
125                     self.create_rectangle(min, min, max, p0[1], fill='brown', stipple='gray50', outline='',
126                                           tags='selection')
127                 if p0[0] > min and p0[1] < max:
128                     self.create_rectangle(min, p0[1], p0[0], max, fill='brown', stipple='gray50', outline='',
129                                           tags='selection')
130                 if p1[1] < max:
131                     self.create_rectangle(p0[0], p1[1], p1[0], max, fill='brown', stipple='gray50', outline='',
132                                           tags='selection')
133                 if p1[0] < max and p0[1] < max:
134                     self.create_rectangle(p1[0], p0[1], max, max, fill='brown', stipple='gray50', outline='',
135                                           tags='selection')
136             self.tkraise('non-bg')
137            
138         self.update_idletasks()
139
140
141     def wildcard(self, event):
142         """ Place/delete a wildcard at position of click. """
143        
144         x, y = self.getBoardCoord((event.x, event.y), 1)
145         if not x*y or self.status.has_key((x,y)): return
146
147         if self.wildcards.has_key((x,y)):
148             self.delete(self.wildcards[(x,y)])
149             del self.wildcards[(x,y)]
150         else:
151             x1, x2, y1, y2 = self.getPixelCoord((x,y),1)
152             if self.canvasSize[1]<=7: margin = 5
153             else: margin = 4
154             self.wildcards[(x,y)] = self.create_oval(x1+margin, x2+margin, y1-margin, y2-margin, fill = 'green',
155                                                      tags=('wildcard','non-bg'))
156             self.tkraise('label')
157         self.changed.set(1)       
158
159
160     def delWildcards(self):
161         """ Delete all wildcards. """
162        
163         if self.wildcards: self.changed.set(1)
164         self.delete('wildcard')
165         self.wildcards = {}
166
167
168     def placeLabel(self, pos, type, text=None, color=None):
169         """ Place a label; take care of wildcards at same position. """
170        
171         if self.wildcards.has_key(pos): override = ('black', '')
172         else: override = None
173
174         Board.placeLabel(self, pos, type, text, color, override)
175
176                              
177     # ---- selection of search-relevant section -----------------------------------
178
179     def selStart(self, event):
180         """ React to right-click. """
181         self.delete('selection')
182         x, y = self.getBoardCoord((event.x, event.y), 1)
183         x = max(x, 1)
184         y = max(y, 1)
185         self.selection = ((x,y), (0,0))
186         if self.smartFixedColor.get():
187             self.fixedColor.set(1)
188         self.changed.set(1)
189
190
191     def selDrag(self, event):
192         """ React to right-mouse-key-drag. """
193         pos = self.getBoardCoord((event.x, event.y), 1)
194         if pos[0] >= self.selection[0][0] and pos[1] >= self.selection[0][1]:
195             self.setSelection(self.selection[0], pos)
196            
197
198     def setSelection(self, pos0, pos1):
199         self.selection = (pos0, pos1)
200         self.delete('selection')
201         p0 = self.getPixelCoord(pos0,1)
202         p1 = self.getPixelCoord((pos1[0]+1, pos1[1]+1), 1)
203         min = self.getPixelCoord((1,1), 1)[0]+1
204         max = self.getPixelCoord((self.boardSize+1,self.boardSize+1),1)[1]-1
205         if self.canvasSize[1] <= 7:
206             self.create_rectangle(p0[0], p0[1], p1[0], p1[1],
207                                   tags=('selection', 'non-bg'))
208         elif self.invertSelection.get():
209             self.create_rectangle(p0[0], p0[1], p1[0], p1[1], fill='brown', stipple='gray50', outline='',
210                                   tags='selection')
211         else:
212             if p0[1] > min:
213                 self.create_rectangle(min, min, max, p0[1], fill='brown', stipple='gray50', outline='',
214                                       tags='selection')
215             if p0[0] > min and p0[1] < max:
216                 self.create_rectangle(min, p0[1], p0[0], max, fill='brown', stipple='gray50', outline='',
217                                       tags='selection')
218             if p1[1] < max:
219                 self.create_rectangle(p0[0], p1[1], p1[0], max, fill='brown', stipple='gray50', outline='',
220                                       tags='selection')
221             if p1[0] < max and p0[1] < max:
222                 self.create_rectangle(p1[0], p0[1], max, max, fill='brown', stipple='gray50', outline='',
223                                       tags='selection')
224            
225         self.tkraise('non-bg')
226        
227         if self.smartFixedColor.get():
228             if self.selection == ((1,1), (19,19)):
229                 self.fixedColor.set(1)
230             else:
231                 self.fixedColor.set(0)
232                
233        
234     def newPosition(self):
235         """ Clear board, selection. """
236         self.delete('selection')
237         self.clear()
238         self.delLabels()
239         self.delMarks()
240         self.delWildcards()
241         self.selection = ((1,1),(19,19))
242
243         if self.smartFixedColor.get():
244             self.fixedColor.set(1)
245
246
247     # ---- snapshot & restore (for 'back' button)
248
249     def snapshot(self):
250         """ Return a dictionary which contains the data of all the objects
251             currently displayed on the board, which are not stored in the SGF file.
252             This means, at the moment: wildcards, and selection. """
253        
254         data = {}
255         data['wildcards'] = copy(self.wildcards)
256         data['selection'] = self.selection
257         return data
258
259    
260     def restore(self, d):
261         """ Restore the data from a 'snapshot' dictionary. """
262        
263         for x,y in d['wildcards'].keys():
264             x1, x2, y1, y2 = self.getPixelCoord((x,y),1)
265             self.wildcards[(x,y)] = self.create_oval(x1+4, x2+4, y1-4, y2-4, fill = 'green',
266                                                      tags=('wildcard','non-bg'))
267
268         if d['selection'] != ((1,1),(19,19)) and d['selection'][1] != (0,0):
269             self.setSelection(d['selection'][0], d['selection'][1])
270
271
272 # ---------------------------------------------------------------------------------------
273
274 class chooseDirectory(Dialog):
275     """ A wrapper tor the Tk chooseDirectory widget. """
276    
277     command = "tk_chooseDirectory"
278
279     def _fixresult(self, widget, result):
280         if result:
281             self.options["initialdir"] = result
282         self.directory = result
283         return result
284
285 def askdirectory(**options):
286     return apply(chooseDirectory, (), options).show()
287
288 # ---------------------------------------------------------------------------------------
289
290 class TextEditor:
291     """ A very simple text editor, based on the Tkinter ScrolledText widget.
292     You can perform very limited editing, and save the result to a file. """
293
294     def __init__(self, t = '', defpath='', font = None):
295
296         if font is None:
297             font = (StringVar(), IntVar(), StringVar())
298             font[0].set('Courier')
299             font[1].set(10)
300             font[2].set('')
301            
302         self.window = Toplevel()
303
304         self.window.protocol('WM_DELETE_WINDOW', self.quit)
305
306         self.text = ScrolledText(self.window, width=70, height=30,
307                                  font=(font[0].get(), font[1].get(), font[2].get()))
308         self.text.pack(side=BOTTOM, fill=BOTH, expand=YES)
309         self.text.insert(END, t)
310
311         self.buttonFrame = Frame(self.window)
312         self.buttonFrame.pack(side=TOP, expand=NO, fill=X)
313
314         Button(self.buttonFrame, text='Quit', command=self.quit).pack(side=RIGHT)
315         Button(self.buttonFrame, text='Save as', command=self.saveas).pack(side=RIGHT)
316
317         # self.window.tkraise()
318         self.window.focus_force()
319
320         if defpath:
321             self.defpath = defpath
322         else:
323             self.defpath = os.curdir
324            
325     def saveas(self):
326         f = tkFileDialog.asksaveasfilename(initialdir = self.defpath)
327         if not f: return
328         try:
329             file = open(f, 'w')
330             file.write(self.text.get('1.0', END).encode('utf-8', 'ignore'))
331             file.close()
332         except IOError:
333             showwarning('IO Error', 'Cannot write to ' + f)
334
335     def quit(self):
336         self.window.destroy()
337
338
339 class ESR_TextEditor(TextEditor):
340     """The text editor which is used by the exportSearchResults function.
341     It adds a button to include the complete game list to the TextEditor."""
342
343
344     def __init__(self, master, style, t='', defpath='', font=None):
345         TextEditor.__init__(self, t, defpath, font)
346         self.mster = master
347         self.style = style
348        
349         Button(self.buttonFrame, text='Include game list', command=self.includeGameList).pack(side=LEFT)
350
351
352     def includeGameList(self):
353         if self.style: # wiki
354             self.text.insert(END, '\n\n!Game list\n\n' + join(self.mster.gamelist.list.get(0, END), ' %%%\n'))
355         else:
356             self.text.insert(END, '\n\nGame list\n\n' + join(self.mster.gamelist.list.get(0, END), '\n'))
357        
358
359 # -------------------------------------------------------------------------------------
360
361 class DataWindow(v.DataWindow):
362
363     def __init__(self, master):
364
365         v.DataWindow.__init__(self, master)
366
367         self.prevSF = Pmw.ScrolledFrame(self.prevSearchF, usehullsize=1, hull_width=300, hull_height=135,
368                                         hscrollmode='static', vscrollmode='none', vertflex='elastic')
369         self.prevSF.pack(expand=YES, fill=X)
370         self.prevSV = IntVar()
371         self.prevSV.set(1)
372
373         b1 = Checkbutton(self.toolbarF, text = 'History', variable = self.prevSV,
374                          command = self.togglePrevSearches, indicatoron=0)
375         b1.grid(row=0, column=6)
376
377         self.win.setnaturalsize()
378
379        
380     def initPanes(self):
381         """ Create the panes in the data window. """
382
383         self.toolbarF = self.win.add(name='toolb', min=28, max=28)
384         self.filelistF = self.win.add(name='filel', min=0.01, max=1.0, size=65)
385         self.gamelistF = self.win.add(name='gamel', min=0.01, max=1.0, size=65)
386         self.gameinfoF = self.win.add(name='gamei', min=0.01, max=1.0, size=75)
387         self.editToolsF = self.win.add(name='editt', min=1, max=30, size=30)
388         self.gametreeF = self.win.add(name='gamet', min=0.01, max=1.0, size=90)
389         self.commentsF = self.win.add(name='comm', min=0.01, max=1.0, size=80)
390         self.prevSearchF = self.win.add('prse', min=1, max=1.0, size=135)
391
392
393     def get_geometry(self):
394         """ Return a list of current sizes of the panes. """
395        
396         l = [ self.filelistV.get(), self.win._size['filel'],
397               self.gamelistV.get(), self.win._size['gamel'],
398               self.gameinfoV.get(), self.win._size['gamei'],
399               self.editToolsV.get(), self.win._size['editt'],
400               self.gametreeV.get(), self.win._size['gamet'],
401               self.commentsV.get(), self.win._size['comm'],
402               self.prevSV.get(), self.win._size['prse']]
403         l1 = [ `x` for x in l ]
404         l1.append(self.window.geometry())
405
406         return join(l1, '|%')
407        
408     def set_geometry(self, s):
409         """ Reset the sizes of the panes to the given ones. """
410        
411         l = split(s, '|%')
412        
413         l1 = [ self.filelistV,
414                self.gamelistV,
415                self.gameinfoV,
416                self.editToolsV,
417                self.gametreeV,
418                self.commentsV,
419                self.prevSV ]
420                
421         for i in range(len(l)/2):
422             l1[i].set(int(l[2*i]))
423             if int(l[2*i]):
424                 self.win.configurepane(i+1, min=10, max=1.0, size = int(l[2*i+1]))
425             else:
426                 self.win.configurepane(i+1, min=0.0, max=0.0, size = 0.0)
427         self.win.updatelayout()
428         self.window.geometry(l[-1])
429
430
431     def gamelistRelease(self, event):
432         index1, index2 = v.DataWindow.gamelistRelease(self, event)
433         if index1:
434             self.mster.prevSearches.exchangeGames(self.mster.cursor, index1, index2)
435        
436
437     def togglePrevSearches(self):
438         if self.prevSV.get():
439             s = self.window.geometry()
440             x1, x2, x3 = split(s, '+')
441             x, y = split(x1, 'x')
442             y = `int(y)+135`
443             self.window.geometry('%sx%s+%s+%s' % (x, y, x2, x3))
444             self.win.configurepane(7, min=10, max=1.0, size=135)
445         else:
446             s = self.window.geometry()
447             x1, x2, x3 = split(s, '+')
448             x, y = split(x1, 'x')
449             y = `int(y)-self.win._size['prse']`
450             self.window.geometry('%sx%s+%s+%s' % (x, y, x2, x3))
451             self.win.configurepane(7, min=0.0, max=0.0, size=0.0)
452         self.win.updatelayout()
453        
454
455 # -------------------------------------------------------------------------------------
456
457 class GameList(v.ScrolledList):
458     """ This is a scrolled list which shows the game list. All the underlying data
459         is contained in self.DBlist, which is a list of dictionaries containing the
460         information for the single databases. self.DBlist[i] will contain the keys
461         'name': name of the database, i.e. path to the *.db files
462         'sgfpath': path to the SGF files
463         'data': list of all games in the database.
464                 This is a list of tuples of the form
465                 (filename, namelistIndex, PB, PW, result, signature)
466         'current': a list of the indices of the games in data which are in the current
467                    game list
468         'results': for each game in current, the list of matches found in the previous
469                    search (If there are lots of matches, this list will consume a lot
470                    of memory. Maybe at some time I will get around to fix this ...)
471         'numNewGames': number of games which were "append"ed, but not yet inserted into
472                        the list (by addAppendedGames)
473         """
474    
475     def __init__(self, parent, master, noGamesLabel, winPercLabel, gameinfo):
476         v.ScrolledList.__init__(self, parent)
477         self.list.config(width=52, height=6)
478
479         self.onSelectionChange = self.printGameInfo
480         self.list.bind('<Button-1>', self.onSelectionChange)
481
482         self.bind('<Up>', self.up)
483         self.bind('<Down>', self.down)
484         self.bind('<Prior>', self.pgup)
485         self.bind('<Next>', self.pgdown)
486         self.bind('<Return>', self.handleDoubleClick)
487         self.list.bind('<Double-1>', self.handleDoubleClick)
488         self.list.bind('<Shift-1>', self.handleShiftClick)
489         self.list.bind('<Button-3>', self.rightMouseButton)
490
491         self.mster = master
492        
493         self.DBlist = []      # list of dicts
494
495         self.noGamesLabel = noGamesLabel
496         self.winPercLabel = winPercLabel
497         self.gameinfo = gameinfo
498        
499         self.Bwins = 0
500         self.Wwins = 0
501         self.Owins = 0 # others: Jigo, Void, Left unfinished, ? (Unknown)
502
503         self.appendClist = []
504
505         self.references = {}
506
507         self.sort = None
508         self.gameIndex = []
509
510         self.showFilename = 1
511         self.showDate = 0
512
513
514     def rightMouseButton(self, event):
515        
516         index = self.list.nearest(event.y)
517         if index == -1: return
518
519         DBindex, index = self.getIndex(index)
520         if DBindex == -1: return
521
522         f1 = strip(os.path.join(self.DBlist[DBindex]['sgfpath'],
523                                 self.DBlist[DBindex]['data'][self.DBlist[DBindex]['current'][index]][0]))
524        
525         if find(f1, '[') != -1:
526             f1, f2 = split(f1, '[')
527             gameNumber = int(strip(f2)[:-1])
528         else:
529             gameNumber = 0
530
531         filename = getFilename(f1)
532
533         try:
534             file = open(filename)
535             sgf = file.read()
536             file.close()
537             c = Cursor(sgf, 1)
538             rootNode = c.getRootNode(gameNumber)
539         except IOError:
540             showwarning('Error', 'I/O Error')
541             return
542         except SGFError:
543             showwarning('Error', 'SGF error')
544             return
545        
546         backup = copy(rootNode)
547
548         newRootNode = self.mster.gameinfo(rootNode)
549         if backup != newRootNode:
550             c.updateRootNode(newRootNode, gameNumber)
551             try:
552                 s = c.output()
553                 file = open(filename, 'w')
554                 file.write(s)
555                 file.close()
556             except IOError:
557                 showwarning('I/O Error', 'Could not write to file ' + filename)
558
559
560     def handleDoubleClick(self, event):
561         """ This is called upon double-clicks."""
562        
563         index = self.list.curselection()
564         if index:
565             label = self.list.get(index)
566             self.mster.openViewer(index, label)
567
568
569     def handleShiftClick(self, event):
570         index = self.list.nearest(event.y)
571         index1 = self.list.curselection()
572         if index1: self.list.select_clear(index1[0])
573         self.list.select_set(index)
574         self.onSelectionChange(event)
575         try:
576             label = self.list.get(index)
577         except:
578             return
579         self.mster.altOpenViewer([index], label)
580
581
582     def sortCrit(self, index, c):
583         dbIndex, j = self.getIndex(index)
584         return self.DBlist[dbIndex]['data'][self.DBlist[dbIndex]['current'][j]][c]
585
586
587     def sortlist(self, perDB, criterion):
588         """ Sort the game list wrt the given criterion, and update it. """
589
590         self.sort = []
591
592         if criterion == 'PW': c = 3
593         elif criterion == 'PB': c = 2
594         elif criterion == 'DT': c = 6
595         else: c = 0          # criterion == 'filename'
596
597         self.appendClist = []
598
599         if perDB and c == 0:
600             for i in range(len(self.DBlist)):
601                 db = self.DBlist[i]
602                 if db['disabled']: continue
603                 if db.has_key('sort'): del db['sort']
604                 self.appendClist.extend([(i, db['current'][j], j) for j in xrange(len(db['current']))])
605         elif perDB:
606             for i in range(len(self.DBlist)):
607                 db = self.DBlist[i]
608                 if db['disabled']: continue
609                 if db.has_key('sort'): del db['sort']
610                
611                 s = []
612                 s.extend([(db['data'][j][c], j) for j in range(len(db['data']))])
613
614                 s.sort()
615
616                 self.sort.extend([(i, j) for dummy, j in s])
617         else:
618            
619             for i in range(len(self.DBlist)):
620                 db = self.DBlist[i]
621                 if db['disabled']: continue
622                 self.sort.extend([(db['data'][j][c], i, j) for j in range(len(db['data']))])
623
624             self.sort.sort()
625            
626             self.sort = [(i, j) for dummy, i, j in self.sort]
627
628         if not (perDB and c==0):
629
630             for i in range(len(self.DBlist)):
631                 db = self.DBlist[i]
632                 db['sort'] = array('L', [0] * len(db['data']))
633
634                 for ii in range(len(self.sort)):
635                     if self.sort[ii][0] == i: db['sort'][self.sort[ii][1]] = ii
636                
637                 self.appendClist.extend([(i, db['current'][j], j) for j in xrange(len(db['current']))])
638            
639         self.list.delete(0, END)
640         self.Bwins, self.Wwins, self.Owins = 0,0,0
641         self.update()
642
643
644     def getIndex(self, i):
645         """ Returns dbIndex, j, such that self.DBlist[dbIndex]['current'][j] corresponds to the
646         i-th line of the ScrolledList. """
647
648         if i < len(self.gameIndex):
649             return self.gameIndex[i]
650         else:
651             return -1, -1
652
653
654     def clear(self):
655         """ Clear the list. """
656
657         self.update()
658
659         for db in self.DBlist:
660             if not db['disabled']:
661                 db['current'] = array('L')
662                 db['results'] = []
663            
664         self.Bwins = 0
665         self.Wwins = 0
666         self.Owins = 0
667         self.update()
668
669         self.delete(0,END)
670         self.list.update_idletasks()
671
672
673     def append(self, i, d):
674         """ Append an entry to the gamelist. """
675         self.DBlist[i]['data'].append(d)
676
677         if not self.DBlist[i]['disabled']:
678             self.DBlist[i]['numNewGames'] += 1
679
680     def addAppendedGames(self):
681         """ Display the games which were "append"ed in the list. """
682        
683         for i in range(len(self.DBlist)):
684             db = self.DBlist[i]
685             if db['numNewGames']:                              # display append'ed entries
686                 db['current'].extend(array('L', range(len(db['data'])-db['numNewGames'], len(db['data']))))
687                 self.gameIndex.extend([(i, j) for j in range(db['numNewGames'])])
688             db['results'] = db['results'] + [''] * db['numNewGames']
689             db['numNewGames'] = 0
690
691         noOfG = self.noOfGames()
692         self.noGamesLabel.config(text = `noOfG` + ' games')
693
694         if noOfG:
695             Bperc = self.Bwins * 100.0 / noOfG
696             Wperc = self.Wwins * 100.0 / noOfG
697             self.winPercLabel.config(text='B: %1.1f%%, W: %1.1f%%' % (Bperc, Wperc))
698         else: self.winPercLabel.config(text='')
699
700         self.delete(0,END)
701         self.sortlist(self.mster.options.sortPerDatabase.get(), self.mster.options.sortCriterion.get())
702        
703
704     def appendC(self, dbIndex, i, res = ''):
705         """ Append an entry to self.current """
706
707         db = self.DBlist[dbIndex]
708        
709         db['current'].append(i)
710         db['results'].append(res)
711         self.appendClist.append((dbIndex, i, len(db['current'])-1))
712
713
714     def reset(self):
715         """ Reset the list, s.t. it includes all the games from self.data. """
716        
717         for i in range(len(self.DBlist)):
718             db = self.DBlist[i]
719             if not db['disabled']:
720                 db['current'] = array('L', range(len(db['data'])))
721                 db['results'] = [''] * len(db['data'])
722                 self.appendClist += [ (i, j, j) for j in range(len(db['data']))]
723             else:
724                 db['current'] = array('L')
725                 db['results'] = []
726         self.delete(0, END)
727         self.Bwins, self.Wwins, self.Owins = 0, 0, 0
728         self.update()
729         self.clearGameInfo()
730
731        
732     def update(self):
733         """ Put the changes in data, current in effect in self.list. """
734        
735         if self.appendClist:         # display appendC'ed entries
736             
737             strl = []
738             self.gameIndex = []
739
740             if self.sort:
741                 self.appendClist = [ (self.DBlist[x[0]]['sort'][x[1]], x) for x in self.appendClist ]
742                 self.appendClist.sort()
743                 self.appendClist = map(lambda x: x[1], self.appendClist)
744
745             if self.mster.options.sortReverse.get():
746                 self.appendClist.reverse()
747
748             for db, index, index1 in self.appendClist:
749                 d = self.DBlist[db]['data'][index]
750                 res = self.DBlist[db]['results'][index1]
751                 if d[4] == 'B': self.Bwins += 1
752                 elif d[4] == 'W': self.Wwins += 1
753                 else: self.Owins += 1
754
755                 li = [d[7]]
756                 if self.showFilename:
757                     endFilename = find(d[0], '[')
758                     if endFilename == -1: endFilename = len(d[0])
759                
760                     if d[0][endFilename-1] == '.':
761                         filename = d[0][:endFilename-1] + d[0][endFilename:]
762                     elif d[0][endFilename-2:endFilename] == '.m':
763                         filename = d[0][:endFilename-2] + d[0][endFilename:]
764                     else: filename = d[0]
765
766                     li.append(filename + ': ')
767
768                 li.append(d[3] + ' - ' + d[2] + ' (' + d[4] + '), ')
769                 if self.showDate: li.append(d[6]+', ')
770                 li.append(res)
771                 strl.append(join(li, ''))
772                 self.gameIndex.append((db, index1))
773
774             ti = time.time()
775             self.insert(END, strl)
776            
777             self.appendClist = []
778
779         noOfG = self.noOfGames()
780         self.noGamesLabel.config(text = `noOfG` + ' games')
781
782         if noOfG:
783             Bperc = self.Bwins * 100.0 / noOfG
784             Wperc = self.Wwins * 100.0 / noOfG
785             self.winPercLabel.config(text='B: %1.1f%%, W: %1.1f%%' % (Bperc, Wperc))
786         else: self.winPercLabel.config(text='')
787        
788
789     def noOfGames(self):
790         return reduce(lambda x,y:x+y, [ len(db['current']) for db in self.DBlist if not db['disabled'] ], 0)
791
792
793     def printGameInfo(self, event, index = -1):
794         """ Print game info of selected game. """
795
796         if index == -1:
797             index = self.list.nearest(event.y)
798             self.focus()
799         if index == -1:
800             return
801
802         DBindex, index = self.getIndex(index)
803         if DBindex == -1: return
804            
805         f1 = strip(os.path.join(self.DBlist[DBindex]['sgfpath'],
806                                 self.DBlist[DBindex]['data'][self.DBlist[DBindex]['current'][index]][0]))
807
808         if find(f1, '[') != -1:
809             f1, f2 = split(f1, '[')
810             gameNumber = int(strip(f2)[:-1])
811         else:
812             gameNumber = 0
813
814         filename = getFilename(f1)
815
816         try:
817             f = open(filename)
818             sgf = f.read()
819             f.close()
820             node = Cursor(sgf, 1).getRootNode(gameNumber)
821         except:
822             return
823
824         t = ''
825        
826         if node.has_key('PW'): t = t + node['PW'][0]
827         else: t = t + ' ?'
828         if node.has_key('WR'): t = t + ', ' + node['WR'][0]
829
830         t = t + ' - '
831
832         if node.has_key('PB'): t = t + node['PB'][0]
833         else: t = t + ' ?'
834         if node.has_key('BR'): t = t + ', ' + node['BR'][0]
835
836         if node.has_key(