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