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