View · Search · Index
No registered users in community xowiki
in last 10 minutes

[Xotcl] little spreadsheet in NX

From: Gustaf Neumann <neumann_at_wu.ac.at>
Date: Thu, 19 May 2011 20:36:09 +0200

Dear Community,

Here is a little spreadsheet program in NX. Maybe, someone has a usage
for it.
best regards -gustaf




===============================================================================
# A small Spreadsheet implementation, originally developed by Richard
# Suchenwirth in plain Tcl (see http://wiki.tcl.tk/1287). The
# spreadsheet was rewritten in an object oriented manner as a design
# study in NX by Gustaf Neumann in May 2011.
#
package require nx
package require Tk

##############################################################################
# Class TkUtils
#
# A small support class to ease syntactically the reference to
# instance variables and the registration of callbacks.
##############################################################################
nx::Class create TkUtils {

   :protected method bindvar {name} {
     :require namespace
     return [nx::self]::$name
   }
   :protected method callback {name args} {
     return [list [nx::self] $name {*}$args]
   }
}

##############################################################################
# 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.
   #
   :attribute {rows:integer 3}
   :attribute {cols:integer 2}
   :attribute {width:integer 8}

   #
   # If no widget is provided, use the name of the object as widget
   # name.
   #
   :attribute {widget ".[namespace tail [self]]"}

   #
   # Use the methods of TkUtils in this class.
   #
   :mixin TkUtils

   #
   # 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 widgets
pack [x widget] [y widget] -fill both

===================================================================