# -*-mode: tcl; fill-column: 75; tab-width: 8; coding: iso-latin-1-unix -*- # # $Id$ # """Wrapper functions for the TkTable widget. """ import Tkinter __version__ = "0.1" class ArrayVar: _names = {} def __init__(self, master=Tkinter._default_master): self._tk = master.tk self._name = "PYARRAY_%08X" % id(self) self._names[self._name] = self self._tk.eval("array set %s [ ]" % self._name) def __del__(self): del self._arrays[self._name] self._tk.eval("array unset %s" % self._name) def __str__(self): return self._name def __repr__(self): return '<%s @ 0x%08X>' % (self.__class__.__name__, id(self)) def _coords(self, coords): if type(coords) is not tuple: ValueError("Bad item coordinates %s: must be ," % str(coords)) return int(coords[0]), int(coords[1]) def __getitem__(self, coords): row, col = self._coords(coords) return self._tk.eval("return $%s(%d,%d)" % (self._name, row, col)) def __setitem__(self, coords, value): row, col = self._coords(coords) self._tk.eval("set %s(%d,%d) {%s}" % (self._name, row, col, value)) class Table(Tkinter.Widget): """Table(master, ...) -> widget Create a tk table widget. """ def __init__(self, master, cnf={}, **kw): # Load dynamic extension. master.tk.eval("package require Tktable") # Validate variable config option. try: if not isinstance(cnf["variable"], ArrayVar): raise ValueError("-variable argument must by an ArrayVar") cnf["variable"] = str(cnf["variable"]) except KeyError: pass except ValueError: raise try: if not isinstance(kw["variable"], ArrayVar): raise ValueError("-variable argument must be an ArrayVar") kw["variable"] = str(kw["variable"]) except KeyError: pass except ValueError: raise Tkinter.Widget.__init__(self, master, "table", cnf, kw) # Index positions. _indexes = { "active":0, "anchor":0, "end":0, "origin":0, "topleft":0, "bottomright":0 } def _cell_index(self, index): "Check the form of the cell index, to validate it." # If it's one of the predefined index positions, then it's OK. if index in _indexes: return index # If it's sequence of length 2, then it's OK. try: return "%d,%d" % (index[0], index[1]) except IndexError: raise ValueError("Bad table index '%s': must be sequence of length 2" % str(index)) # Check it it's a string of the form "@x,y": if index[:0] == '@' and index.find(",") > 0: return index raise ValueError("Bad table index '%s': must be active, anchor, end, origin, topleft, bottomright, @x,y, or ," % index) def activate(self, index): "Activate a certain cell." index = self._cell_index(index) self.tk.call(self._w, "activate", index) def bbox(self, start, end): "Return bounding box for specified cells, in pixel X,Y,width,height" start = self._cell_index(start) end = self._cell_index(end) box = self.tk.call(self._w, "bbox", start, end).split() box = map(float, box) return tuple(box) def border(self, subcmd, x, y, *options): r = self.tk.call(self._w, "border", subcmd, x, y) r.replace("{}", "") return tuple(r.split()) def border_mark(self, x, y): r = self.tk.call(self._w, "border", "mark", x, y) r.replace("{}", "") return tuple(r.split()) def border_dragto(self, x, y): self.tk.call(self._w, "border", "dragto", x, y) def clear(self, option, *cells): first = last = None if len(cells) == 1: first = self._cell_index(cells[0]) if len(cells) > 1: last = self._cell_index(cells[1]) self.tk.call(self._w, "clear", option, first, last) def clear_cache(self, *cells): self.clear("cache", *cells) def clear_sizes(self, *cells): self.clear("sizes", *cells) def clear_tags(self, *cells): self.clear("tags", *cells) def clear_all(self, *cells): self.clear("all", *cells) def curselection(self, value=None): "Query current selected cells, or set their value." if value is None: row, col = self.tk.call(self._w, "curselection") return int(row), int(col) self.tk.call(self._w, "curselection", value) def curvalue(self, value=None): "Set or get value of active cell." if value is None: return self.tk.call(self._w, "curvalue") self.tk.call(self._w, "curvalue", value) def delete(self, option, *args): "Delete something." self.tk.call(self._w, "delete", option, *args) def delete_active(self, indexes): "Delete text from active cell." index1 = index2 = None if len(indexes) > 1: self.tk.call(self._w. "delete", "active", indexes[0], indexes[1]) else: self.tk.call(self._w, "delete", "active", indexes[0]) def delete_rowscols(self, what, index1, index2, count, switches): "Delete either rows or columns." cmd = [ self._w, "delete", what ] cmd.extend(["-%s" % K for K in switches]) if index1 is not None: cmd.append(self._cell_index(index1)) if index2 is not None: cmd.append(self._cell_index(index2)) if count is not None: cmd.append(count) self.tk.call(*cmd) def delete_rows(self, index1, index2=None, count=1, **kw): self.delete_rowscols("rows", index1, index2, count, kw) def delete_cols(self, index1, index2=None, count=1, **kw): self.delete_rowscols("cols", index1, index2, count, kw) def get(self, *cells): index1 = index2 = None if len(cells) > 0: index1 = self._cell_index(cells[0]) if len(cells) > 1: index2 = self._cell_index(cells[1]) R = self.tk.call(self._w, "get", index1, index2).split() for i,r in zip(range(len(R)),R): if r == "{}": R[i] = "" return R def height(self, *values): if not values: # Return all rows with a height set. R = self.tk.call(self._w, "height") pairs = [] while R: left = R.find("{") right = R.find("}") pairs.append(R[left:right].split()) R = R[right:].strip() return [ (int(row),int(height)) for row,height in R ] if len(values) == 1: # Return height of specified row. return int(self.tk.call(self._w, "height", values[0])) # Set heights for various rows. cmd = [ self._w, "height" ] for i in range(len(values)/2): cmd.append(values[2*i]) # row cmd.append(values[2*i+1]) # height self.tk.call(*cmd) def hidden(self, index1=None, index2=None): if not index1 and not index2: # Return all hidden cells. R = self.tk.call(self._w, "hidden").split() R = [ index.split(",") for index in R ] return [ (int(row), int(col)) for row,col in R ] if index1 and not index2: # Return spanning cell covering this index. index1 = self._cell_index(index1) cell = self.tk.call(self._w, "hidden", index1) if cell: cell = tuple(map(int, cell.split(","))) else: cell = None return cell if index1 and index2: # Return 0 or 1 if there's a spanning cell covering the range. index1 = self._cell_index(index1) index2 = self._cell_index(index2) return int(self.tk.call(self._w, "hidden", index1, index2)) raise ValueError("Bad arguments: must specify start or start and finish") def icursor(self, *pos): "Set position of insertion cursor in the active cell." if not pos: pos = None return self.tk.call(self._w, "icursor", pos) def index(self, index, *rowcol): "Return the integer cell coordinate corresponding to the cell 'index'." index = self._cell_index(index) if rowcol: return int(self.tk.call(self._w, "index", index, rowcol[0])) R = self.tk.call(self,_w, "index", index).split(",") return int(R[0]), int(R[1]) def insert(self, options, *args): "Insert various things in the table." self.tk.call(self._w, "insert", options, *args) def insert_active(self, index, value): "Insert chars in the active cell." self.tk.call(self._w, "insert", "active", index, value) def insert_rowcols(self, what, index, count, switches): "Insert rows or columns." cmd = [ self._w, "insert", what ] cmd.extend(switches.keys()) cmd.append(self._cell_index(index)) if count is not None: cmd.append(count) self.tk.call(*cmd) def insert_rows(self, index, count=1, **kw): "Insert rows at the index." self.insert_rowscols("rows", index, count, kw) def insert_cols(self, index, count=1, **kw): "Insert cols at the index." self.insert_rowscols("cols", index, count, kw) def reread(self): "Reread the contents of the active cell." self.tk.call(self._w, "reread") def scan(self, option, *args): "Implement scanning on the table." self.tk.call(self._w, "scan", option, *args) def scan_mark(self, x, y): self.tk.call(self._w, "scan", "mark", x, y) def scan_dragto(self, x, y): self.tk.call(self._w, "scan", "dragto", x, y) def see(self, index): "Adjust view in the table to see the specified cell." index = self._cell_index(index) self.tk.call(self._w, "see", index) def selection(self, option, *args): "Command used to adjust the selection within the table." self.tk.call(self._w, "selection", option, *args) def selection_anchor(self, index): "Set the selection anchor to the cell given by index." index = self._cell_index(index) self.tk.call(self._w, "selection", "anchor", index) def selection_clear(self, index1, index2=None): "Remove the specified cell or range of cells from the selection." index1 = self._cell_index(index1) if index2: index = self._cell_index(index2) self.tk.call(self._w, "selection", "clear", index1, index2) else: self.tk.call(self._w, "selection", "clear", index1) def selection_includes(self, index): "Returns 1 if the selection includes the given index." index = self._cell_index(index) ok = self.tk.call(self._w, "selection", "includes", index) return int(ok) def selection_set(self, first, last=None): "Selects all of the cells in the range first-last, inclusive." first = self._cell_index(first) if last: last = self._cell_index(last) self.tk.call(self._w, "selection", "set", first, last) else: self.tk.call(self._w, "selection", "set", first) def spans(self, *args): "Query or set spanning cells." if not args: # return list of all cell spans. R = [] spans = self.tk.call(self._w, "spans").split() for i in range(len(spans)/2): start = tuple(map(int, spans[2*i].split(","))) finish = tuple(map(int, spans[2*i+1].split(","))) R.append((start,finish)) return R if len(args) == 1: # Return span of given index. index = self._cell_index(args[0]) span = self.tk.call(self._w, "spans", index).split(",") return int(span[0]), int(span[1]) if len(args) > 1: # Set spans of given indexes. cmd = [ self._w, "spans" ] for i in range(len(args)/2): cmd.append(self._cell_index(args[2*i])) cmd.append(self._cell_index(args[2*i+1])) self.tk.call(*cmd) def tag(self, option, tag, *args): "Query or set tags." self.tk.call(self._w, "tag", option, tag, *args) def tag_celltag(self, tag, *cells): "Query or set cell tags." if not cells: # Return list of cells having the tag. cells = self.tk.call(self._w, "tag", "celltag", tag).split() cells = [ tuple(map(int,rc.split(","))) for rc in cells ] return cells # Set a certain tag to a list of cells. cells = [ self._cell_index(index) for index in cells ] self.tk.call(self._w, "tag", "celltag", tag, *cells) def tag_cget(self, tag, option): "Query option of a tag." return self.tk.call(self._w, "tag", "cget", tag) def tag_coltag(self, tag, *cols): "Query or set column tags." if not cols: # Query which cols have a certain tag. cols = self.tk.call(self._w, "tag", "coltag", tag).split() return map(int, cols) # Set a list of columns to a tag. self.tk.call(self._w, "tag", "coltag", tag, *cols) def tag_configure(self, tag, **kw): "Configure a tag." opt = [ ] for k,v in kw.items(): opt.append("-%s" % k) if type(v) in (tuple,list): opt.extend(v) else: opt.append(v) self.tk.call(self._w, "tag", "configure", tag, *opt) def tag_delete(self, *tags): "Delete (one or more) tags." if tags: self.tk.call(self._w, "tag", "delete", *tags) def tag_exists(self, tag): "Returns boolean true if tag exists." return int( self.tk.call(self._w, "tag", "exists", tag) ) def tag_includes(self, tag, index): "Returns true if tag is included in cell 'index'" index = self._cell_index(index) return int( self.tk.call(self._w, "tag", "includes", tag, index) ) def tag_lower(self, *tags): "Lower priority of a tag." self.tk.call(self._w, "tag", "lower", *tags) def tag_names(self): "Returns a list of defined tags." return self.tk.call(self._w, "tag", "names").split() def tag_raise(self, *tags): "Raise priority of a tag." self.tk.call(self._w, "tag", "raise", *tags) def tag_rowtag(self, tag, *rows): "Query or set row tags." if not rows: # Query which rows have the tag. rows = self.tk.call(self._w, "tag", "rowtag", tag).split() return map(int, rows) # Define rows to have the tag. self.tk.call(self._w, "tag", "rowtag", *rows) def validate(self, index): "Validate a cell." index = self._cell_index(index) return int( self.tk.call(self._w, "validate", index) ) def version(self): "Return version of TkTable widget." return float( self.tk.call(self._w, "version") ) def window(self, option, *args): "Commands for embedding windows in cells." self.tk.call(self._w, "window", option, *args) def window_cget(self, index, option): "Query cell's window configuration." index = self._cell_index(index) conf = self.tk.call(self._w, "window", "cget", index, option) if conf.find("no window at index") > -1: raise AttributeError(conf) return conf def window_configure(self, index, *options): "Query or set window configuration." pass def window_create(self, index, widget): "Embed a widget in a window." self.tk.call(self._w, "window", "configure", "-create", widget._w) def window_delete(self, *cells): "Destroy embedded widgets." if cells: cells = [ self._cell_index(c) for c in cells ] self.tk.call(self._w, "window", "delete", *cells) def window_names(self): "Return a list of cells with embedded widgets." cells = self.tk.call(self._w, "window", "names").split() cells = [ tuple(map(int, cell.split(","))) for cell in cells ] return cells