17db96d56Sopenharmony_ci#!/usr/bin/env python3 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ci""" 47db96d56Sopenharmony_ciSS1 -- a spreadsheet-like application. 57db96d56Sopenharmony_ci""" 67db96d56Sopenharmony_ci 77db96d56Sopenharmony_ciimport os 87db96d56Sopenharmony_ciimport re 97db96d56Sopenharmony_ciimport sys 107db96d56Sopenharmony_cifrom xml.parsers import expat 117db96d56Sopenharmony_cifrom xml.sax.saxutils import escape 127db96d56Sopenharmony_ci 137db96d56Sopenharmony_ciLEFT, CENTER, RIGHT = "LEFT", "CENTER", "RIGHT" 147db96d56Sopenharmony_ci 157db96d56Sopenharmony_cidef ljust(x, n): 167db96d56Sopenharmony_ci return x.ljust(n) 177db96d56Sopenharmony_cidef center(x, n): 187db96d56Sopenharmony_ci return x.center(n) 197db96d56Sopenharmony_cidef rjust(x, n): 207db96d56Sopenharmony_ci return x.rjust(n) 217db96d56Sopenharmony_cialign2action = {LEFT: ljust, CENTER: center, RIGHT: rjust} 227db96d56Sopenharmony_ci 237db96d56Sopenharmony_cialign2xml = {LEFT: "left", CENTER: "center", RIGHT: "right"} 247db96d56Sopenharmony_cixml2align = {"left": LEFT, "center": CENTER, "right": RIGHT} 257db96d56Sopenharmony_ci 267db96d56Sopenharmony_cialign2anchor = {LEFT: "w", CENTER: "center", RIGHT: "e"} 277db96d56Sopenharmony_ci 287db96d56Sopenharmony_cidef sum(seq): 297db96d56Sopenharmony_ci total = 0 307db96d56Sopenharmony_ci for x in seq: 317db96d56Sopenharmony_ci if x is not None: 327db96d56Sopenharmony_ci total += x 337db96d56Sopenharmony_ci return total 347db96d56Sopenharmony_ci 357db96d56Sopenharmony_ciclass Sheet: 367db96d56Sopenharmony_ci 377db96d56Sopenharmony_ci def __init__(self): 387db96d56Sopenharmony_ci self.cells = {} # {(x, y): cell, ...} 397db96d56Sopenharmony_ci self.ns = dict( 407db96d56Sopenharmony_ci cell = self.cellvalue, 417db96d56Sopenharmony_ci cells = self.multicellvalue, 427db96d56Sopenharmony_ci sum = sum, 437db96d56Sopenharmony_ci ) 447db96d56Sopenharmony_ci 457db96d56Sopenharmony_ci def cellvalue(self, x, y): 467db96d56Sopenharmony_ci cell = self.getcell(x, y) 477db96d56Sopenharmony_ci if hasattr(cell, 'recalc'): 487db96d56Sopenharmony_ci return cell.recalc(self.ns) 497db96d56Sopenharmony_ci else: 507db96d56Sopenharmony_ci return cell 517db96d56Sopenharmony_ci 527db96d56Sopenharmony_ci def multicellvalue(self, x1, y1, x2, y2): 537db96d56Sopenharmony_ci if x1 > x2: 547db96d56Sopenharmony_ci x1, x2 = x2, x1 557db96d56Sopenharmony_ci if y1 > y2: 567db96d56Sopenharmony_ci y1, y2 = y2, y1 577db96d56Sopenharmony_ci seq = [] 587db96d56Sopenharmony_ci for y in range(y1, y2+1): 597db96d56Sopenharmony_ci for x in range(x1, x2+1): 607db96d56Sopenharmony_ci seq.append(self.cellvalue(x, y)) 617db96d56Sopenharmony_ci return seq 627db96d56Sopenharmony_ci 637db96d56Sopenharmony_ci def getcell(self, x, y): 647db96d56Sopenharmony_ci return self.cells.get((x, y)) 657db96d56Sopenharmony_ci 667db96d56Sopenharmony_ci def setcell(self, x, y, cell): 677db96d56Sopenharmony_ci assert x > 0 and y > 0 687db96d56Sopenharmony_ci assert isinstance(cell, BaseCell) 697db96d56Sopenharmony_ci self.cells[x, y] = cell 707db96d56Sopenharmony_ci 717db96d56Sopenharmony_ci def clearcell(self, x, y): 727db96d56Sopenharmony_ci try: 737db96d56Sopenharmony_ci del self.cells[x, y] 747db96d56Sopenharmony_ci except KeyError: 757db96d56Sopenharmony_ci pass 767db96d56Sopenharmony_ci 777db96d56Sopenharmony_ci def clearcells(self, x1, y1, x2, y2): 787db96d56Sopenharmony_ci for xy in self.selectcells(x1, y1, x2, y2): 797db96d56Sopenharmony_ci del self.cells[xy] 807db96d56Sopenharmony_ci 817db96d56Sopenharmony_ci def clearrows(self, y1, y2): 827db96d56Sopenharmony_ci self.clearcells(0, y1, sys.maxsize, y2) 837db96d56Sopenharmony_ci 847db96d56Sopenharmony_ci def clearcolumns(self, x1, x2): 857db96d56Sopenharmony_ci self.clearcells(x1, 0, x2, sys.maxsize) 867db96d56Sopenharmony_ci 877db96d56Sopenharmony_ci def selectcells(self, x1, y1, x2, y2): 887db96d56Sopenharmony_ci if x1 > x2: 897db96d56Sopenharmony_ci x1, x2 = x2, x1 907db96d56Sopenharmony_ci if y1 > y2: 917db96d56Sopenharmony_ci y1, y2 = y2, y1 927db96d56Sopenharmony_ci return [(x, y) for x, y in self.cells 937db96d56Sopenharmony_ci if x1 <= x <= x2 and y1 <= y <= y2] 947db96d56Sopenharmony_ci 957db96d56Sopenharmony_ci def movecells(self, x1, y1, x2, y2, dx, dy): 967db96d56Sopenharmony_ci if dx == 0 and dy == 0: 977db96d56Sopenharmony_ci return 987db96d56Sopenharmony_ci if x1 > x2: 997db96d56Sopenharmony_ci x1, x2 = x2, x1 1007db96d56Sopenharmony_ci if y1 > y2: 1017db96d56Sopenharmony_ci y1, y2 = y2, y1 1027db96d56Sopenharmony_ci assert x1+dx > 0 and y1+dy > 0 1037db96d56Sopenharmony_ci new = {} 1047db96d56Sopenharmony_ci for x, y in self.cells: 1057db96d56Sopenharmony_ci cell = self.cells[x, y] 1067db96d56Sopenharmony_ci if hasattr(cell, 'renumber'): 1077db96d56Sopenharmony_ci cell = cell.renumber(x1, y1, x2, y2, dx, dy) 1087db96d56Sopenharmony_ci if x1 <= x <= x2 and y1 <= y <= y2: 1097db96d56Sopenharmony_ci x += dx 1107db96d56Sopenharmony_ci y += dy 1117db96d56Sopenharmony_ci new[x, y] = cell 1127db96d56Sopenharmony_ci self.cells = new 1137db96d56Sopenharmony_ci 1147db96d56Sopenharmony_ci def insertrows(self, y, n): 1157db96d56Sopenharmony_ci assert n > 0 1167db96d56Sopenharmony_ci self.movecells(0, y, sys.maxsize, sys.maxsize, 0, n) 1177db96d56Sopenharmony_ci 1187db96d56Sopenharmony_ci def deleterows(self, y1, y2): 1197db96d56Sopenharmony_ci if y1 > y2: 1207db96d56Sopenharmony_ci y1, y2 = y2, y1 1217db96d56Sopenharmony_ci self.clearrows(y1, y2) 1227db96d56Sopenharmony_ci self.movecells(0, y2+1, sys.maxsize, sys.maxsize, 0, y1-y2-1) 1237db96d56Sopenharmony_ci 1247db96d56Sopenharmony_ci def insertcolumns(self, x, n): 1257db96d56Sopenharmony_ci assert n > 0 1267db96d56Sopenharmony_ci self.movecells(x, 0, sys.maxsize, sys.maxsize, n, 0) 1277db96d56Sopenharmony_ci 1287db96d56Sopenharmony_ci def deletecolumns(self, x1, x2): 1297db96d56Sopenharmony_ci if x1 > x2: 1307db96d56Sopenharmony_ci x1, x2 = x2, x1 1317db96d56Sopenharmony_ci self.clearcells(x1, x2) 1327db96d56Sopenharmony_ci self.movecells(x2+1, 0, sys.maxsize, sys.maxsize, x1-x2-1, 0) 1337db96d56Sopenharmony_ci 1347db96d56Sopenharmony_ci def getsize(self): 1357db96d56Sopenharmony_ci maxx = maxy = 0 1367db96d56Sopenharmony_ci for x, y in self.cells: 1377db96d56Sopenharmony_ci maxx = max(maxx, x) 1387db96d56Sopenharmony_ci maxy = max(maxy, y) 1397db96d56Sopenharmony_ci return maxx, maxy 1407db96d56Sopenharmony_ci 1417db96d56Sopenharmony_ci def reset(self): 1427db96d56Sopenharmony_ci for cell in self.cells.values(): 1437db96d56Sopenharmony_ci if hasattr(cell, 'reset'): 1447db96d56Sopenharmony_ci cell.reset() 1457db96d56Sopenharmony_ci 1467db96d56Sopenharmony_ci def recalc(self): 1477db96d56Sopenharmony_ci self.reset() 1487db96d56Sopenharmony_ci for cell in self.cells.values(): 1497db96d56Sopenharmony_ci if hasattr(cell, 'recalc'): 1507db96d56Sopenharmony_ci cell.recalc(self.ns) 1517db96d56Sopenharmony_ci 1527db96d56Sopenharmony_ci def display(self): 1537db96d56Sopenharmony_ci maxx, maxy = self.getsize() 1547db96d56Sopenharmony_ci width, height = maxx+1, maxy+1 1557db96d56Sopenharmony_ci colwidth = [1] * width 1567db96d56Sopenharmony_ci full = {} 1577db96d56Sopenharmony_ci # Add column heading labels in row 0 1587db96d56Sopenharmony_ci for x in range(1, width): 1597db96d56Sopenharmony_ci full[x, 0] = text, alignment = colnum2name(x), RIGHT 1607db96d56Sopenharmony_ci colwidth[x] = max(colwidth[x], len(text)) 1617db96d56Sopenharmony_ci # Add row labels in column 0 1627db96d56Sopenharmony_ci for y in range(1, height): 1637db96d56Sopenharmony_ci full[0, y] = text, alignment = str(y), RIGHT 1647db96d56Sopenharmony_ci colwidth[0] = max(colwidth[0], len(text)) 1657db96d56Sopenharmony_ci # Add sheet cells in columns with x>0 and y>0 1667db96d56Sopenharmony_ci for (x, y), cell in self.cells.items(): 1677db96d56Sopenharmony_ci if x <= 0 or y <= 0: 1687db96d56Sopenharmony_ci continue 1697db96d56Sopenharmony_ci if hasattr(cell, 'recalc'): 1707db96d56Sopenharmony_ci cell.recalc(self.ns) 1717db96d56Sopenharmony_ci if hasattr(cell, 'format'): 1727db96d56Sopenharmony_ci text, alignment = cell.format() 1737db96d56Sopenharmony_ci assert isinstance(text, str) 1747db96d56Sopenharmony_ci assert alignment in (LEFT, CENTER, RIGHT) 1757db96d56Sopenharmony_ci else: 1767db96d56Sopenharmony_ci text = str(cell) 1777db96d56Sopenharmony_ci if isinstance(cell, str): 1787db96d56Sopenharmony_ci alignment = LEFT 1797db96d56Sopenharmony_ci else: 1807db96d56Sopenharmony_ci alignment = RIGHT 1817db96d56Sopenharmony_ci full[x, y] = (text, alignment) 1827db96d56Sopenharmony_ci colwidth[x] = max(colwidth[x], len(text)) 1837db96d56Sopenharmony_ci # Calculate the horizontal separator line (dashes and dots) 1847db96d56Sopenharmony_ci sep = "" 1857db96d56Sopenharmony_ci for x in range(width): 1867db96d56Sopenharmony_ci if sep: 1877db96d56Sopenharmony_ci sep += "+" 1887db96d56Sopenharmony_ci sep += "-"*colwidth[x] 1897db96d56Sopenharmony_ci # Now print The full grid 1907db96d56Sopenharmony_ci for y in range(height): 1917db96d56Sopenharmony_ci line = "" 1927db96d56Sopenharmony_ci for x in range(width): 1937db96d56Sopenharmony_ci text, alignment = full.get((x, y)) or ("", LEFT) 1947db96d56Sopenharmony_ci text = align2action[alignment](text, colwidth[x]) 1957db96d56Sopenharmony_ci if line: 1967db96d56Sopenharmony_ci line += '|' 1977db96d56Sopenharmony_ci line += text 1987db96d56Sopenharmony_ci print(line) 1997db96d56Sopenharmony_ci if y == 0: 2007db96d56Sopenharmony_ci print(sep) 2017db96d56Sopenharmony_ci 2027db96d56Sopenharmony_ci def xml(self): 2037db96d56Sopenharmony_ci out = ['<spreadsheet>'] 2047db96d56Sopenharmony_ci for (x, y), cell in self.cells.items(): 2057db96d56Sopenharmony_ci if hasattr(cell, 'xml'): 2067db96d56Sopenharmony_ci cellxml = cell.xml() 2077db96d56Sopenharmony_ci else: 2087db96d56Sopenharmony_ci cellxml = '<value>%s</value>' % escape(cell) 2097db96d56Sopenharmony_ci out.append('<cell row="%s" col="%s">\n %s\n</cell>' % 2107db96d56Sopenharmony_ci (y, x, cellxml)) 2117db96d56Sopenharmony_ci out.append('</spreadsheet>') 2127db96d56Sopenharmony_ci return '\n'.join(out) 2137db96d56Sopenharmony_ci 2147db96d56Sopenharmony_ci def save(self, filename): 2157db96d56Sopenharmony_ci text = self.xml() 2167db96d56Sopenharmony_ci with open(filename, "w", encoding='utf-8') as f: 2177db96d56Sopenharmony_ci f.write(text) 2187db96d56Sopenharmony_ci if text and not text.endswith('\n'): 2197db96d56Sopenharmony_ci f.write('\n') 2207db96d56Sopenharmony_ci 2217db96d56Sopenharmony_ci def load(self, filename): 2227db96d56Sopenharmony_ci with open(filename, 'rb') as f: 2237db96d56Sopenharmony_ci SheetParser(self).parsefile(f) 2247db96d56Sopenharmony_ci 2257db96d56Sopenharmony_ciclass SheetParser: 2267db96d56Sopenharmony_ci 2277db96d56Sopenharmony_ci def __init__(self, sheet): 2287db96d56Sopenharmony_ci self.sheet = sheet 2297db96d56Sopenharmony_ci 2307db96d56Sopenharmony_ci def parsefile(self, f): 2317db96d56Sopenharmony_ci parser = expat.ParserCreate() 2327db96d56Sopenharmony_ci parser.StartElementHandler = self.startelement 2337db96d56Sopenharmony_ci parser.EndElementHandler = self.endelement 2347db96d56Sopenharmony_ci parser.CharacterDataHandler = self.data 2357db96d56Sopenharmony_ci parser.ParseFile(f) 2367db96d56Sopenharmony_ci 2377db96d56Sopenharmony_ci def startelement(self, tag, attrs): 2387db96d56Sopenharmony_ci method = getattr(self, 'start_'+tag, None) 2397db96d56Sopenharmony_ci if method: 2407db96d56Sopenharmony_ci method(attrs) 2417db96d56Sopenharmony_ci self.texts = [] 2427db96d56Sopenharmony_ci 2437db96d56Sopenharmony_ci def data(self, text): 2447db96d56Sopenharmony_ci self.texts.append(text) 2457db96d56Sopenharmony_ci 2467db96d56Sopenharmony_ci def endelement(self, tag): 2477db96d56Sopenharmony_ci method = getattr(self, 'end_'+tag, None) 2487db96d56Sopenharmony_ci if method: 2497db96d56Sopenharmony_ci method("".join(self.texts)) 2507db96d56Sopenharmony_ci 2517db96d56Sopenharmony_ci def start_cell(self, attrs): 2527db96d56Sopenharmony_ci self.y = int(attrs.get("row")) 2537db96d56Sopenharmony_ci self.x = int(attrs.get("col")) 2547db96d56Sopenharmony_ci 2557db96d56Sopenharmony_ci def start_value(self, attrs): 2567db96d56Sopenharmony_ci self.fmt = attrs.get('format') 2577db96d56Sopenharmony_ci self.alignment = xml2align.get(attrs.get('align')) 2587db96d56Sopenharmony_ci 2597db96d56Sopenharmony_ci start_formula = start_value 2607db96d56Sopenharmony_ci 2617db96d56Sopenharmony_ci def end_int(self, text): 2627db96d56Sopenharmony_ci try: 2637db96d56Sopenharmony_ci self.value = int(text) 2647db96d56Sopenharmony_ci except (TypeError, ValueError): 2657db96d56Sopenharmony_ci self.value = None 2667db96d56Sopenharmony_ci 2677db96d56Sopenharmony_ci end_long = end_int 2687db96d56Sopenharmony_ci 2697db96d56Sopenharmony_ci def end_double(self, text): 2707db96d56Sopenharmony_ci try: 2717db96d56Sopenharmony_ci self.value = float(text) 2727db96d56Sopenharmony_ci except (TypeError, ValueError): 2737db96d56Sopenharmony_ci self.value = None 2747db96d56Sopenharmony_ci 2757db96d56Sopenharmony_ci def end_complex(self, text): 2767db96d56Sopenharmony_ci try: 2777db96d56Sopenharmony_ci self.value = complex(text) 2787db96d56Sopenharmony_ci except (TypeError, ValueError): 2797db96d56Sopenharmony_ci self.value = None 2807db96d56Sopenharmony_ci 2817db96d56Sopenharmony_ci def end_string(self, text): 2827db96d56Sopenharmony_ci self.value = text 2837db96d56Sopenharmony_ci 2847db96d56Sopenharmony_ci def end_value(self, text): 2857db96d56Sopenharmony_ci if isinstance(self.value, BaseCell): 2867db96d56Sopenharmony_ci self.cell = self.value 2877db96d56Sopenharmony_ci elif isinstance(self.value, str): 2887db96d56Sopenharmony_ci self.cell = StringCell(self.value, 2897db96d56Sopenharmony_ci self.fmt or "%s", 2907db96d56Sopenharmony_ci self.alignment or LEFT) 2917db96d56Sopenharmony_ci else: 2927db96d56Sopenharmony_ci self.cell = NumericCell(self.value, 2937db96d56Sopenharmony_ci self.fmt or "%s", 2947db96d56Sopenharmony_ci self.alignment or RIGHT) 2957db96d56Sopenharmony_ci 2967db96d56Sopenharmony_ci def end_formula(self, text): 2977db96d56Sopenharmony_ci self.cell = FormulaCell(text, 2987db96d56Sopenharmony_ci self.fmt or "%s", 2997db96d56Sopenharmony_ci self.alignment or RIGHT) 3007db96d56Sopenharmony_ci 3017db96d56Sopenharmony_ci def end_cell(self, text): 3027db96d56Sopenharmony_ci self.sheet.setcell(self.x, self.y, self.cell) 3037db96d56Sopenharmony_ci 3047db96d56Sopenharmony_ciclass BaseCell: 3057db96d56Sopenharmony_ci __init__ = None # Must provide 3067db96d56Sopenharmony_ci """Abstract base class for sheet cells. 3077db96d56Sopenharmony_ci 3087db96d56Sopenharmony_ci Subclasses may but needn't provide the following APIs: 3097db96d56Sopenharmony_ci 3107db96d56Sopenharmony_ci cell.reset() -- prepare for recalculation 3117db96d56Sopenharmony_ci cell.recalc(ns) -> value -- recalculate formula 3127db96d56Sopenharmony_ci cell.format() -> (value, alignment) -- return formatted value 3137db96d56Sopenharmony_ci cell.xml() -> string -- return XML 3147db96d56Sopenharmony_ci """ 3157db96d56Sopenharmony_ci 3167db96d56Sopenharmony_ciclass NumericCell(BaseCell): 3177db96d56Sopenharmony_ci 3187db96d56Sopenharmony_ci def __init__(self, value, fmt="%s", alignment=RIGHT): 3197db96d56Sopenharmony_ci assert isinstance(value, (int, float, complex)) 3207db96d56Sopenharmony_ci assert alignment in (LEFT, CENTER, RIGHT) 3217db96d56Sopenharmony_ci self.value = value 3227db96d56Sopenharmony_ci self.fmt = fmt 3237db96d56Sopenharmony_ci self.alignment = alignment 3247db96d56Sopenharmony_ci 3257db96d56Sopenharmony_ci def recalc(self, ns): 3267db96d56Sopenharmony_ci return self.value 3277db96d56Sopenharmony_ci 3287db96d56Sopenharmony_ci def format(self): 3297db96d56Sopenharmony_ci try: 3307db96d56Sopenharmony_ci text = self.fmt % self.value 3317db96d56Sopenharmony_ci except: 3327db96d56Sopenharmony_ci text = str(self.value) 3337db96d56Sopenharmony_ci return text, self.alignment 3347db96d56Sopenharmony_ci 3357db96d56Sopenharmony_ci def xml(self): 3367db96d56Sopenharmony_ci method = getattr(self, '_xml_' + type(self.value).__name__) 3377db96d56Sopenharmony_ci return '<value align="%s" format="%s">%s</value>' % ( 3387db96d56Sopenharmony_ci align2xml[self.alignment], 3397db96d56Sopenharmony_ci self.fmt, 3407db96d56Sopenharmony_ci method()) 3417db96d56Sopenharmony_ci 3427db96d56Sopenharmony_ci def _xml_int(self): 3437db96d56Sopenharmony_ci if -2**31 <= self.value < 2**31: 3447db96d56Sopenharmony_ci return '<int>%s</int>' % self.value 3457db96d56Sopenharmony_ci else: 3467db96d56Sopenharmony_ci return '<long>%s</long>' % self.value 3477db96d56Sopenharmony_ci 3487db96d56Sopenharmony_ci def _xml_float(self): 3497db96d56Sopenharmony_ci return '<double>%r</double>' % self.value 3507db96d56Sopenharmony_ci 3517db96d56Sopenharmony_ci def _xml_complex(self): 3527db96d56Sopenharmony_ci return '<complex>%r</complex>' % self.value 3537db96d56Sopenharmony_ci 3547db96d56Sopenharmony_ciclass StringCell(BaseCell): 3557db96d56Sopenharmony_ci 3567db96d56Sopenharmony_ci def __init__(self, text, fmt="%s", alignment=LEFT): 3577db96d56Sopenharmony_ci assert isinstance(text, str) 3587db96d56Sopenharmony_ci assert alignment in (LEFT, CENTER, RIGHT) 3597db96d56Sopenharmony_ci self.text = text 3607db96d56Sopenharmony_ci self.fmt = fmt 3617db96d56Sopenharmony_ci self.alignment = alignment 3627db96d56Sopenharmony_ci 3637db96d56Sopenharmony_ci def recalc(self, ns): 3647db96d56Sopenharmony_ci return self.text 3657db96d56Sopenharmony_ci 3667db96d56Sopenharmony_ci def format(self): 3677db96d56Sopenharmony_ci return self.text, self.alignment 3687db96d56Sopenharmony_ci 3697db96d56Sopenharmony_ci def xml(self): 3707db96d56Sopenharmony_ci s = '<value align="%s" format="%s"><string>%s</string></value>' 3717db96d56Sopenharmony_ci return s % ( 3727db96d56Sopenharmony_ci align2xml[self.alignment], 3737db96d56Sopenharmony_ci self.fmt, 3747db96d56Sopenharmony_ci escape(self.text)) 3757db96d56Sopenharmony_ci 3767db96d56Sopenharmony_ciclass FormulaCell(BaseCell): 3777db96d56Sopenharmony_ci 3787db96d56Sopenharmony_ci def __init__(self, formula, fmt="%s", alignment=RIGHT): 3797db96d56Sopenharmony_ci assert alignment in (LEFT, CENTER, RIGHT) 3807db96d56Sopenharmony_ci self.formula = formula 3817db96d56Sopenharmony_ci self.translated = translate(self.formula) 3827db96d56Sopenharmony_ci self.fmt = fmt 3837db96d56Sopenharmony_ci self.alignment = alignment 3847db96d56Sopenharmony_ci self.reset() 3857db96d56Sopenharmony_ci 3867db96d56Sopenharmony_ci def reset(self): 3877db96d56Sopenharmony_ci self.value = None 3887db96d56Sopenharmony_ci 3897db96d56Sopenharmony_ci def recalc(self, ns): 3907db96d56Sopenharmony_ci if self.value is None: 3917db96d56Sopenharmony_ci try: 3927db96d56Sopenharmony_ci self.value = eval(self.translated, ns) 3937db96d56Sopenharmony_ci except: 3947db96d56Sopenharmony_ci exc = sys.exc_info()[0] 3957db96d56Sopenharmony_ci if hasattr(exc, "__name__"): 3967db96d56Sopenharmony_ci self.value = exc.__name__ 3977db96d56Sopenharmony_ci else: 3987db96d56Sopenharmony_ci self.value = str(exc) 3997db96d56Sopenharmony_ci return self.value 4007db96d56Sopenharmony_ci 4017db96d56Sopenharmony_ci def format(self): 4027db96d56Sopenharmony_ci try: 4037db96d56Sopenharmony_ci text = self.fmt % self.value 4047db96d56Sopenharmony_ci except: 4057db96d56Sopenharmony_ci text = str(self.value) 4067db96d56Sopenharmony_ci return text, self.alignment 4077db96d56Sopenharmony_ci 4087db96d56Sopenharmony_ci def xml(self): 4097db96d56Sopenharmony_ci return '<formula align="%s" format="%s">%s</formula>' % ( 4107db96d56Sopenharmony_ci align2xml[self.alignment], 4117db96d56Sopenharmony_ci self.fmt, 4127db96d56Sopenharmony_ci escape(self.formula)) 4137db96d56Sopenharmony_ci 4147db96d56Sopenharmony_ci def renumber(self, x1, y1, x2, y2, dx, dy): 4157db96d56Sopenharmony_ci out = [] 4167db96d56Sopenharmony_ci for part in re.split(r'(\w+)', self.formula): 4177db96d56Sopenharmony_ci m = re.match('^([A-Z]+)([1-9][0-9]*)$', part) 4187db96d56Sopenharmony_ci if m is not None: 4197db96d56Sopenharmony_ci sx, sy = m.groups() 4207db96d56Sopenharmony_ci x = colname2num(sx) 4217db96d56Sopenharmony_ci y = int(sy) 4227db96d56Sopenharmony_ci if x1 <= x <= x2 and y1 <= y <= y2: 4237db96d56Sopenharmony_ci part = cellname(x+dx, y+dy) 4247db96d56Sopenharmony_ci out.append(part) 4257db96d56Sopenharmony_ci return FormulaCell("".join(out), self.fmt, self.alignment) 4267db96d56Sopenharmony_ci 4277db96d56Sopenharmony_cidef translate(formula): 4287db96d56Sopenharmony_ci """Translate a formula containing fancy cell names to valid Python code. 4297db96d56Sopenharmony_ci 4307db96d56Sopenharmony_ci Examples: 4317db96d56Sopenharmony_ci B4 -> cell(2, 4) 4327db96d56Sopenharmony_ci B4:Z100 -> cells(2, 4, 26, 100) 4337db96d56Sopenharmony_ci """ 4347db96d56Sopenharmony_ci out = [] 4357db96d56Sopenharmony_ci for part in re.split(r"(\w+(?::\w+)?)", formula): 4367db96d56Sopenharmony_ci m = re.match(r"^([A-Z]+)([1-9][0-9]*)(?::([A-Z]+)([1-9][0-9]*))?$", part) 4377db96d56Sopenharmony_ci if m is None: 4387db96d56Sopenharmony_ci out.append(part) 4397db96d56Sopenharmony_ci else: 4407db96d56Sopenharmony_ci x1, y1, x2, y2 = m.groups() 4417db96d56Sopenharmony_ci x1 = colname2num(x1) 4427db96d56Sopenharmony_ci if x2 is None: 4437db96d56Sopenharmony_ci s = "cell(%s, %s)" % (x1, y1) 4447db96d56Sopenharmony_ci else: 4457db96d56Sopenharmony_ci x2 = colname2num(x2) 4467db96d56Sopenharmony_ci s = "cells(%s, %s, %s, %s)" % (x1, y1, x2, y2) 4477db96d56Sopenharmony_ci out.append(s) 4487db96d56Sopenharmony_ci return "".join(out) 4497db96d56Sopenharmony_ci 4507db96d56Sopenharmony_cidef cellname(x, y): 4517db96d56Sopenharmony_ci "Translate a cell coordinate to a fancy cell name (e.g. (1, 1)->'A1')." 4527db96d56Sopenharmony_ci assert x > 0 # Column 0 has an empty name, so can't use that 4537db96d56Sopenharmony_ci return colnum2name(x) + str(y) 4547db96d56Sopenharmony_ci 4557db96d56Sopenharmony_cidef colname2num(s): 4567db96d56Sopenharmony_ci "Translate a column name to number (e.g. 'A'->1, 'Z'->26, 'AA'->27)." 4577db96d56Sopenharmony_ci s = s.upper() 4587db96d56Sopenharmony_ci n = 0 4597db96d56Sopenharmony_ci for c in s: 4607db96d56Sopenharmony_ci assert 'A' <= c <= 'Z' 4617db96d56Sopenharmony_ci n = n*26 + ord(c) - ord('A') + 1 4627db96d56Sopenharmony_ci return n 4637db96d56Sopenharmony_ci 4647db96d56Sopenharmony_cidef colnum2name(n): 4657db96d56Sopenharmony_ci "Translate a column number to name (e.g. 1->'A', etc.)." 4667db96d56Sopenharmony_ci assert n > 0 4677db96d56Sopenharmony_ci s = "" 4687db96d56Sopenharmony_ci while n: 4697db96d56Sopenharmony_ci n, m = divmod(n-1, 26) 4707db96d56Sopenharmony_ci s = chr(m+ord('A')) + s 4717db96d56Sopenharmony_ci return s 4727db96d56Sopenharmony_ci 4737db96d56Sopenharmony_ciimport tkinter as Tk 4747db96d56Sopenharmony_ci 4757db96d56Sopenharmony_ciclass SheetGUI: 4767db96d56Sopenharmony_ci 4777db96d56Sopenharmony_ci """Beginnings of a GUI for a spreadsheet. 4787db96d56Sopenharmony_ci 4797db96d56Sopenharmony_ci TO DO: 4807db96d56Sopenharmony_ci - clear multiple cells 4817db96d56Sopenharmony_ci - Insert, clear, remove rows or columns 4827db96d56Sopenharmony_ci - Show new contents while typing 4837db96d56Sopenharmony_ci - Scroll bars 4847db96d56Sopenharmony_ci - Grow grid when window is grown 4857db96d56Sopenharmony_ci - Proper menus 4867db96d56Sopenharmony_ci - Undo, redo 4877db96d56Sopenharmony_ci - Cut, copy and paste 4887db96d56Sopenharmony_ci - Formatting and alignment 4897db96d56Sopenharmony_ci """ 4907db96d56Sopenharmony_ci 4917db96d56Sopenharmony_ci def __init__(self, filename="sheet1.xml", rows=10, columns=5): 4927db96d56Sopenharmony_ci """Constructor. 4937db96d56Sopenharmony_ci 4947db96d56Sopenharmony_ci Load the sheet from the filename argument. 4957db96d56Sopenharmony_ci Set up the Tk widget tree. 4967db96d56Sopenharmony_ci """ 4977db96d56Sopenharmony_ci # Create and load the sheet 4987db96d56Sopenharmony_ci self.filename = filename 4997db96d56Sopenharmony_ci self.sheet = Sheet() 5007db96d56Sopenharmony_ci if os.path.isfile(filename): 5017db96d56Sopenharmony_ci self.sheet.load(filename) 5027db96d56Sopenharmony_ci # Calculate the needed grid size 5037db96d56Sopenharmony_ci maxx, maxy = self.sheet.getsize() 5047db96d56Sopenharmony_ci rows = max(rows, maxy) 5057db96d56Sopenharmony_ci columns = max(columns, maxx) 5067db96d56Sopenharmony_ci # Create the widgets 5077db96d56Sopenharmony_ci self.root = Tk.Tk() 5087db96d56Sopenharmony_ci self.root.wm_title("Spreadsheet: %s" % self.filename) 5097db96d56Sopenharmony_ci self.beacon = Tk.Label(self.root, text="A1", 5107db96d56Sopenharmony_ci font=('helvetica', 16, 'bold')) 5117db96d56Sopenharmony_ci self.entry = Tk.Entry(self.root) 5127db96d56Sopenharmony_ci self.savebutton = Tk.Button(self.root, text="Save", 5137db96d56Sopenharmony_ci command=self.save) 5147db96d56Sopenharmony_ci self.cellgrid = Tk.Frame(self.root) 5157db96d56Sopenharmony_ci # Configure the widget lay-out 5167db96d56Sopenharmony_ci self.cellgrid.pack(side="bottom", expand=1, fill="both") 5177db96d56Sopenharmony_ci self.beacon.pack(side="left") 5187db96d56Sopenharmony_ci self.savebutton.pack(side="right") 5197db96d56Sopenharmony_ci self.entry.pack(side="left", expand=1, fill="x") 5207db96d56Sopenharmony_ci # Bind some events 5217db96d56Sopenharmony_ci self.entry.bind("<Return>", self.return_event) 5227db96d56Sopenharmony_ci self.entry.bind("<Shift-Return>", self.shift_return_event) 5237db96d56Sopenharmony_ci self.entry.bind("<Tab>", self.tab_event) 5247db96d56Sopenharmony_ci self.entry.bind("<Shift-Tab>", self.shift_tab_event) 5257db96d56Sopenharmony_ci self.entry.bind("<Delete>", self.delete_event) 5267db96d56Sopenharmony_ci self.entry.bind("<Escape>", self.escape_event) 5277db96d56Sopenharmony_ci # Now create the cell grid 5287db96d56Sopenharmony_ci self.makegrid(rows, columns) 5297db96d56Sopenharmony_ci # Select the top-left cell 5307db96d56Sopenharmony_ci self.currentxy = None 5317db96d56Sopenharmony_ci self.cornerxy = None 5327db96d56Sopenharmony_ci self.setcurrent(1, 1) 5337db96d56Sopenharmony_ci # Copy the sheet cells to the GUI cells 5347db96d56Sopenharmony_ci self.sync() 5357db96d56Sopenharmony_ci 5367db96d56Sopenharmony_ci def delete_event(self, event): 5377db96d56Sopenharmony_ci if self.cornerxy != self.currentxy and self.cornerxy is not None: 5387db96d56Sopenharmony_ci self.sheet.clearcells(*(self.currentxy + self.cornerxy)) 5397db96d56Sopenharmony_ci else: 5407db96d56Sopenharmony_ci self.sheet.clearcell(*self.currentxy) 5417db96d56Sopenharmony_ci self.sync() 5427db96d56Sopenharmony_ci self.entry.delete(0, 'end') 5437db96d56Sopenharmony_ci return "break" 5447db96d56Sopenharmony_ci 5457db96d56Sopenharmony_ci def escape_event(self, event): 5467db96d56Sopenharmony_ci x, y = self.currentxy 5477db96d56Sopenharmony_ci self.load_entry(x, y) 5487db96d56Sopenharmony_ci 5497db96d56Sopenharmony_ci def load_entry(self, x, y): 5507db96d56Sopenharmony_ci cell = self.sheet.getcell(x, y) 5517db96d56Sopenharmony_ci if cell is None: 5527db96d56Sopenharmony_ci text = "" 5537db96d56Sopenharmony_ci elif isinstance(cell, FormulaCell): 5547db96d56Sopenharmony_ci text = '=' + cell.formula 5557db96d56Sopenharmony_ci else: 5567db96d56Sopenharmony_ci text, alignment = cell.format() 5577db96d56Sopenharmony_ci self.entry.delete(0, 'end') 5587db96d56Sopenharmony_ci self.entry.insert(0, text) 5597db96d56Sopenharmony_ci self.entry.selection_range(0, 'end') 5607db96d56Sopenharmony_ci 5617db96d56Sopenharmony_ci def makegrid(self, rows, columns): 5627db96d56Sopenharmony_ci """Helper to create the grid of GUI cells. 5637db96d56Sopenharmony_ci 5647db96d56Sopenharmony_ci The edge (x==0 or y==0) is filled with labels; the rest is real cells. 5657db96d56Sopenharmony_ci """ 5667db96d56Sopenharmony_ci self.rows = rows 5677db96d56Sopenharmony_ci self.columns = columns 5687db96d56Sopenharmony_ci self.gridcells = {} 5697db96d56Sopenharmony_ci # Create the top left corner cell (which selects all) 5707db96d56Sopenharmony_ci cell = Tk.Label(self.cellgrid, relief='raised') 5717db96d56Sopenharmony_ci cell.grid_configure(column=0, row=0, sticky='NSWE') 5727db96d56Sopenharmony_ci cell.bind("<ButtonPress-1>", self.selectall) 5737db96d56Sopenharmony_ci # Create the top row of labels, and configure the grid columns 5747db96d56Sopenharmony_ci for x in range(1, columns+1): 5757db96d56Sopenharmony_ci self.cellgrid.grid_columnconfigure(x, minsize=64) 5767db96d56Sopenharmony_ci cell = Tk.Label(self.cellgrid, text=colnum2name(x), relief='raised') 5777db96d56Sopenharmony_ci cell.grid_configure(column=x, row=0, sticky='WE') 5787db96d56Sopenharmony_ci self.gridcells[x, 0] = cell 5797db96d56Sopenharmony_ci cell.__x = x 5807db96d56Sopenharmony_ci cell.__y = 0 5817db96d56Sopenharmony_ci cell.bind("<ButtonPress-1>", self.selectcolumn) 5827db96d56Sopenharmony_ci cell.bind("<B1-Motion>", self.extendcolumn) 5837db96d56Sopenharmony_ci cell.bind("<ButtonRelease-1>", self.extendcolumn) 5847db96d56Sopenharmony_ci cell.bind("<Shift-Button-1>", self.extendcolumn) 5857db96d56Sopenharmony_ci # Create the leftmost column of labels 5867db96d56Sopenharmony_ci for y in range(1, rows+1): 5877db96d56Sopenharmony_ci cell = Tk.Label(self.cellgrid, text=str(y), relief='raised') 5887db96d56Sopenharmony_ci cell.grid_configure(column=0, row=y, sticky='WE') 5897db96d56Sopenharmony_ci self.gridcells[0, y] = cell 5907db96d56Sopenharmony_ci cell.__x = 0 5917db96d56Sopenharmony_ci cell.__y = y 5927db96d56Sopenharmony_ci cell.bind("<ButtonPress-1>", self.selectrow) 5937db96d56Sopenharmony_ci cell.bind("<B1-Motion>", self.extendrow) 5947db96d56Sopenharmony_ci cell.bind("<ButtonRelease-1>", self.extendrow) 5957db96d56Sopenharmony_ci cell.bind("<Shift-Button-1>", self.extendrow) 5967db96d56Sopenharmony_ci # Create the real cells 5977db96d56Sopenharmony_ci for x in range(1, columns+1): 5987db96d56Sopenharmony_ci for y in range(1, rows+1): 5997db96d56Sopenharmony_ci cell = Tk.Label(self.cellgrid, relief='sunken', 6007db96d56Sopenharmony_ci bg='white', fg='black') 6017db96d56Sopenharmony_ci cell.grid_configure(column=x, row=y, sticky='NSWE') 6027db96d56Sopenharmony_ci self.gridcells[x, y] = cell 6037db96d56Sopenharmony_ci cell.__x = x 6047db96d56Sopenharmony_ci cell.__y = y 6057db96d56Sopenharmony_ci # Bind mouse events 6067db96d56Sopenharmony_ci cell.bind("<ButtonPress-1>", self.press) 6077db96d56Sopenharmony_ci cell.bind("<B1-Motion>", self.motion) 6087db96d56Sopenharmony_ci cell.bind("<ButtonRelease-1>", self.release) 6097db96d56Sopenharmony_ci cell.bind("<Shift-Button-1>", self.release) 6107db96d56Sopenharmony_ci 6117db96d56Sopenharmony_ci def selectall(self, event): 6127db96d56Sopenharmony_ci self.setcurrent(1, 1) 6137db96d56Sopenharmony_ci self.setcorner(sys.maxsize, sys.maxsize) 6147db96d56Sopenharmony_ci 6157db96d56Sopenharmony_ci def selectcolumn(self, event): 6167db96d56Sopenharmony_ci x, y = self.whichxy(event) 6177db96d56Sopenharmony_ci self.setcurrent(x, 1) 6187db96d56Sopenharmony_ci self.setcorner(x, sys.maxsize) 6197db96d56Sopenharmony_ci 6207db96d56Sopenharmony_ci def extendcolumn(self, event): 6217db96d56Sopenharmony_ci x, y = self.whichxy(event) 6227db96d56Sopenharmony_ci if x > 0: 6237db96d56Sopenharmony_ci self.setcurrent(self.currentxy[0], 1) 6247db96d56Sopenharmony_ci self.setcorner(x, sys.maxsize) 6257db96d56Sopenharmony_ci 6267db96d56Sopenharmony_ci def selectrow(self, event): 6277db96d56Sopenharmony_ci x, y = self.whichxy(event) 6287db96d56Sopenharmony_ci self.setcurrent(1, y) 6297db96d56Sopenharmony_ci self.setcorner(sys.maxsize, y) 6307db96d56Sopenharmony_ci 6317db96d56Sopenharmony_ci def extendrow(self, event): 6327db96d56Sopenharmony_ci x, y = self.whichxy(event) 6337db96d56Sopenharmony_ci if y > 0: 6347db96d56Sopenharmony_ci self.setcurrent(1, self.currentxy[1]) 6357db96d56Sopenharmony_ci self.setcorner(sys.maxsize, y) 6367db96d56Sopenharmony_ci 6377db96d56Sopenharmony_ci def press(self, event): 6387db96d56Sopenharmony_ci x, y = self.whichxy(event) 6397db96d56Sopenharmony_ci if x > 0 and y > 0: 6407db96d56Sopenharmony_ci self.setcurrent(x, y) 6417db96d56Sopenharmony_ci 6427db96d56Sopenharmony_ci def motion(self, event): 6437db96d56Sopenharmony_ci x, y = self.whichxy(event) 6447db96d56Sopenharmony_ci if x > 0 and y > 0: 6457db96d56Sopenharmony_ci self.setcorner(x, y) 6467db96d56Sopenharmony_ci 6477db96d56Sopenharmony_ci release = motion 6487db96d56Sopenharmony_ci 6497db96d56Sopenharmony_ci def whichxy(self, event): 6507db96d56Sopenharmony_ci w = self.cellgrid.winfo_containing(event.x_root, event.y_root) 6517db96d56Sopenharmony_ci if w is not None and isinstance(w, Tk.Label): 6527db96d56Sopenharmony_ci try: 6537db96d56Sopenharmony_ci return w.__x, w.__y 6547db96d56Sopenharmony_ci except AttributeError: 6557db96d56Sopenharmony_ci pass 6567db96d56Sopenharmony_ci return 0, 0 6577db96d56Sopenharmony_ci 6587db96d56Sopenharmony_ci def save(self): 6597db96d56Sopenharmony_ci self.sheet.save(self.filename) 6607db96d56Sopenharmony_ci 6617db96d56Sopenharmony_ci def setcurrent(self, x, y): 6627db96d56Sopenharmony_ci "Make (x, y) the current cell." 6637db96d56Sopenharmony_ci if self.currentxy is not None: 6647db96d56Sopenharmony_ci self.change_cell() 6657db96d56Sopenharmony_ci self.clearfocus() 6667db96d56Sopenharmony_ci self.beacon['text'] = cellname(x, y) 6677db96d56Sopenharmony_ci self.load_entry(x, y) 6687db96d56Sopenharmony_ci self.entry.focus_set() 6697db96d56Sopenharmony_ci self.currentxy = x, y 6707db96d56Sopenharmony_ci self.cornerxy = None 6717db96d56Sopenharmony_ci gridcell = self.gridcells.get(self.currentxy) 6727db96d56Sopenharmony_ci if gridcell is not None: 6737db96d56Sopenharmony_ci gridcell['bg'] = 'yellow' 6747db96d56Sopenharmony_ci 6757db96d56Sopenharmony_ci def setcorner(self, x, y): 6767db96d56Sopenharmony_ci if self.currentxy is None or self.currentxy == (x, y): 6777db96d56Sopenharmony_ci self.setcurrent(x, y) 6787db96d56Sopenharmony_ci return 6797db96d56Sopenharmony_ci self.clearfocus() 6807db96d56Sopenharmony_ci self.cornerxy = x, y 6817db96d56Sopenharmony_ci x1, y1 = self.currentxy 6827db96d56Sopenharmony_ci x2, y2 = self.cornerxy or self.currentxy 6837db96d56Sopenharmony_ci if x1 > x2: 6847db96d56Sopenharmony_ci x1, x2 = x2, x1 6857db96d56Sopenharmony_ci if y1 > y2: 6867db96d56Sopenharmony_ci y1, y2 = y2, y1 6877db96d56Sopenharmony_ci for (x, y), cell in self.gridcells.items(): 6887db96d56Sopenharmony_ci if x1 <= x <= x2 and y1 <= y <= y2: 6897db96d56Sopenharmony_ci cell['bg'] = 'lightBlue' 6907db96d56Sopenharmony_ci gridcell = self.gridcells.get(self.currentxy) 6917db96d56Sopenharmony_ci if gridcell is not None: 6927db96d56Sopenharmony_ci gridcell['bg'] = 'yellow' 6937db96d56Sopenharmony_ci self.setbeacon(x1, y1, x2, y2) 6947db96d56Sopenharmony_ci 6957db96d56Sopenharmony_ci def setbeacon(self, x1, y1, x2, y2): 6967db96d56Sopenharmony_ci if x1 == y1 == 1 and x2 == y2 == sys.maxsize: 6977db96d56Sopenharmony_ci name = ":" 6987db96d56Sopenharmony_ci elif (x1, x2) == (1, sys.maxsize): 6997db96d56Sopenharmony_ci if y1 == y2: 7007db96d56Sopenharmony_ci name = "%d" % y1 7017db96d56Sopenharmony_ci else: 7027db96d56Sopenharmony_ci name = "%d:%d" % (y1, y2) 7037db96d56Sopenharmony_ci elif (y1, y2) == (1, sys.maxsize): 7047db96d56Sopenharmony_ci if x1 == x2: 7057db96d56Sopenharmony_ci name = "%s" % colnum2name(x1) 7067db96d56Sopenharmony_ci else: 7077db96d56Sopenharmony_ci name = "%s:%s" % (colnum2name(x1), colnum2name(x2)) 7087db96d56Sopenharmony_ci else: 7097db96d56Sopenharmony_ci name1 = cellname(*self.currentxy) 7107db96d56Sopenharmony_ci name2 = cellname(*self.cornerxy) 7117db96d56Sopenharmony_ci name = "%s:%s" % (name1, name2) 7127db96d56Sopenharmony_ci self.beacon['text'] = name 7137db96d56Sopenharmony_ci 7147db96d56Sopenharmony_ci 7157db96d56Sopenharmony_ci def clearfocus(self): 7167db96d56Sopenharmony_ci if self.currentxy is not None: 7177db96d56Sopenharmony_ci x1, y1 = self.currentxy 7187db96d56Sopenharmony_ci x2, y2 = self.cornerxy or self.currentxy 7197db96d56Sopenharmony_ci if x1 > x2: 7207db96d56Sopenharmony_ci x1, x2 = x2, x1 7217db96d56Sopenharmony_ci if y1 > y2: 7227db96d56Sopenharmony_ci y1, y2 = y2, y1 7237db96d56Sopenharmony_ci for (x, y), cell in self.gridcells.items(): 7247db96d56Sopenharmony_ci if x1 <= x <= x2 and y1 <= y <= y2: 7257db96d56Sopenharmony_ci cell['bg'] = 'white' 7267db96d56Sopenharmony_ci 7277db96d56Sopenharmony_ci def return_event(self, event): 7287db96d56Sopenharmony_ci "Callback for the Return key." 7297db96d56Sopenharmony_ci self.change_cell() 7307db96d56Sopenharmony_ci x, y = self.currentxy 7317db96d56Sopenharmony_ci self.setcurrent(x, y+1) 7327db96d56Sopenharmony_ci return "break" 7337db96d56Sopenharmony_ci 7347db96d56Sopenharmony_ci def shift_return_event(self, event): 7357db96d56Sopenharmony_ci "Callback for the Return key with Shift modifier." 7367db96d56Sopenharmony_ci self.change_cell() 7377db96d56Sopenharmony_ci x, y = self.currentxy 7387db96d56Sopenharmony_ci self.setcurrent(x, max(1, y-1)) 7397db96d56Sopenharmony_ci return "break" 7407db96d56Sopenharmony_ci 7417db96d56Sopenharmony_ci def tab_event(self, event): 7427db96d56Sopenharmony_ci "Callback for the Tab key." 7437db96d56Sopenharmony_ci self.change_cell() 7447db96d56Sopenharmony_ci x, y = self.currentxy 7457db96d56Sopenharmony_ci self.setcurrent(x+1, y) 7467db96d56Sopenharmony_ci return "break" 7477db96d56Sopenharmony_ci 7487db96d56Sopenharmony_ci def shift_tab_event(self, event): 7497db96d56Sopenharmony_ci "Callback for the Tab key with Shift modifier." 7507db96d56Sopenharmony_ci self.change_cell() 7517db96d56Sopenharmony_ci x, y = self.currentxy 7527db96d56Sopenharmony_ci self.setcurrent(max(1, x-1), y) 7537db96d56Sopenharmony_ci return "break" 7547db96d56Sopenharmony_ci 7557db96d56Sopenharmony_ci def change_cell(self): 7567db96d56Sopenharmony_ci "Set the current cell from the entry widget." 7577db96d56Sopenharmony_ci x, y = self.currentxy 7587db96d56Sopenharmony_ci text = self.entry.get() 7597db96d56Sopenharmony_ci cell = None 7607db96d56Sopenharmony_ci if text.startswith('='): 7617db96d56Sopenharmony_ci cell = FormulaCell(text[1:]) 7627db96d56Sopenharmony_ci else: 7637db96d56Sopenharmony_ci for cls in int, float, complex: 7647db96d56Sopenharmony_ci try: 7657db96d56Sopenharmony_ci value = cls(text) 7667db96d56Sopenharmony_ci except (TypeError, ValueError): 7677db96d56Sopenharmony_ci continue 7687db96d56Sopenharmony_ci else: 7697db96d56Sopenharmony_ci cell = NumericCell(value) 7707db96d56Sopenharmony_ci break 7717db96d56Sopenharmony_ci if cell is None and text: 7727db96d56Sopenharmony_ci cell = StringCell(text) 7737db96d56Sopenharmony_ci if cell is None: 7747db96d56Sopenharmony_ci self.sheet.clearcell(x, y) 7757db96d56Sopenharmony_ci else: 7767db96d56Sopenharmony_ci self.sheet.setcell(x, y, cell) 7777db96d56Sopenharmony_ci self.sync() 7787db96d56Sopenharmony_ci 7797db96d56Sopenharmony_ci def sync(self): 7807db96d56Sopenharmony_ci "Fill the GUI cells from the sheet cells." 7817db96d56Sopenharmony_ci self.sheet.recalc() 7827db96d56Sopenharmony_ci for (x, y), gridcell in self.gridcells.items(): 7837db96d56Sopenharmony_ci if x == 0 or y == 0: 7847db96d56Sopenharmony_ci continue 7857db96d56Sopenharmony_ci cell = self.sheet.getcell(x, y) 7867db96d56Sopenharmony_ci if cell is None: 7877db96d56Sopenharmony_ci gridcell['text'] = "" 7887db96d56Sopenharmony_ci else: 7897db96d56Sopenharmony_ci if hasattr(cell, 'format'): 7907db96d56Sopenharmony_ci text, alignment = cell.format() 7917db96d56Sopenharmony_ci else: 7927db96d56Sopenharmony_ci text, alignment = str(cell), LEFT 7937db96d56Sopenharmony_ci gridcell['text'] = text 7947db96d56Sopenharmony_ci gridcell['anchor'] = align2anchor[alignment] 7957db96d56Sopenharmony_ci 7967db96d56Sopenharmony_ci 7977db96d56Sopenharmony_cidef test_basic(): 7987db96d56Sopenharmony_ci "Basic non-gui self-test." 7997db96d56Sopenharmony_ci a = Sheet() 8007db96d56Sopenharmony_ci for x in range(1, 11): 8017db96d56Sopenharmony_ci for y in range(1, 11): 8027db96d56Sopenharmony_ci if x == 1: 8037db96d56Sopenharmony_ci cell = NumericCell(y) 8047db96d56Sopenharmony_ci elif y == 1: 8057db96d56Sopenharmony_ci cell = NumericCell(x) 8067db96d56Sopenharmony_ci else: 8077db96d56Sopenharmony_ci c1 = cellname(x, 1) 8087db96d56Sopenharmony_ci c2 = cellname(1, y) 8097db96d56Sopenharmony_ci formula = "%s*%s" % (c1, c2) 8107db96d56Sopenharmony_ci cell = FormulaCell(formula) 8117db96d56Sopenharmony_ci a.setcell(x, y, cell) 8127db96d56Sopenharmony_ci## if os.path.isfile("sheet1.xml"): 8137db96d56Sopenharmony_ci## print "Loading from sheet1.xml" 8147db96d56Sopenharmony_ci## a.load("sheet1.xml") 8157db96d56Sopenharmony_ci a.display() 8167db96d56Sopenharmony_ci a.save("sheet1.xml") 8177db96d56Sopenharmony_ci 8187db96d56Sopenharmony_cidef test_gui(): 8197db96d56Sopenharmony_ci "GUI test." 8207db96d56Sopenharmony_ci if sys.argv[1:]: 8217db96d56Sopenharmony_ci filename = sys.argv[1] 8227db96d56Sopenharmony_ci else: 8237db96d56Sopenharmony_ci filename = "sheet1.xml" 8247db96d56Sopenharmony_ci g = SheetGUI(filename) 8257db96d56Sopenharmony_ci g.root.mainloop() 8267db96d56Sopenharmony_ci 8277db96d56Sopenharmony_ciif __name__ == '__main__': 8287db96d56Sopenharmony_ci #test_basic() 8297db96d56Sopenharmony_ci test_gui() 830