summaryrefslogtreecommitdiffstats
path: root/library
diff options
context:
space:
mode:
Diffstat (limited to 'library')
-rwxr-xr-xlibrary/tkTable.tcl825
-rwxr-xr-xlibrary/tktable.py651
2 files changed, 1476 insertions, 0 deletions
diff --git a/library/tkTable.tcl b/library/tkTable.tcl
new file mode 100755
index 0000000..0343a43
--- /dev/null
+++ b/library/tkTable.tcl
@@ -0,0 +1,825 @@
+# table.tcl --
+#
+# Version align with tkTable 2.7, jeff at hobbs org
+# This file defines the default bindings for Tk table widgets
+# and provides procedures that help in implementing those bindings.
+#
+# RCS: @(#) $Id: tkTable.tcl,v 1.1.1.1 2011/03/01 20:00:38 joye Exp $
+
+#--------------------------------------------------------------------------
+# ::tk::table::Priv elements used in this file:
+#
+# x && y - Coords in widget
+# afterId - Token returned by "after" for autoscanning.
+# tablePrev - The last element to be selected or deselected
+# during a selection operation.
+# mouseMoved - Boolean to indicate whether mouse moved while
+# the button was pressed.
+# borderInfo - Boolean to know if the user clicked on a border
+# borderB1 - Boolean that set whether B1 can be used for the
+# interactiving resizing
+#--------------------------------------------------------------------------
+
+namespace eval ::tk::table {
+ # Ensure that a namespace is created for us
+ variable Priv
+ array set Priv [list x 0 y 0 afterId {} mouseMoved 0 \
+ borderInfo {} borderB1 1]
+}
+
+# ::tk::table::ClipboardKeysyms --
+# This procedure is invoked to identify the keys that correspond to
+# the "copy", "cut", and "paste" functions for the clipboard.
+#
+# Arguments:
+# copy - Name of the key (keysym name plus modifiers, if any,
+# such as "Meta-y") used for the copy operation.
+# cut - Name of the key used for the cut operation.
+# paste - Name of the key used for the paste operation.
+
+proc ::tk::table::ClipboardKeysyms {copy cut paste} {
+ bind Table <$copy> {tk_tableCopy %W}
+ bind Table <$cut> {tk_tableCut %W}
+ bind Table <$paste> {tk_tablePaste %W}
+}
+::tk::table::ClipboardKeysyms <Copy> <Cut> <Paste>
+
+##
+## Interactive cell resizing, affected by -resizeborders option
+##
+bind Table <3> {
+ ## You might want to check for cell returned if you want to
+ ## restrict the resizing of certain cells
+ %W border mark %x %y
+}
+bind Table <B3-Motion> { %W border dragto %x %y }
+
+## Button events
+
+bind Table <1> { ::tk::table::Button1 %W %x %y }
+bind Table <B1-Motion> { ::tk::table::B1Motion %W %x %y }
+
+bind Table <ButtonRelease-1> {
+ if {$::tk::table::Priv(borderInfo) == "" && [winfo exists %W]} {
+ ::tk::table::CancelRepeat
+ %W activate @%x,%y
+ }
+}
+bind Table <Double-1> {
+ # empty
+}
+
+bind Table <Shift-1> {::tk::table::BeginExtend %W [%W index @%x,%y]}
+bind Table <Control-1> {::tk::table::BeginToggle %W [%W index @%x,%y]}
+bind Table <B1-Enter> {::tk::table::CancelRepeat}
+bind Table <B1-Leave> {
+ if {$::tk::table::Priv(borderInfo) == ""} {
+ array set ::tk::table::Priv {x %x y %y}
+ ::tk::table::AutoScan %W
+ }
+}
+bind Table <2> {
+ %W scan mark %x %y
+ array set ::tk::table::Priv {x %x y %y}
+ set ::tk::table::Priv(mouseMoved) 0
+}
+bind Table <B2-Motion> {
+ if {(%x != $::tk::table::Priv(x)) || (%y != $::tk::table::Priv(y))} {
+ set ::tk::table::Priv(mouseMoved) 1
+ }
+ if {$::tk::table::Priv(mouseMoved)} { %W scan dragto %x %y }
+}
+bind Table <ButtonRelease-2> {
+ if {!$::tk::table::Priv(mouseMoved)} { tk_tablePaste %W [%W index @%x,%y] }
+}
+
+## Key events
+
+# This forces a cell commit if an active cell exists
+bind Table <<Table_Commit>> {
+ catch {%W activate active}
+}
+# Remove this if you don't want cell commit to occur on every Leave for
+# the table (via mouse) or FocusOut (loss of focus by table).
+event add <<Table_Commit>> <Leave> <FocusOut>
+
+bind Table <Shift-Up> {::tk::table::ExtendSelect %W -1 0}
+bind Table <Shift-Down> {::tk::table::ExtendSelect %W 1 0}
+bind Table <Shift-Left> {::tk::table::ExtendSelect %W 0 -1}
+bind Table <Shift-Right> {::tk::table::ExtendSelect %W 0 1}
+bind Table <Prior> {%W yview scroll -1 pages; %W activate topleft}
+bind Table <Next> {%W yview scroll 1 pages; %W activate topleft}
+bind Table <Control-Prior> {%W xview scroll -1 pages}
+bind Table <Control-Next> {%W xview scroll 1 pages}
+bind Table <Home> {%W see origin}
+bind Table <End> {%W see end}
+bind Table <Control-Home> {
+ %W selection clear all
+ %W activate origin
+ %W selection set active
+ %W see active
+}
+bind Table <Control-End> {
+ %W selection clear all
+ %W activate end
+ %W selection set active
+ %W see active
+}
+bind Table <Shift-Control-Home> {::tk::table::DataExtend %W origin}
+bind Table <Shift-Control-End> {::tk::table::DataExtend %W end}
+bind Table <Select> {::tk::table::BeginSelect %W [%W index active]}
+bind Table <Shift-Select> {::tk::table::BeginExtend %W [%W index active]}
+bind Table <Control-slash> {::tk::table::SelectAll %W}
+bind Table <Control-backslash> {
+ if {[string match browse [%W cget -selectmode]]} {%W selection clear all}
+}
+bind Table <Up> {::tk::table::MoveCell %W -1 0}
+bind Table <Down> {::tk::table::MoveCell %W 1 0}
+bind Table <Left> {::tk::table::MoveCell %W 0 -1}
+bind Table <Right> {::tk::table::MoveCell %W 0 1}
+bind Table <KeyPress> {::tk::table::Insert %W %A}
+bind Table <BackSpace> {::tk::table::BackSpace %W}
+bind Table <Delete> {%W delete active insert}
+bind Table <Escape> {%W reread}
+
+#bind Table <Return> {::tk::table::MoveCell %W 1 0}
+bind Table <Return> {::tk::table::Insert %W "\n"}
+
+bind Table <Control-Left> {%W icursor [expr {[%W icursor]-1}]}
+bind Table <Control-Right> {%W icursor [expr {[%W icursor]+1}]}
+bind Table <Control-e> {%W icursor end}
+bind Table <Control-a> {%W icursor 0}
+bind Table <Control-k> {%W delete active insert end}
+bind Table <Control-equal> {::tk::table::ChangeWidth %W active 1}
+bind Table <Control-minus> {::tk::table::ChangeWidth %W active -1}
+
+# Ignore all Alt, Meta, and Control keypresses unless explicitly bound.
+# Otherwise, if a widget binding for one of these is defined, the
+# <KeyPress> class binding will also fire and insert the character,
+# which is wrong. Ditto for Tab.
+
+bind Table <Alt-KeyPress> {# nothing}
+bind Table <Meta-KeyPress> {# nothing}
+bind Table <Control-KeyPress> {# nothing}
+bind Table <Any-Tab> {# nothing}
+if {[string match "macintosh" $::tcl_platform(platform)]} {
+ bind Table <Command-KeyPress> {# nothing}
+}
+
+# ::tk::table::GetSelection --
+# This tries to obtain the default selection. On Unix, we first try
+# and get a UTF8_STRING, a type supported by modern Unix apps for
+# passing Unicode data safely. We fall back on the default STRING
+# type otherwise. On Windows, only the STRING type is necessary.
+# Arguments:
+# w The widget for which the selection will be retrieved.
+# Important for the -displayof property.
+# sel The source of the selection (PRIMARY or CLIPBOARD)
+# Results:
+# Returns the selection, or an error if none could be found
+#
+if {[string compare $::tcl_platform(platform) "unix"]} {
+ proc ::tk::table::GetSelection {w {sel PRIMARY}} {
+ if {[catch {selection get -displayof $w -selection $sel} txt]} {
+ return -code error "could not find default selection"
+ } else {
+ return $txt
+ }
+ }
+} else {
+ proc ::tk::table::GetSelection {w {sel PRIMARY}} {
+ if {[catch {selection get -displayof $w -selection $sel \
+ -type UTF8_STRING} txt] \
+ && [catch {selection get -displayof $w -selection $sel} txt]} {
+ return -code error "could not find default selection"
+ } else {
+ return $txt
+ }
+ }
+}
+
+# ::tk::table::CancelRepeat --
+# A copy of tkCancelRepeat, just in case it's not available or changes.
+# This procedure is invoked to cancel an auto-repeat action described
+# by ::tk::table::Priv(afterId). It's used by several widgets to auto-scroll
+# the widget when the mouse is dragged out of the widget with a
+# button pressed.
+#
+# Arguments:
+# None.
+
+proc ::tk::table::CancelRepeat {} {
+ variable Priv
+ after cancel $Priv(afterId)
+ set Priv(afterId) {}
+}
+
+# ::tk::table::Insert --
+#
+# Insert into the active cell
+#
+# Arguments:
+# w - the table widget
+# s - the string to insert
+# Results:
+# Returns nothing
+#
+proc ::tk::table::Insert {w s} {
+ if {[string compare $s {}]} {
+ $w insert active insert $s
+ }
+}
+
+# ::tk::table::BackSpace --
+#
+# BackSpace in the current cell
+#
+# Arguments:
+# w - the table widget
+# Results:
+# Returns nothing
+#
+proc ::tk::table::BackSpace {w} {
+ set cur [$w icursor]
+ if {[string compare {} $cur] && $cur} {
+ $w delete active [expr {$cur-1}]
+ }
+}
+
+# ::tk::table::Button1 --
+#
+# This procedure is called to handle selecting with mouse button 1.
+# It will distinguish whether to start selection or mark a border.
+#
+# Arguments:
+# w - the table widget
+# x - x coord
+# y - y coord
+# Results:
+# Returns nothing
+#
+proc ::tk::table::Button1 {w x y} {
+ variable Priv
+ #
+ # $Priv(borderInfo) is null if the user did not click on a border
+ #
+ if {$Priv(borderB1) == 1} {
+ set Priv(borderInfo) [$w border mark $x $y]
+ # account for what resizeborders are set [Bug 876320] (ferenc)
+ set rbd [$w cget -resizeborders]
+ if {$rbd == "none" || ![llength $Priv(borderInfo)]
+ || ($rbd == "col" && [lindex $Priv(borderInfo) 1] == "")
+ || ($rbd == "row" && [lindex $Priv(borderInfo) 0] == "")} {
+ set Priv(borderInfo) ""
+ }
+ } else {
+ set Priv(borderInfo) ""
+ }
+ if {$Priv(borderInfo) == ""} {
+ #
+ # Only do this when a border wasn't selected
+ #
+ if {[winfo exists $w]} {
+ ::tk::table::BeginSelect $w [$w index @$x,$y]
+ focus $w
+ }
+ array set Priv [list x $x y $y]
+ set Priv(mouseMoved) 0
+ }
+}
+
+# ::tk::table::B1Motion --
+#
+# This procedure is called to start processing mouse motion events while
+# button 1 moves while pressed. It will distinguish whether to change
+# the selection or move a border.
+#
+# Arguments:
+# w - the table widget
+# x - x coord
+# y - y coord
+# Results:
+# Returns nothing
+#
+proc ::tk::table::B1Motion {w x y} {
+ variable Priv
+
+ # If we already had motion, or we moved more than 1 pixel,
+ # then we start the Motion routine
+ if {$Priv(borderInfo) != ""} {
+ #
+ # If the motion is on a border, drag it and skip the rest
+ # of this binding.
+ #
+ $w border dragto $x $y
+ } else {
+ #
+ # If we already had motion, or we moved more than 1 pixel,
+ # then we start the Motion routine
+ #
+ if {
+ $::tk::table::Priv(mouseMoved)
+ || abs($x-$::tk::table::Priv(x)) > 1
+ || abs($y-$::tk::table::Priv(y)) > 1
+ } {
+ set ::tk::table::Priv(mouseMoved) 1
+ }
+ if {$::tk::table::Priv(mouseMoved)} {
+ ::tk::table::Motion $w [$w index @$x,$y]
+ }
+ }
+}
+
+# ::tk::table::BeginSelect --
+#
+# This procedure is typically invoked on button-1 presses. It begins
+# the process of making a selection in the table. Its exact behavior
+# depends on the selection mode currently in effect for the table;
+# see the Motif documentation for details.
+#
+# Arguments:
+# w - The table widget.
+# el - The element for the selection operation (typically the
+# one under the pointer). Must be in row,col form.
+
+proc ::tk::table::BeginSelect {w el} {
+ variable Priv
+ if {[scan $el %d,%d r c] != 2} return
+ switch [$w cget -selectmode] {
+ multiple {
+ if {[$w tag includes title $el]} {
+ ## in the title area
+ if {$r < [$w cget -titlerows]+[$w cget -roworigin]} {
+ ## We're in a column header
+ if {$c < [$w cget -titlecols]+[$w cget -colorigin]} {
+ ## We're in the topleft title area
+ set inc topleft
+ set el2 end
+ } else {
+ set inc [$w index topleft row],$c
+ set el2 [$w index end row],$c
+ }
+ } else {
+ ## We're in a row header
+ set inc $r,[$w index topleft col]
+ set el2 $r,[$w index end col]
+ }
+ } else {
+ set inc $el
+ set el2 $el
+ }
+ if {[$w selection includes $inc]} {
+ $w selection clear $el $el2
+ } else {
+ $w selection set $el $el2
+ }
+ }
+ extended {
+ $w selection clear all
+ if {[$w tag includes title $el]} {
+ if {$r < [$w cget -titlerows]+[$w cget -roworigin]} {
+ ## We're in a column header
+ if {$c < [$w cget -titlecols]+[$w cget -colorigin]} {
+ ## We're in the topleft title area
+ $w selection set $el end
+ } else {
+ $w selection set $el [$w index end row],$c
+ }
+ } else {
+ ## We're in a row header
+ $w selection set $el $r,[$w index end col]
+ }
+ } else {
+ $w selection set $el
+ }
+ $w selection anchor $el
+ set Priv(tablePrev) $el
+ }
+ default {
+ if {![$w tag includes title $el]} {
+ $w selection clear all
+ $w selection set $el
+ set Priv(tablePrev) $el
+ }
+ $w selection anchor $el
+ }
+ }
+}
+
+# ::tk::table::Motion --
+#
+# This procedure is called to process mouse motion events while
+# button 1 is down. It may move or extend the selection, depending
+# on the table's selection mode.
+#
+# Arguments:
+# w - The table widget.
+# el - The element under the pointer (must be in row,col form).
+
+proc ::tk::table::Motion {w el} {
+ variable Priv
+ if {![info exists Priv(tablePrev)]} {
+ set Priv(tablePrev) $el
+ return
+ }
+ if {[string match $Priv(tablePrev) $el]} return
+ switch [$w cget -selectmode] {
+ browse {
+ $w selection clear all
+ $w selection set $el
+ set Priv(tablePrev) $el
+ }
+ extended {
+ # avoid tables that have no anchor index yet.
+ if {[catch {$w index anchor}]} { return }
+ scan $Priv(tablePrev) %d,%d r c
+ scan $el %d,%d elr elc
+ if {[$w tag includes title $el]} {
+ if {$r < [$w cget -titlerows]+[$w cget -roworigin]} {
+ ## We're in a column header
+ if {$c < [$w cget -titlecols]+[$w cget -colorigin]} {
+ ## We're in the topleft title area
+ $w selection clear anchor end
+ } else {
+ $w selection clear anchor [$w index end row],$c
+ }
+ $w selection set anchor [$w index end row],$elc
+ } else {
+ ## We're in a row header
+ $w selection clear anchor $r,[$w index end col]
+ $w selection set anchor $elr,[$w index end col]
+ }
+ } else {
+ $w selection clear anchor $Priv(tablePrev)
+ $w selection set anchor $el
+ }
+ set Priv(tablePrev) $el
+ }
+ }
+}
+
+# ::tk::table::BeginExtend --
+#
+# This procedure is typically invoked on shift-button-1 presses. It
+# begins the process of extending a selection in the table. Its
+# exact behavior depends on the selection mode currently in effect
+# for the table; see the Motif documentation for details.
+#
+# Arguments:
+# w - The table widget.
+# el - The element for the selection operation (typically the
+# one under the pointer). Must be in numerical form.
+
+proc ::tk::table::BeginExtend {w el} {
+ # avoid tables that have no anchor index yet.
+ if {[catch {$w index anchor}]} { return }
+ if {[string match extended [$w cget -selectmode]] &&
+ [$w selection includes anchor]} {
+ ::tk::table::Motion $w $el
+ }
+}
+
+# ::tk::table::BeginToggle --
+#
+# This procedure is typically invoked on control-button-1 presses. It
+# begins the process of toggling a selection in the table. Its
+# exact behavior depends on the selection mode currently in effect
+# for the table; see the Motif documentation for details.
+#
+# Arguments:
+# w - The table widget.
+# el - The element for the selection operation (typically the
+# one under the pointer). Must be in numerical form.
+
+proc ::tk::table::BeginToggle {w el} {
+ if {[string match extended [$w cget -selectmode]]} {
+ variable Priv
+ set Priv(tablePrev) $el
+ $w selection anchor $el
+ if {[$w tag includes title $el]} {
+ scan $el %d,%d r c
+ if {$r < [$w cget -titlerows]+[$w cget -roworigin]} {
+ ## We're in a column header
+ if {$c < [$w cget -titlecols]+[$w cget -colorigin]} {
+ ## We're in the topleft title area
+ set end end
+ } else {
+ set end [$w index end row],$c
+ }
+ } else {
+ ## We're in a row header
+ set end $r,[$w index end col]
+ }
+ } else {
+ ## We're in a non-title cell
+ set end $el
+ }
+ if {[$w selection includes $end]} {
+ $w selection clear $el $end
+ } else {
+ $w selection set $el $end
+ }
+ }
+}
+
+# ::tk::table::AutoScan --
+# This procedure is invoked when the mouse leaves an table window
+# with button 1 down. It scrolls the window up, down, left, or
+# right, depending on where the mouse left the window, and reschedules
+# itself as an "after" command so that the window continues to scroll until
+# the mouse moves back into the window or the mouse button is released.
+#
+# Arguments:
+# w - The table window.
+
+proc ::tk::table::AutoScan {w} {
+ if {![winfo exists $w]} return
+ variable Priv
+ set x $Priv(x)
+ set y $Priv(y)
+ if {$y >= [winfo height $w]} {
+ $w yview scroll 1 units
+ } elseif {$y < 0} {
+ $w yview scroll -1 units
+ } elseif {$x >= [winfo width $w]} {
+ $w xview scroll 1 units
+ } elseif {$x < 0} {
+ $w xview scroll -1 units
+ } else {
+ return
+ }
+ ::tk::table::Motion $w [$w index @$x,$y]
+ set Priv(afterId) [after 50 ::tk::table::AutoScan $w]
+}
+
+# ::tk::table::MoveCell --
+#
+# Moves the location cursor (active element) by the specified number
+# of cells and changes the selection if we're in browse or extended
+# selection mode. If the new cell is "hidden", we skip to the next
+# visible cell if possible, otherwise just abort.
+#
+# Arguments:
+# w - The table widget.
+# x - +1 to move down one cell, -1 to move up one cell.
+# y - +1 to move right one cell, -1 to move left one cell.
+
+proc ::tk::table::MoveCell {w x y} {
+ if {[catch {$w index active row} r]} return
+ set c [$w index active col]
+ set cell [$w index [incr r $x],[incr c $y]]
+ while {[string compare [set true [$w hidden $cell]] {}]} {
+ # The cell is in some way hidden
+ if {[string compare $true [$w index active]]} {
+ # The span cell wasn't the previous cell, so go to that
+ set cell $true
+ break
+ }
+ if {$x > 0} {incr r} elseif {$x < 0} {incr r -1}
+ if {$y > 0} {incr c} elseif {$y < 0} {incr c -1}
+ if {[string compare $cell [$w index $r,$c]]} {
+ set cell [$w index $r,$c]
+ } else {
+ # We couldn't find a non-hidden cell, just don't move
+ return
+ }
+ }
+ $w activate $cell
+ $w see active
+ switch [$w cget -selectmode] {
+ browse {
+ $w selection clear all
+ $w selection set active
+ }
+ extended {
+ variable Priv
+ $w selection clear all
+ $w selection set active
+ $w selection anchor active
+ set Priv(tablePrev) [$w index active]
+ }
+ }
+}
+
+# ::tk::table::ExtendSelect --
+#
+# Does nothing unless we're in extended selection mode; in this
+# case it moves the location cursor (active element) by the specified
+# number of cells, and extends the selection to that point.
+#
+# Arguments:
+# w - The table widget.
+# x - +1 to move down one cell, -1 to move up one cell.
+# y - +1 to move right one cell, -1 to move left one cell.
+
+proc ::tk::table::ExtendSelect {w x y} {
+ if {[string compare extended [$w cget -selectmode]] ||
+ [catch {$w index active row} r]} return
+ set c [$w index active col]
+ $w activate [incr r $x],[incr c $y]
+ $w see active
+ ::tk::table::Motion $w [$w index active]
+}
+
+# ::tk::table::DataExtend
+#
+# This procedure is called for key-presses such as Shift-KEndData.
+# If the selection mode isnt multiple or extend then it does nothing.
+# Otherwise it moves the active element to el and, if we're in
+# extended mode, extends the selection to that point.
+#
+# Arguments:
+# w - The table widget.
+# el - An integer cell number.
+
+proc ::tk::table::DataExtend {w el} {
+ set mode [$w cget -selectmode]
+ if {[string match extended $mode]} {
+ $w activate $el
+ $w see $el
+ if {[$w selection includes anchor]} {::tk::table::Motion $w $el}
+ } elseif {[string match multiple $mode]} {
+ $w activate $el
+ $w see $el
+ }
+}
+
+# ::tk::table::SelectAll
+#
+# This procedure is invoked to handle the "select all" operation.
+# For single and browse mode, it just selects the active element.
+# Otherwise it selects everything in the widget.
+#
+# Arguments:
+# w - The table widget.
+
+proc ::tk::table::SelectAll {w} {
+ if {[regexp {^(single|browse)$} [$w cget -selectmode]]} {
+ $w selection clear all
+ catch {$w selection set active}
+ } elseif {[$w cget -selecttitles]} {
+ $w selection set [$w cget -roworigin],[$w cget -colorigin] end
+ } else {
+ $w selection set origin end
+ }
+}
+
+# ::tk::table::ChangeWidth --
+#
+# Adjust the widget of the specified cell by $a.
+#
+# Arguments:
+# w - The table widget.
+# i - cell index
+# a - amount to adjust by
+
+proc ::tk::table::ChangeWidth {w i a} {
+ set tmp [$w index $i col]
+ if {[set width [$w width $tmp]] >= 0} {
+ $w width $tmp [incr width $a]
+ } else {
+ $w width $tmp [incr width [expr {-$a}]]
+ }
+}
+
+# tk_tableCopy --
+#
+# This procedure copies the selection from a table widget into the
+# clipboard.
+#
+# Arguments:
+# w - Name of a table widget.
+
+proc tk_tableCopy w {
+ if {[selection own -displayof $w] == "$w"} {
+ clipboard clear -displayof $w
+ catch {clipboard append -displayof $w [::tk::table::GetSelection $w]}
+ }
+}
+
+# tk_tableCut --
+#
+# This procedure copies the selection from a table widget into the
+# clipboard, then deletes the selection (if it exists in the given
+# widget).
+#
+# Arguments:
+# w - Name of a table widget.
+
+proc tk_tableCut w {
+ if {[selection own -displayof $w] == "$w"} {
+ clipboard clear -displayof $w
+ catch {
+ clipboard append -displayof $w [::tk::table::GetSelection $w]
+ $w cursel {}
+ $w selection clear all
+ }
+ }
+}
+
+# tk_tablePaste --
+#
+# This procedure pastes the contents of the clipboard to the specified
+# cell (active by default) in a table widget.
+#
+# Arguments:
+# w - Name of a table widget.
+# cell - Cell to start pasting in.
+#
+proc tk_tablePaste {w {cell {}}} {
+ if {[string compare {} $cell]} {
+ if {[catch {::tk::table::GetSelection $w} data]} return
+ } else {
+ if {[catch {::tk::table::GetSelection $w CLIPBOARD} data]} {
+ return
+ }
+ set cell active
+ }
+ tk_tablePasteHandler $w [$w index $cell] $data
+ if {[$w cget -state] == "normal"} {focus $w}
+}
+
+# tk_tablePasteHandler --
+#
+# This procedure handles how data is pasted into the table widget.
+# This handles data in the default table selection form.
+#
+# NOTE: this allows pasting into all cells except title cells,
+# even those with -state disabled
+#
+# Arguments:
+# w - Name of a table widget.
+# cell - Cell to start pasting in.
+#
+proc tk_tablePasteHandler {w cell data} {
+ #
+ # Don't allow pasting into the title cells
+ #
+ if {[$w tag includes title $cell]} {
+ return
+ }
+
+ set rows [expr {[$w cget -rows]-[$w cget -roworigin]}]
+ set cols [expr {[$w cget -cols]-[$w cget -colorigin]}]
+ set r [$w index $cell row]
+ set c [$w index $cell col]
+ set rsep [$w cget -rowseparator]
+ set csep [$w cget -colseparator]
+ ## Assume separate rows are split by row separator if specified
+ ## If you were to want multi-character row separators, you would need:
+ # regsub -all $rsep $data <newline> data
+ # set data [join $data <newline>]
+ if {[string compare {} $rsep]} { set data [split $data $rsep] }
+ set row $r
+ foreach line $data {
+ if {$row > $rows} break
+ set col $c
+ ## Assume separate cols are split by col separator if specified
+ ## Unless a -separator was specified
+ if {[string compare {} $csep]} { set line [split $line $csep] }
+ ## If you were to want multi-character col separators, you would need:
+ # regsub -all $csep $line <newline> line
+ # set line [join $line <newline>]
+ foreach item $line {
+ if {$col > $cols} break
+ $w set $row,$col $item
+ incr col
+ }
+ incr row
+ }
+}
+
+# tk::table::Sort --
+#
+# This procedure handles how data is sorted in the table widget.
+# This isn't currently used by tktable, but can be called by the user.
+# It's behavior may change in the future.
+#
+# Arguments:
+# w - Name of a table widget.
+# start - start cell of rectangle to sort
+# end - end cell of rectangle to sort
+# col - column within rectangle to sort on
+# args - passed to lsort proc (ie: -integer -decreasing)
+
+proc ::tk::table::Sort {w start end col args} {
+ set start [$w index $start]
+ set end [$w index $end]
+ scan $start %d,%d sr sc
+ scan $end %d,%d er ec
+ if {($col < $sc) || ($col > $ec)} {
+ return -code error "$col is not within sort range $sc to $ec"
+ }
+ set col [expr {$col - $sc}]
+
+ set data {}
+ for {set i $sr} {$i <= $er} {incr i} {
+ lappend data [$w get $i,$sc $i,$ec]
+ }
+
+ set i $sr
+ foreach row [eval [list lsort -index $col] $args [list $data]] {
+ $w set row $i,$sc $row
+ incr i
+ }
+}
diff --git a/library/tktable.py b/library/tktable.py
new file mode 100755
index 0000000..d293f9f
--- /dev/null
+++ b/library/tktable.py
@@ -0,0 +1,651 @@
+# Updated tktable.py wrapper for Python 2.x with Tkinter.
+# Improvements over previous version can be seen at:
+# https://sf.net/tracker2/?func=detail&aid=2244167&group_id=11464&atid=311464
+#
+# Copyright (c) 2008, Guilherme Polo
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+"""
+This contains a wrapper class for the tktable widget as well a class for using
+tcl arrays that are, in some instances, required by tktable.
+"""
+
+__author__ = "Guilherme Polo <ggpolo@gmail.com>"
+
+__all__ = ["ArrayVar", "Table"]
+
+import Tkinter
+
+def _setup_master(master):
+ if master is None:
+ if Tkinter._support_default_root:
+ master = Tkinter._default_root or Tkinter.Tk()
+ else:
+ raise RuntimeError("No master specified and Tkinter is "
+ "configured to not support default master")
+ return master
+
+class ArrayVar(Tkinter.Variable):
+ def __init__(self, master=None, name=None):
+ # Tkinter.Variable.__init__ is not called on purpose! I don't wanna
+ # see an ugly _default value in the pretty array.
+ self._master = _setup_master(master)
+ self._tk = self._master.tk
+ if name:
+ self._name = name
+ else:
+ self._name = 'PY_VAR%s' % id(self)
+
+ def get(self, index=None):
+ if index is None:
+ res = {}
+ for key in self.names():
+ res[key] = self._tk.globalgetvar(str(self), key)
+ return res
+
+ return self._tk.globalgetvar(str(self), str(index))
+
+ def names(self):
+ return self._tk.call('array', 'names', self._name)
+
+ def set(self, key, value):
+ self._tk.globalsetvar(str(self), str(key), value)
+
+
+class Table(Tkinter.Widget):
+ """Create and manipulate tables."""
+
+ _switches = ('holddimensions', 'holdselection', 'holdtags', 'holdwindows',
+ 'keeptitles', '-')
+ _tabsubst_format = ('%c', '%C', '%i', '%r', '%s', '%S', '%W')
+ _tabsubst_commands = ('browsecommand', 'browsecmd', 'command',
+ 'selectioncommand', 'selcmd',
+ 'validatecommand', 'valcmd')
+
+ def __init__(self, master=None, **kw):
+ master = _setup_master(master)
+ try:
+ master.tk.call('package', 'require', 'Tktable')
+ except Tkinter.TclError:
+ try:
+ master.tk.call('load', 'Tktable.dll', 'Tktable')
+ except Tkinter.TclError:
+ master.tk.call('load', '', 'Tktable')
+
+ Tkinter.Widget.__init__(self, master, 'table', kw)
+
+
+ def _options(self, cnf, kw=None):
+ if kw:
+ cnf = Tkinter._cnfmerge((cnf, kw))
+ else:
+ cnf = Tkinter._cnfmerge(cnf)
+
+ res = ()
+ for k, v in cnf.iteritems():
+ if callable(v):
+ if k in self._tabsubst_commands:
+ v = "%s %s" % (self._register(v, self._tabsubst),
+ ' '.join(self._tabsubst_format))
+ else:
+ v = self._register(v)
+ res += ('-%s' % k, v)
+
+ return res
+
+
+ def _tabsubst(self, *args):
+ if len(args) != len(self._tabsubst_format):
+ return args
+
+ tk = self.tk
+ c, C, i, r, s, S, W = args
+ e = Tkinter.Event()
+
+ e.widget = self
+ e.c = tk.getint(c)
+ e.i = tk.getint(i)
+ e.r = tk.getint(r)
+ e.C = (e.r, e.c)
+ try:
+ e.s = tk.getint(s)
+ except Tkinter.TclError:
+ e.s = s
+ try:
+ e.S = tk.getint(S)
+ except Tkinter.TclError:
+ e.S = S
+ e.W = W
+
+ return (e,)
+
+
+ def _handle_switches(self, args):
+ args = args or ()
+ return tuple(('-%s' % x) for x in args if x in self._switches)
+
+
+ def activate(self, index):
+ """Set the active cell to the one indicated by index."""
+ self.tk.call(self._w, 'activate', index)
+
+
+ def bbox(self, first, last=None):
+ """Return the bounding box for the specified cell (range) as a
+ 4-tuple of x, y, width and height in pixels. It clips the box to
+ the visible portion, if any, otherwise an empty tuple is returned."""
+ return self._getints(self.tk.call(self._w, 'bbox', first, last)) or ()
+
+
+ def clear(self, option, first=None, last=None):
+ """This is a convenience routine to clear certain state information
+ managed by the table. first and last represent valid table indices.
+ If neither are specified, then the command operates on the whole
+ table."""
+ self.tk.call(self._w, 'clear', option, first, last)
+
+
+ def clear_cache(self, first=None, last=None):
+ """Clear the specified section of the cache, if the table has been
+ keeping one."""
+ self.clear('cache', first, last)
+
+
+ def clear_sizes(self, first=None, last=None):
+ """Clear the specified row and column areas of specific height/width
+ dimensions. When just one index is specified, for example 2,0, that
+ is interpreted as row 2 and column 0."""
+ self.clear('sizes', first, last)
+
+
+ def clear_tags(self, first=None, last=None):
+ """Clear the specified area of tags (all row, column and cell tags)."""
+ self.clear('tags', first, last)
+
+
+ def clear_all(self, first=None, last=None):
+ """Perform all of the above clear functions on the specified area."""
+ self.clear('all', first, last)
+
+
+ def curselection(self, value=None):
+ """With no arguments, it returns the sorted indices of the currently
+ selected cells. Otherwise it sets all the selected cells to the given
+ value if there is an associated ArrayVar and the state is not
+ disabled."""
+ result = self.tk.call(self._w, 'curselection', value)
+ if value is None:
+ return result
+
+
+ def curvalue(self, value=None):
+ """If no value is given, the value of the cell being edited (indexed
+ by active) is returned, else it is set to the given value. """
+ return self.tk.call(self._w, 'curvalue', value)
+
+
+ def delete_active(self, index1, index2=None):
+ """Deletes text from the active cell. If only one index is given,
+ it deletes the character after that index, otherwise it deletes from
+ the first index to the second. index can be a number, insert or end."""
+ self.tk.call(self._w, 'delete', 'active', index1, index2)
+
+
+ def delete_cols(self, index, count=None, switches=None):
+ args = self._handle_switches(switches) + (index, count)
+ self.tk.call(self._w, 'delete', 'cols', *args)
+
+
+ def delete_rows(self, index, count=None, switches=None):
+ args = self._handle_switches(switches) + (index, count)
+ self.tk.call(self._w, 'delete', 'rows', *args)
+
+
+ def get(self, first, last=None):
+ """Returns the value of the cells specified by the table indices
+ first and (optionally) last."""
+ return self.tk.call(self._w, 'get', first, last)
+
+
+ def height(self, row=None, **kwargs):
+ """If row and kwargs are not given, a list describing all rows for
+ which a width has been set is returned.
+ If row is given, the height of that row is returnd.
+ If kwargs is given, then it sets the key/value pairs, where key is a
+ row and value represents the height for the row."""
+ if row is None and not kwargs:
+ pairs = self.tk.splitlist(self.tk.call(self._w, 'height'))
+ return dict(pair.split() for pair in pairs)
+ elif row:
+ return int(self.tk.call(self._w, 'height', str(row)))
+
+ args = Tkinter._flatten(kwargs.items())
+ self.tk.call(self._w, 'height', *args)
+
+
+ def hidden(self, *args):
+ """When called without args, it returns all the hidden cells (those
+ cells covered by a spanning cell). If one index is specified, it
+ returns the spanning cell covering that index, if any. If multiple
+ indices are specified, it returns 1 if all indices are hidden cells,
+ 0 otherwise."""
+ return self.tk.call(self._w, 'hidden', *args)
+
+
+ def icursor(self, arg=None):
+ """If arg is not specified, return the location of the insertion
+ cursor in the active cell. Otherwise, set the cursor to that point in
+ the string.
+
+ 0 is before the first character, you can also use insert or end for
+ the current insertion point or the end of the text. If there is no
+ active cell, or the cell or table is disabled, this will return -1."""
+ return self.tk.call(self._w, 'icursor', arg)
+
+
+ def index(self, index, rc=None):
+ """Return the integer cell coordinate that corresponds to index in the
+ form row, col. If rc is specified, it must be either 'row' or 'col' so
+ only the row or column index is returned."""
+ res = self.tk.call(self._w, 'index', index, rc)
+ if rc is None:
+ return res
+ else:
+ return int(res)
+
+
+ def insert_active(self, index, value):
+ """The value is a text string which is inserted at the index postion
+ of the active cell. The cursor is then positioned after the new text.
+ index can be a number, insert or end. """
+ self.tk.call(self._w, 'insert', 'active', index, value)
+
+
+ def insert_cols(self, index, count=None, switches=None):
+ args = self._handle_switches(switches) + (index, count)
+ self.tk.call(self._w, 'insert', 'cols', *args)
+
+
+ def insert_rows(self, index, count=None, switches=None):
+ args = self._handle_switches(switches) + (index, count)
+ self.tk.call(self._w, 'insert', 'rows', *args)
+
+
+ #def postscript(self, **kwargs):
+ # """Skip this command if you are under Windows.
+ #
+ # Accepted options:
+ # colormap, colormode, file, channel, first, fontmap, height,
+ # last, pageanchor, pageheight, pagewidth, pagex, pagey, rotate,
+ # width, x, y
+ # """
+ # args = ()
+ # for key, val in kwargs.iteritems():
+ # args += ('-%s' % key, val)
+ #
+ # return self.tk.call(self._w, 'postscript', *args)
+
+
+ def reread(self):
+ """Rereads the old contents of the cell back into the editing buffer.
+ Useful for a key binding when <Escape> is pressed to abort the edit
+ (a default binding)."""
+ self.tk.call(self._w, 'reread')
+
+
+ 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):
+ self.tk.call(self._w, 'see', index)
+
+
+ def selection_anchor(self, index):
+ self.tk.call(self._w, 'selection', 'anchor', index)
+
+
+ def selection_clear(self, first, last=None):
+ self.tk.call(self._w, 'selection', 'clear', first, last)
+
+
+ def selection_includes(self, index):
+ return self.getboolean(self.tk.call(self._w, 'selection', 'includes',
+ index))
+
+
+ def selection_set(self, first, last=None):
+ self.tk.call(self._w, 'selection', 'set', first, last)
+
+
+ def set(self, rc=None, index=None, *args, **kwargs):
+ """If rc is specified (either 'row' or 'col') then it is assumes that
+ args (if given) represents values which will be set into the
+ subsequent columns (if row is specified) or rows (for col).
+ If index is not None and args is not given, then it will return the
+ value(s) for the cell(s) specified.
+
+ If kwargs is given, assumes that each key in kwargs is a index in this
+ table and sets the specified index to the associated value. Table
+ validation will not be triggered via this method.
+
+ Note that the table must have an associated array (defined through the
+ variable option) in order to this work."""
+ if not args and index is not None:
+ if rc:
+ args = (rc, index)
+ else:
+ args = (index, )
+ return self.tk.call(self._w, 'set', *args)
+
+ if rc is None:
+ args = Tkinter._flatten(kwargs.items())
+ self.tk.call(self._w, 'set', *args)
+ else:
+ self.tk.call(self._w, 'set', rc, index, args)
+
+
+ def spans(self, index=None, **kwargs):
+ """Manipulate row/col spans.
+
+ When called with no arguments, all known spans are returned as a dict.
+ When called with only the index, the span for that index only is
+ returned, if any. Otherwise kwargs is assumed to contain keys/values
+ pairs used to set spans. A span starts at the row,col defined by a key
+ and continues for the specified number of rows,cols specified by
+ its value. A span of 0,0 unsets any span on that cell."""
+ if kwargs:
+ args = Tkinter._flatten(kwargs.items())
+ self.tk.call(self._w, 'spans', *args)
+ else:
+ return self.tk.call(self._w, 'spans', index)
+
+
+ def tag_cell(self, tagname, *args):
+ return self.tk.call(self._w, 'tag', 'cell', tagname, *args)
+
+
+ def tag_cget(self, tagname, option):
+ return self.tk.call(self._w, 'tag', 'cget', tagname, '-%s' % option)
+
+
+ def tag_col(self, tagname, *args):
+ return self.tk.call(self._w, 'tag', 'col', tagname, *args)
+
+
+ def tag_configure(self, tagname, option=None, **kwargs):
+ """Query or modify options associated with the tag given by tagname.
+
+ If no option is specified, a dict describing all of the available
+ options for tagname is returned. If option is specified, then the
+ command returns a list describing the one named option. Lastly, if
+ kwargs is given then it corresponds to option-value pairs that should
+ be modified."""
+ if option is None and not kwargs:
+ split1 = self.tk.splitlist(
+ self.tk.call(self._w, 'tag', 'configure', tagname))
+
+ result = {}
+ for item in split1:
+ res = self.tk.splitlist(item)
+ result[res[0]] = res[1:]
+
+ return result
+
+ elif option:
+ return self.tk.call(self._w, 'tag', 'configure', tagname,
+ '-%s' % option)
+
+ else:
+ args = ()
+ for key, val in kwargs.iteritems():
+ args += ('-%s' % key, val)
+
+ self.tk.call(self._w, 'tag', 'configure', tagname, *args)
+
+
+ def tag_delete(self, tagname):
+ self.tk.call(self._w, 'tag', 'delete', tagname)
+
+
+ def tag_exists(self, tagname):
+ return self.getboolean(self.tk.call(self._w, 'tag', 'exists', tagname))
+
+
+ def tag_includes(self, tagname, index):
+ return self.getboolean(self.tk.call(self._w, 'tag', 'includes',
+ tagname, index))
+
+
+ def tag_lower(self, tagname, belowthis=None):
+ self.tk.call(self._w, 'tag', 'lower', belowthis)
+
+
+ def tag_names(self, pattern=None):
+ return self.tk.call(self._w, 'tag', 'names', pattern)
+
+
+ def tag_raise(self, tagname, abovethis=None):
+ self.tk.call(self._w, 'tag', 'raise', tagname, abovethis)
+
+
+ def tag_row(self, tagname, *args):
+ return self.tk.call(self._w, 'tag', 'row', tagname, *args)
+
+
+ def validate(self, index):
+ """Explicitly validates the specified index based on the current
+ callback set for the validatecommand option. Return 0 or 1 based on
+ whether the cell was validated."""
+ return self.tk.call(self._w, 'validate', index)
+
+
+ @property
+ def version(self):
+ """Return tktable's package version."""
+ return self.tk.call(self._w, 'version')
+
+
+ def width(self, column=None, **kwargs):
+ """If column and kwargs are not given, a dict describing all columns
+ for which a width has been set is returned.
+ If column is given, the width of that column is returnd.
+ If kwargs is given, then it sets the key/value pairs, where key is a
+ column and value represents the width for the column."""
+ if column is None and not kwargs:
+ pairs = self.tk.splitlist(self.tk.call(self._w, 'width'))
+ return dict(pair.split() for pair in pairs)
+ elif column:
+ return int(self.tk.call(self._w, 'width', str(column)))
+
+ args = Tkinter._flatten(kwargs.items())
+ self.tk.call(self._w, 'width', *args)
+
+
+ def window_cget(self, index, option):
+ return self.tk.call(self._w, 'window', 'cget', index, option)
+
+
+ def window_configure(self, index, option=None, **kwargs):
+ """Query or modify options associated with the embedded window given
+ by index. This should also be used to add a new embedded window into
+ the table.
+
+ If no option is specified, a dict describing all of the available
+ options for index is returned. If option is specified, then the
+ command returns a list describing the one named option. Lastly, if
+ kwargs is given then it corresponds to option-value pairs that should
+ be modified."""
+ if option is None and not kwargs:
+ return self.tk.call(self._w, 'window', 'configure', index)
+ elif option:
+ return self.tk.call(self._w, 'window', 'configure', index,
+ '-%s' % option)
+ else:
+ args = ()
+ for key, val in kwargs.iteritems():
+ args += ('-%s' % key, val)
+
+ self.tk.call(self._w, 'window', 'configure', index, *args)
+
+
+ def window_delete(self, *indexes):
+ self.tk.call(self._w, 'window', 'delete', *indexes)
+
+
+ def window_move(self, index_from, index_to):
+ self.tk.call(self._w, 'window', 'move', index_from, index_to)
+
+
+ def window_names(self, pattern=None):
+ return self.tk.call(self._w, 'window', 'names', pattern)
+
+
+ def xview(self, index=None):
+ """If index is not given a tuple containing two fractions is returned,
+ each fraction is between 0 and 1. Together they describe the
+ horizontal span that is visible in the window.
+
+ If index is given the view in the window is adjusted so that the
+ column given by index is displayed at the left edge of the window."""
+ res = self.tk.call(self._w, 'xview', index)
+ if index is None:
+ return self._getdoubles(res)
+
+
+ def xview_moveto(self, fraction):
+ """Adjusts the view in the window so that fraction of the total width
+ of the table text is off-screen to the left. The fraction parameter
+ must be a fraction between 0 and 1."""
+ self.tk.call(self._w, 'xview', 'moveto', fraction)
+
+
+ def xview_scroll(self, number, what):
+ """Shift the view in the window left or right according to number and
+ what. The 'number' parameter must be an integer. The 'what' parameter
+ must be either units or pages or an abbreviation of one of these.
+
+ If 'what' is units, the view adjusts left or right by number cells on
+ the display; if it is pages then the view adjusts by number screenfuls.
+ If 'number' is negative then cells farther to the left become visible;
+ if it is positive then cells farther to the right become visible. """
+ self.tk.call(self._w, 'xview', 'scroll', number, what)
+
+
+ def yview(self, index=None):
+ """If index is not given a tuple containing two fractions is returned,
+ each fraction is between 0 and 1. The first element gives the position
+ of the table element at the top of the window, relative to the table
+ as a whole. The second element gives the position of the table element
+ just after the last one in the window, relative to the table as a
+ whole.
+
+ If index is given the view in the window is adjusted so that the
+ row given by index is displayed at the top of the window."""
+ res = self.tk.call(self._w, 'yview', index)
+ if index is None:
+ return self._getdoubles(res)
+
+
+ def yview_moveto(self, fraction):
+ """Adjusts the view in the window so that the element given by
+ fraction appears at the top of the window. The fraction parameter
+ must be a fraction between 0 and 1."""
+ self.tk.call(self._w, 'yview', 'moveto', fraction)
+
+
+ def yview_scroll(self, number, what):
+ """Adjust the view in the window up or down according to number and
+ what. The 'number' parameter must be an integer. The 'what' parameter
+ must be either units or pages or an abbreviation of one of these.
+
+ If 'what' is units, the view adjusts up or down by number cells; if it
+ is pages then the view adjusts by number screenfuls.
+ If 'number' is negative then earlier elements become visible; if it
+ is positive then later elements become visible. """
+ self.tk.call(self._w, 'yview', 'scroll', number, what)
+
+
+# Sample test taken from tktable cvs, original tktable python wrapper
+def sample_test():
+ from Tkinter import Tk, Label, Button
+
+ def test_cmd(event):
+ if event.i == 0:
+ return '%i, %i' % (event.r, event.c)
+ else:
+ return 'set'
+
+ def browsecmd(event):
+ print "event:", event.__dict__
+ print "curselection:", test.curselection()
+ print "active cell index:", test.index('active')
+ print "active:", test.index('active', 'row')
+ print "anchor:", test.index('anchor', 'row')
+
+ root = Tk()
+
+ var = ArrayVar(root)
+ for y in range(-1, 4):
+ for x in range(-1, 5):
+ index = "%i,%i" % (y, x)
+ var.set(index, index)
+
+ label = Label(root, text="Proof-of-existence test for Tktable")
+ label.pack(side = 'top', fill = 'x')
+
+ quit = Button(root, text="QUIT", command=root.destroy)
+ quit.pack(side = 'bottom', fill = 'x')
+
+ test = Table(root,
+ rows=10,
+ cols=5,
+ state='disabled',
+ width=6,
+ height=6,
+ titlerows=1,
+ titlecols=1,
+ roworigin=-1,
+ colorigin=-1,
+ selectmode='browse',
+ selecttype='row',
+ rowstretch='unset',
+ colstretch='last',
+ browsecmd=browsecmd,
+ flashmode='on',
+ variable=var,
+ usecommand=0,
+ command=test_cmd)
+ test.pack(expand=1, fill='both')
+ test.tag_configure('sel', background = 'yellow')
+ test.tag_configure('active', background = 'blue')
+ test.tag_configure('title', anchor='w', bg='red', relief='sunken')
+ root.mainloop()
+
+if __name__ == '__main__':
+ sample_test()