1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
|
# A small Spreadsheet implementation, originally developed by Richard
# Suchenwirth in plain Tcl (see https://wiki.tcl-lang.org/1287). The
# spreadsheet was rewritten in an object oriented manner as a design
# study in NX by Gustaf Neumann in May 2011.
#
# image::tk-spread.png[]
#
package require Tk
package require nx::trait
##############################################################################
# Class SpreadSheet
#
# The SpreadSheet computes simply totals for rows and columns.
##############################################################################
nx::Class create SpreadSheet {
#
# The following attributes can be used for configuring the
# spreadsheet.
#
:property {rows:integer 3}
:property {cols:integer 2}
:property {width:integer 8}
#
# If no widget is provided, use the name of the object as widget
# name.
#
:property {widget ".[namespace tail [self]]"}
#
# Use the nx callback trait
#
:require trait nx::trait::callback
#
# The method "cell" hides the internal respresentation and sets a
# cell to a value.
#
:method cell {pair value} {
set :data($pair) $value
}
#
# The constructor builds the SpreadSheet matrix via multiple text
# entry fields.
#
:method init {} {
set :last ${:rows},${:cols} ;# keep grand total field
trace var [:bindvar data] w [:callback redo]
frame ${:widget}
for {set y 0} {$y <= ${:rows}} {incr y} {
set row [list]
for {set x 0} {$x <= ${:cols}} {incr x} {
set e [entry ${:widget}.$y,$x -width ${:width} \
-textvar [:bindvar data($y,$x)] -just right]
if {$x==${:cols} || $y==${:rows}} {
$e config -state disabled -background grey -relief flat
}
lappend row $e
}
grid {*}$row -sticky news
}
$e config -relief solid
}
#
# The method "redo" is triggered via the updates in the cells
#
:public method redo {varname el op} {
if {$el ne ${:last}} {
lassign [split $el ,] y x
if {$x ne ""} {
:sum $y,* $y,${:cols}
:sum *,$x ${:rows},$x
} ;# otherwise 'el' was not a cell index
} ;# prevent endless recalculation of grand total
}
#
# The method "sum" adds the values matched by pattern (typically a
# row or column) and sets finally the target column with the total
#
:method sum {pat target} {
set sum 0
set total "" ;# default if no addition succeeds
foreach {i value} [array get :data $pat] {
if {$i != $target} {
if {[string is double -strict $value]} {
set total [set sum [expr {$sum + $value}]]
}
}
}
:cell $target $total
}
}
# Build spreadsheet "x"
SpreadSheet create x {
# populate with some values
:cell 0,0 Spread1
:cell 1,0 47
:cell 2,1 11
}
# Build spreadsheet "y"
SpreadSheet create y -rows 4 -cols 4 {
:cell 0,0 Spread2
:cell 1,0 12
:cell 2,2 22
}
# Pack the spreadsheets into one pane
pack [x cget -widget] [y cget -widget] -fill both
|