1 # Copyright (c) 2008, Guilherme Polo
   2 # All rights reserved.
   3 #
   4 # Redistribution and use in source and binary forms, with or without
   5 # modification, are permitted provided that the following conditions are met:
   6 #
   7 #  * Redistributions of source code must retain the above copyright notice,
   8 #    this list of conditions and the following disclaimer.
   9 #  * Redistributions in binary form must reproduce the above copyright notice,
  10 #    this list of conditions and the following disclaimer in the documentation
  11 #    and/or other materials provided with the distribution.
  12 #
  13 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  14 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  15 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  16 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  17 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  18 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  19 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  20 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  21 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  22 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  23 # POSSIBILITY OF SUCH DAMAGE.
  24 
  25 """
  26 This contains a wrapper class for the tktable widget as well a class for using
  27 tcl arrays that are, in some instances, required by tktable.
  28 """
  29 
  30 __author__ = "Guilherme Polo <ggpolo@gmail.com>"
  31 
  32 __all__ = ["ArrayVar", "Table"]
  33 
  34 import Tkinter
  35 
  36 def _setup_master(master):
  37     if master is None:
  38         if Tkinter._support_default_root:
  39             master = Tkinter._default_root or Tkinter.Tk()
  40         else:
  41             raise RuntimeError("No master specified and Tkinter is "
  42                 "configured to not support default master")
  43     return master
  44 
  45 class ArrayVar(Tkinter.Variable):
  46     """Class for handling Tcl arrays.
  47 
  48     An array is actually an associative array in Tcl, so this class supports
  49     some dict operations.
  50     """
  51 
  52     def __init__(self, master=None, name=None):
  53         # Tkinter.Variable.__init__ is not called on purpose! I don't wanna
  54         # see an ugly _default value in the pretty array.
  55         self._master = _setup_master(master)
  56         self._tk = self._master.tk
  57         if name:
  58             self._name = name
  59         else:
  60             self._name = 'PY_VAR%s' % id(self)
  61 
  62     def __del__(self):
  63         if bool(self._tk.call('info', 'exists', self._name)):
  64             self._tk.globalunsetvar(self._name)
  65 
  66     def __len__(self):
  67         return int(self._tk.call('array', 'size', str(self)))
  68 
  69     def __getitem__(self, key):
  70         return self.get(key)
  71 
  72     def __setitem__(self, key, value):
  73         self.set(**{str(key): value})
  74 
  75     def names(self):
  76         return self._tk.call('array', 'names', self._name)
  77 
  78     def get(self, key=None):
  79         if key is None:
  80             flatten_pairs = self._tk.call('array', 'get', str(self))
  81             return dict(zip(flatten_pairs[::2], flatten_pairs[1::2]))
  82 
  83         return self._tk.globalgetvar(str(self), str(key))
  84 
  85     def set(self, **kw):
  86         self._tk.call('array', 'set', str(self), Tkinter._flatten(kw.items()))
  87 
  88     def unset(self, pattern=None):
  89         """Unsets all of the elements in the array. If pattern is given, only
  90         the elements that match pattern are unset. """
  91         self._tk.call('array', 'unset', str(self), pattern)
  92 
  93 
  94 _TKTABLE_LOADED = False
  95 
  96 class Table(Tkinter.Widget):
  97     """Create and manipulate tables."""
  98 
  99     _switches = ('holddimensions', 'holdselection', 'holdtags', 'holdwindows',
 100                  'keeptitles', '-')
 101     _tabsubst_format = ('%c', '%C', '%i', '%r', '%s', '%S', '%W')
 102     _tabsubst_commands = ('browsecommand', 'browsecmd', 'command',
 103                           'selectioncommand', 'selcmd',
 104                           'validatecommand', 'valcmd')
 105 
 106     def __init__(self, master=None, **kw):
 107         master = _setup_master(master)
 108         global _TKTABLE_LOADED
 109         if not _TKTABLE_LOADED:
 110             tktable_lib = os.environ.get('TKTABLE_LIBRARY')
 111             if tktable_lib:
 112                 master.tk.eval('global auto_path; '
 113                                 'lappend auto_path {%s}' % tktable_lib)
 114             master.tk.call('package', 'require', 'Tktable')
 115             _TKTABLE_LOADED = True
 116 
 117         Tkinter.Widget.__init__(self, master, 'table', kw)
 118 
 119 
 120     def _options(self, cnf, kw=None):
 121         if kw:
 122             cnf = Tkinter._cnfmerge((cnf, kw))
 123         else:
 124             cnf = Tkinter._cnfmerge(cnf)
 125 
 126         res = ()
 127         for k, v in cnf.iteritems():
 128             if callable(v):
 129                 if k in self._tabsubst_commands:
 130                     v = "%s %s"  % (self._register(v, self._tabsubst),
 131                                     ' '.join(self._tabsubst_format))
 132                 else:
 133                     v = self._register(v)
 134             res += ('-%s' % k, v)
 135 
 136         return res
 137 
 138 
 139     def _tabsubst(self, *args):
 140         if len(args) != len(self._tabsubst_format):
 141             return args
 142 
 143         tk = self.tk
 144         c, C, i, r, s, S, W = args
 145         e = Tkinter.Event()
 146 
 147         e.widget = self
 148         e.c = tk.getint(c)
 149         e.i = tk.getint(i)
 150         e.r = tk.getint(r)
 151         e.C = "%d,%d" % (e.r, e.c)
 152         e.s = s
 153         e.S = S
 154         try:
 155             e.W = self._nametowidget(W)
 156         except KeyError:
 157             e.W = None
 158 
 159         return (e,)
 160 
 161 
 162     def _handle_switches(self, args):
 163         args = args or ()
 164         return tuple(('-%s' % x) for x in args if x in self._switches)
 165 
 166 
 167     def activate(self, index):
 168         """Set the active cell to the one indicated by index."""
 169         self.tk.call(self._w, 'activate', index)
 170 
 171 
 172     def bbox(self, first, last=None):
 173         """Return the bounding box for the specified cell (range) as a
 174         4-tuple of x, y, width and height in pixels. It clips the box to
 175         the visible portion, if any, otherwise an empty tuple is returned."""
 176         return self._getints(self.tk.call(self._w, 'bbox', first, last)) or ()
 177 
 178 
 179     def clear(self, option, first=None, last=None):
 180         """This is a convenience routine to clear certain state information
 181         managed by the table. first and last represent valid table indices.
 182         If neither are specified, then the command operates on the whole
 183         table."""
 184         self.tk.call(self._w, 'clear', option, first, last)
 185 
 186 
 187     def clear_cache(self, first=None, last=None):
 188         """Clear the specified section of the cache, if the table has been
 189         keeping one."""
 190         self.clear('cache', first, last)
 191 
 192 
 193     def clear_sizes(self, first=None, last=None):
 194         """Clear the specified row and column areas of specific height/width
 195         dimensions. When just one index is specified, for example 2,0, that
 196         is interpreted as row 2 and column 0."""
 197         self.clear('sizes', first, last)
 198 
 199 
 200     def clear_tags(self, first=None, last=None):
 201         """Clear the specified area of tags (all row, column and cell tags)."""
 202         self.clear('tags', first, last)
 203 
 204 
 205     def clear_all(self, first=None, last=None):
 206         """Perform all of the above clear functions on the specified area."""
 207         self.clear('all', first, last)
 208 
 209 
 210     def curselection(self, value=None):
 211         """With no arguments, it returns the sorted indices of the currently
 212         selected cells. Otherwise it sets all the selected cells to the given
 213         value if there is an associated ArrayVar and the state is not
 214         disabled."""
 215         result = self.tk.call(self._w, 'curselection', value)
 216         if value is None:
 217             return result
 218 
 219 
 220     def curvalue(self, value=None):
 221         """If no value is given, the value of the cell being edited (indexed
 222         by active) is returned, else it is set to the given value. """
 223         return self.tk.call(self._w, 'curvalue', value)
 224 
 225 
 226     def delete_active(self, index1, index2=None):
 227         """Deletes text from the active cell. If only one index is given,
 228         it deletes the character after that index, otherwise it deletes from
 229         the first index to the second. index can be a number, insert or end."""
 230         self.tk.call(self._w, 'delete', 'active', index1, index2)
 231 
 232 
 233     def delete_cols(self, index, count=None, switches=None):
 234         args = self._handle_switches(switches) + (index, count)
 235         self.tk.call(self._w, 'delete', 'cols', *args)
 236 
 237 
 238     def delete_rows(self, index, count=None, switches=None):
 239         args = self._handle_switches(switches) + (index, count)
 240         self.tk.call(self._w, 'delete', 'rows', *args)
 241 
 242 
 243     def get(self, first, last=None):
 244         """Returns the value of the cells specified by the table indices
 245         first and (optionally) last."""
 246         return self.tk.call(self._w, 'get', first, last)
 247 
 248 
 249     def height(self, row=None, **kwargs):
 250         """If row and kwargs are not given, a list describing all rows for
 251         which a width has been set is returned.
 252         If row is given, the height of that row is returnd.
 253         If kwargs is given, then it sets the key/value pairs, where key is a
 254         row and value represents the height for the row."""
 255         if row is None and not kwargs:
 256             pairs = self.tk.splitlist(self.tk.call(self._w, 'height'))
 257             return dict(pair.split() for pair in pairs)
 258         elif row:
 259             return int(self.tk.call(self._w, 'height', str(row)))
 260 
 261         args = Tkinter._flatten(kwargs.items())
 262         self.tk.call(self._w, 'height', *args)
 263 
 264 
 265     def hidden(self, *args):
 266         """When called without args, it returns all the hidden cells (those
 267         cells covered by a spanning cell). If one index is specified, it
 268         returns the spanning cell covering that index, if any. If multiple
 269         indices are specified, it returns 1 if all indices are hidden cells,
 270         0 otherwise."""
 271         return self.tk.call(self._w, 'hidden', *args)
 272 
 273 
 274     def icursor(self, arg=None):
 275         """If arg is not specified, return the location of the insertion
 276         cursor in the active cell. Otherwise, set the cursor to that point in
 277         the string.
 278 
 279         0 is before the first character, you can also use insert or end for
 280         the current insertion point or the end of the text. If there is no
 281         active cell, or the cell or table is disabled, this will return -1."""
 282         return self.tk.call(self._w, 'icursor', arg)
 283 
 284 
 285     def index(self, index, rc=None):
 286         """Return the integer cell coordinate that corresponds to index in the
 287         form row, col. If rc is specified, it must be either 'row' or 'col' so
 288         only the row or column index is returned."""
 289         res = self.tk.call(self._w, 'index', index, rc)
 290         if rc is None:
 291             return res
 292         else:
 293             return int(res)
 294 
 295 
 296     def insert_active(self, index, value):
 297         """The value is a text string which is inserted at the index postion
 298         of the active cell. The cursor is then positioned after the new text.
 299         index can be a number, insert or end. """
 300         self.tk.call(self._w, 'insert', 'active', index, value)
 301 
 302 
 303     def insert_cols(self, index, count=None, switches=None):
 304         args = self._handle_switches(switches) + (index, count)
 305         self.tk.call(self._w, 'insert', 'cols', *args)
 306 
 307 
 308     def insert_rows(self, index, count=None, switches=None):
 309         args = self._handle_switches(switches) + (index, count)
 310         self.tk.call(self._w, 'insert', 'rows', *args)
 311 
 312 
 313     #def postscript(self, **kwargs):
 314     #    """Skip this command if you are under Windows.
 315     #
 316     #    Accepted options:
 317     #        colormap, colormode, file, channel, first, fontmap, height,
 318     #        last, pageanchor, pageheight, pagewidth, pagex, pagey, rotate,
 319     #        width, x, y
 320     #    """
 321     #    args = ()
 322     #    for key, val in kwargs.iteritems():
 323     #        args += ('-%s' % key, val)
 324     #
 325     #    return self.tk.call(self._w, 'postscript', *args)
 326 
 327 
 328     def reread(self):
 329         """Rereads the old contents of the cell back into the editing buffer.
 330         Useful for a key binding when <Escape> is pressed to abort the edit
 331         (a default binding)."""
 332         self.tk.call(self._w, 'reread')
 333 
 334 
 335     def scan_mark(self, x, y):
 336         self.tk.call(self._w, 'scan', 'mark', x, y)
 337 
 338 
 339     def scan_dragto(self, x, y):
 340         self.tk.call(self._w, 'scan', 'dragto', x, y)
 341 
 342 
 343     def see(self, index):
 344         self.tk.call(self._w, 'see', index)
 345 
 346 
 347     def selection_anchor(self, index):
 348         self.tk.call(self._w, 'selection', 'anchor', index)
 349 
 350 
 351     def selection_clear(self, first, last=None):
 352         self.tk.call(self._w, 'selection', 'clear', first, last)
 353 
 354 
 355     def selection_includes(self, index):
 356         return self.getboolean(self.tk.call(self._w, 'selection', 'includes',
 357                                             index))
 358 
 359 
 360     def selection_set(self, first, last=None):
 361         self.tk.call(self._w, 'selection', 'set', first, last)
 362 
 363 
 364     def set(self, rc=None, index=None, *args, **kwargs):
 365         """If rc is specified (either 'row' or 'col') then it is assumes that
 366         args (if given) represents values which will be set into the
 367         subsequent columns (if row is specified) or rows (for col).
 368         If index is not None and args is not given, then it will return the
 369         value(s) for the cell(s) specified.
 370 
 371         If kwargs is given, assumes that each key in kwargs is a index in this
 372         table and sets the specified index to the associated value. Table
 373         validation will not be triggered via this method.
 374 
 375         Note that the table must have an associated array (defined through the
 376         variable option) in order to this work."""
 377         if not args and index is not None:
 378             if rc:
 379                 args = (rc, index)
 380             else:
 381                 args = (index, )
 382             return self.tk.call(self._w, 'set', *args)
 383 
 384         if rc is None:
 385             args = Tkinter._flatten(kwargs.items())
 386             self.tk.call(self._w, 'set', *args)
 387         else:
 388             self.tk.call(self._w, 'set', rc, index, args)
 389 
 390 
 391     def spans(self, index=None, **kwargs):
 392         """Manipulate row/col spans.
 393         
 394         When called with no arguments, all known spans are returned as a dict.
 395         When called with only the index, the span for that index only is
 396         returned, if any. Otherwise kwargs is assumed to contain keys/values
 397         pairs used to set spans. A span starts at the row,col defined by a key
 398         and continues for the specified number of rows,cols specified by
 399         its value. A span of 0,0 unsets any span on that cell."""
 400         if kwargs:
 401             args = Tkinter._flatten(kwargs.items())
 402             self.tk.call(self._w, 'spans', *args)
 403         else:
 404             return self.tk.call(self._w, 'spans', index)
 405 
 406 
 407     def tag_cell(self, tagname, *args):
 408         return self.tk.call(self._w, 'tag', 'cell', tagname, *args)
 409 
 410 
 411     def tag_cget(self, tagname, option):
 412         return self.tk.call(self._w, 'tag', 'cget', tagname, '-%s' % option)
 413 
 414 
 415     def tag_col(self, tagname, *args):
 416         return self.tk.call(self._w, 'tag', 'col', tagname, *args)
 417 
 418 
 419     def tag_configure(self, tagname, option=None, **kwargs):
 420         """Query or modify options associated with the tag given by tagname.
 421 
 422         If no option is specified, a dict describing all of the available
 423         options for tagname is returned. If option is specified, then the
 424         command returns a list describing the one named option. Lastly, if
 425         kwargs is given then it corresponds to option-value pairs that should
 426         be modified."""
 427         if option is None and not kwargs:
 428             split1 = self.tk.splitlist(
 429                     self.tk.call(self._w, 'tag', 'configure', tagname))
 430 
 431             result = {}
 432             for item in split1:
 433                 res = self.tk.splitlist(item)
 434                 result[res[0]] = res[1:]
 435 
 436             return result
 437 
 438         elif option:
 439             return self.tk.call(self._w, 'tag', 'configure', tagname,
 440                                 '-%s' % option)
 441 
 442         else:
 443             args = ()
 444             for key, val in kwargs.iteritems():
 445                 args += ('-%s' % key, val)
 446 
 447             self.tk.call(self._w, 'tag', 'configure', tagname, *args)
 448 
 449 
 450     def tag_delete(self, tagname):
 451         self.tk.call(self._w, 'tag', 'delete', tagname)
 452 
 453 
 454     def tag_exists(self, tagname):
 455         return self.getboolean(self.tk.call(self._w, 'tag', 'exists', tagname))
 456 
 457 
 458     def tag_includes(self, tagname, index):
 459         return self.getboolean(self.tk.call(self._w, 'tag', 'includes',
 460                                             tagname, index))
 461 
 462 
 463     def tag_lower(self, tagname, belowthis=None):
 464         self.tk.call(self._w, 'tag', 'lower', belowthis)
 465 
 466 
 467     def tag_names(self, pattern=None):
 468         return self.tk.call(self._w, 'tag', 'names', pattern)
 469 
 470 
 471     def tag_raise(self, tagname, abovethis=None):
 472         self.tk.call(self._w, 'tag', 'raise', tagname, abovethis)
 473 
 474 
 475     def tag_row(self, tagname, *args):
 476         return self.tk.call(self._w, 'tag', 'row', tagname, *args)
 477 
 478 
 479     def validate(self, index):
 480         """Explicitly validates the specified index based on the current
 481         callback set for the validatecommand option. Return 0 or 1 based on
 482         whether the cell was validated."""
 483         return self.tk.call(self._w, 'validate', index)
 484 
 485 
 486     @property
 487     def version(self):
 488         """Return tktable's package version."""
 489         return self.tk.call(self._w, 'version')
 490 
 491 
 492     def width(self, column=None, **kwargs):
 493         """If column and kwargs are not given, a dict describing all columns
 494         for which a width has been set is returned.
 495         If column is given, the width of that column is returnd.
 496         If kwargs is given, then it sets the key/value pairs, where key is a
 497         column and value represents the width for the column."""
 498         if column is None and not kwargs:
 499             pairs = self.tk.splitlist(self.tk.call(self._w, 'width'))
 500             return dict(pair.split() for pair in pairs)
 501         elif column is not None:
 502             return int(self.tk.call(self._w, 'width', str(column)))
 503 
 504         args = Tkinter._flatten(kwargs.items())
 505         self.tk.call(self._w, 'width', *args)
 506 
 507 
 508     def window_cget(self, index, option):
 509         return self.tk.call(self._w, 'window', 'cget', index, option)
 510 
 511 
 512     def window_configure(self, index, option=None, **kwargs):
 513         """Query or modify options associated with the embedded window given
 514         by index. This should also be used to add a new embedded window into
 515         the table.
 516 
 517         If no option is specified, a dict describing all of the available
 518         options for index is returned. If option is specified, then the
 519         command returns a list describing the one named option. Lastly, if
 520         kwargs is given then it corresponds to option-value pairs that should
 521         be modified."""
 522         if option is None and not kwargs:
 523             return self.tk.call(self._w, 'window', 'configure', index)
 524         elif option:
 525             return self.tk.call(self._w, 'window', 'configure', index,
 526                                 '-%s' % option)
 527         else:
 528             args = ()
 529             for key, val in kwargs.iteritems():
 530                 args += ('-%s' % key, val)
 531 
 532             self.tk.call(self._w, 'window', 'configure', index, *args)
 533 
 534 
 535     def window_delete(self, *indexes):
 536         self.tk.call(self._w, 'window', 'delete', *indexes)
 537 
 538 
 539     def window_move(self, index_from, index_to):
 540         self.tk.call(self._w, 'window', 'move', index_from, index_to)
 541 
 542 
 543     def window_names(self, pattern=None):
 544         return self.tk.call(self._w, 'window', 'names', pattern)
 545 
 546 
 547     def xview(self, index=None):
 548         """If index is not given a tuple containing two fractions is returned,
 549         each fraction is between 0 and 1. Together they describe the
 550         horizontal span that is visible in the window.
 551 
 552         If index is given the view in the window is adjusted so that the
 553         column given by index is displayed at the left edge of the window."""
 554         res = self.tk.call(self._w, 'xview', index)
 555         if index is None:
 556             return self._getdoubles(res)
 557 
 558 
 559     def xview_moveto(self, fraction):
 560         """Adjusts the view in the window so that fraction of the total width
 561         of the table text is off-screen to the left. The fraction parameter
 562         must be a fraction between 0 and 1."""
 563         self.tk.call(self._w, 'xview', 'moveto', fraction)
 564 
 565 
 566     def xview_scroll(self, number, what):
 567         """Shift the view in the window left or right according to number and
 568         what. The 'number' parameter must be an integer. The 'what' parameter
 569         must be either units or pages or an abbreviation of one of these.
 570 
 571         If 'what' is units, the view adjusts left or right by number cells on
 572         the display; if it is pages then the view adjusts by number screenfuls.
 573         If 'number' is negative then cells farther to the left become visible;
 574         if it is positive then cells farther to the right become visible. """
 575         self.tk.call(self._w, 'xview', 'scroll', number, what)
 576 
 577 
 578     def yview(self, index=None):
 579         """If index is not given a tuple containing two fractions is returned,
 580         each fraction is between 0 and 1. The first element gives the position
 581         of the table element at the top of the window, relative to the table
 582         as a whole. The second element gives the position of the table element
 583         just after the last one in the window, relative to the table as a
 584         whole.
 585 
 586         If index is given the view in the window is adjusted so that the
 587         row given by index is displayed at the top of the window."""
 588         res = self.tk.call(self._w, 'yview', index)
 589         if index is None:
 590             return self._getdoubles(res)
 591 
 592 
 593     def yview_moveto(self, fraction):
 594         """Adjusts the view in the window so that the element given by
 595         fraction appears at the top of the window. The fraction parameter
 596         must be a fraction between 0 and 1."""
 597         self.tk.call(self._w, 'yview', 'moveto', fraction)
 598 
 599 
 600     def yview_scroll(self, number, what):
 601         """Adjust the view in the window up or down according to number and
 602         what. The 'number' parameter must be an integer. The 'what' parameter
 603         must be either units or pages or an abbreviation of one of these.
 604 
 605         If 'what' is units, the view adjusts up or down by number cells; if it
 606         is pages then the view adjusts by number screenfuls.
 607         If 'number' is negative then earlier elements become visible; if it
 608         is positive then later elements become visible. """
 609         self.tk.call(self._w, 'yview', 'scroll', number, what)
 610 
 611 
 612 # Sample test taken from tktable cvs, original tktable python wrapper
 613 def sample_test():
 614     from Tkinter import Tk, Label, Button
 615 
 616     def test_cmd(event):
 617         if event.i == 0:
 618             return '%i, %i' % (event.r, event.c)
 619         else:
 620             return 'set'
 621 
 622     def browsecmd(event):
 623         print "event:", event.__dict__
 624         print "curselection:", test.curselection()
 625         print "active cell index:", test.index('active')
 626         print "active:", test.index('active', 'row')
 627         print "anchor:", test.index('anchor', 'row')
 628 
 629     root = Tk()
 630 
 631     var = ArrayVar(root)
 632     for y in range(-1, 4):
 633         for x in range(-1, 5):
 634             index = "%i,%i" % (y, x)
 635             var[index] = index
 636 
 637     label = Label(root, text="Proof-of-existence test for Tktable")
 638     label.pack(side = 'top', fill = 'x')
 639 
 640     quit = Button(root, text="QUIT", command=root.destroy)
 641     quit.pack(side = 'bottom', fill = 'x')
 642 
 643     test = Table(root,
 644                  rows=10,
 645                  cols=5,
 646                  state='disabled',
 647                  width=6,
 648                  height=6,
 649                  titlerows=1,
 650                  titlecols=1,
 651                  roworigin=-1,
 652                  colorigin=-1,
 653                  selectmode='browse',
 654                  selecttype='row',
 655                  rowstretch='unset',
 656                  colstretch='last',
 657                  browsecmd=browsecmd,
 658                  flashmode='on',
 659                  variable=var,
 660                  usecommand=0,
 661                  command=test_cmd)
 662     test.pack(expand=1, fill='both')
 663     test.tag_configure('sel', background = 'yellow')
 664     test.tag_configure('active', background = 'blue')
 665     test.tag_configure('title', anchor='w', bg='red', relief='sunken')
 666     root.mainloop()
 667 
 668 if __name__ == '__main__':
 669     sample_test()

None: TkTableWrapper (last edited 2008-11-23 20:06:02 by GuilhermePolo)