A small calculator can be useful to evaluate a single formula involving a few parameters/variables. This formula calculator class constructs a small calculator which evaluates the formula or its inverse with respect to a particular parameter, according to specified code for each result. RVB
First, in incr tcl:
class ForCalc { public variable title "formula calculator" { if {[info exists _Comp(text)]} { configure $_Comp(text) $title } } private variable _Info private variable _Comp # ======================================================================== # constants that can be used in recalculation code # ======================================================================== common _pi_ [expr {acos(-1)}] common _tabs_ 273.16 ;# T(O deg C) deg K common _kb_ 1.380622e-23 ;# (J/K) : boltzmann constant common _kbev_ 8.61708e-05 ;# (eV/K) : boltzmann constant common _hbar_ 6.58218e-16 ;# (eV-s) : planck constant common _co_ 2.99792e+10 ;# (cm/s) : speed of light in vacuum common _qe_ 1.602192e-19 ;# (C) : unit charge common _eo_ 8.854215e-14 ;# (F/cm) : permittivity of free space common _eox_ 3.45314385e-13 ;# F/cm : permittivity of SiO2 common _esi_ 1.03594315e-12 ;# F/cm : permittivity of Si common _mo_ 9.10956e-31 ;# (kg) : electron rest mass # ======================================================================== # METHOD: constructor # ARGUMENTS: # % par_specs # list of lists of: # parameter name # parameter label for calculator display # unit label for calculator display # default value of the parameter # recalculation key: pointer to code to evaluate # when return is typed in parameter entry box # % recalc_specs # list of lists of: # recalculation key # recalculation code # recalculation code should set the value(s) of one or more of the # parameters specified in par_specs # % args # CONFIGURATION-OPTIONS # CONFIGURATION-OPTIONS: # % -title <title> # specify the calculator title # ======================================================================== constructor {par_specs recalc_specs args} { foreach {par label unit value recalc} $par_specs { lappend _Info(pars) $par set _Info($par-label) $label set _Info($par-unit) $unit set _Info($par-value) $value set _Info($par-recalc) $recalc } foreach {key code} $recalc_specs { set _Info($key-code) $code } eval configure $args _gui } # ======================================================================== # METHOD: destructor # RESULTS: # * destroys calculator gui # ======================================================================== destructor { if {[info exists _Comp(top)]} { destroy $_Comp(top) } } # ======================================================================== # METHOD: _gui (private) # PURPOSE: build calculator gui # ======================================================================== private method _gui {} { set top .[namespace tail $this] toplevel $top set _Comp(top) $top #--------------------------------------------------------------------- # main layout #--------------------------------------------------------------------- frame $top.text -borderwidth 2 -relief raised pack $top.text -side top -expand 1 -fill both frame $top.pars -borderwidth 2 -relief raised pack $top.pars -side top -expand 1 -fill both set _Comp(text) $top.text.text set _Comp(quit) $top.text.quit message $_Comp(text) -justify center -text $title -aspect 800 pack $_Comp(text) -side left -expand 1 -fill both button $_Comp(quit) -text quit -command "delete object $this" pack $_Comp(quit) -side left -padx 10 -pady 10 -expand 1 -fill x #--------------------------------------------------------------------- # parameter entry #--------------------------------------------------------------------- foreach par $_Info(pars) { set _Comp($par-frame) $top.pars.$par set _Comp($par-label) $_Comp($par-frame).l set _Comp($par-entry) $_Comp($par-frame).e set _Comp($par-unit) $_Comp($par-frame).u frame $_Comp($par-frame) pack $_Comp($par-frame) -side top -expand 1 -fill x label $_Comp($par-label) -text $_Info($par-label) -width 30 -anchor w pack $_Comp($par-label) -side left -expand 1 -fill x entry $_Comp($par-entry) -relief sunken pack $_Comp($par-entry) -side left -expand 1 -fill x label $_Comp($par-unit) -text $_Info($par-unit) -width 15 -anchor c pack $_Comp($par-unit) -side left -expand 1 -fill x $_Comp($par-entry) insert 0 $_Info($par-value) bind $_Comp($par-entry) <Return> [code $this _recalculate $par] } _recalculate [lindex $_Info(pars) 0] wm title $top $title } # ======================================================================== # METHOD: _recalculate (private) # PURPOSE: recalculate parameter values # ARGUMENTS: # % par_changed # name of parameter which had <Return> in entry # ======================================================================== private method _recalculate {par_changed} { foreach par $_Info(pars) { set $par [$_Comp($par-entry) get] } set recalc $_Info($par_changed-recalc) if {[info exists _Info($recalc-code)]} { eval $_Info($recalc-code) } foreach par $_Info(pars) { $_Comp($par-entry) delete 0 end $_Comp($par-entry) insert 0 [set $par] } } } Here is a basic tcl version namespace eval ::forcalc { namespace export ForCalc variable Info variable Comp # ======================================================================== # constants that can be used in recalculation code # ======================================================================== variable _pi_ [expr {acos(-1)}] variable _tabs_ 273.16 ;# T(O deg C) deg K variable _kb_ 1.380622e-23 ;# (J/K) : boltzmann constant variable _kbev_ 8.61708e-05 ;# (eV/K) : boltzmann constant variable _hbar_ 6.58218e-16 ;# (eV-s) : planck constant variable _co_ 2.99792e+10 ;# (cm/s) : speed of light in vacuum variable _qe_ 1.602192e-19 ;# (C) : unit charge variable _eo_ 8.854215e-14 ;# (F/cm) : permittivity of free space variable _eox_ 3.45314385e-13 ;# F/cm : permittivity of SiO2 variable _esi_ 1.03594315e-12 ;# F/cm : permittivity of Si variable _mo_ 9.10956e-31 ;# (kg) : electron rest mass # ======================================================================== # PROC: ForCalc # PURPOSE: create and configure a new calculator # ARGUMENTS: # % this # a unique calculator handle # % par_specs # list of lists of: # parameter name # parameter label for calculator display # unit label for calculator display # default value of the parameter # recalculation key: pointer to code to evaluate # when return is typed in parameter entry box # % recalc_specs # list of lists of: # recalculation key # recalculation code # recalculation code should set the value(s) of one or more of the # parameters specified in par_specs # % args # CONFIGURATION-OPTIONS # CONFIGURATION-OPTIONS: # % -title <title> # specify the calculator title # ======================================================================== proc ForCalc {this par_specs recalc_specs args} { variable Info variable Comp if {[info exists Comp($this-top)]} { error "\"$this\" calculator already exists" } set title "formula calculator" while {[llength $args] > 0} { set arg [lindex $args 0] set args [lrange $args 1 end] switch -- $arg { -title { set title [lindex $args 0] set args [lrange $args 1 end] } default { error "option \"$arg\" is not supported" } } } foreach {par label unit value recalc} $par_specs { lappend Info($this-pars) $par set Info($this-$par-label) $label set Info($this-$par-unit) $unit set Info($this-$par-value) $value set Info($this-$par-recalc) $recalc } foreach {key code} $recalc_specs { set Info($this-$key-code) $code } set top .$this toplevel $top set Comp($this-top) $top frame $top.text -borderwidth 2 -relief raised pack $top.text -side top -expand 1 -fill both frame $top.pars -borderwidth 2 -relief raised pack $top.pars -side top -expand 1 -fill both message $top.text.msg -justify center -text $title -aspect 800 pack $top.text.msg -side left -expand 1 -fill both button $top.text.but -text quit \ -command [list ::forcalc::_destroy $this] pack $top.text.but -side left -padx 10 -pady 10 -expand 1 -fill x #--------------------------------------------------------------------- # parameter entry #--------------------------------------------------------------------- foreach par $Info($this-pars) { set Comp($this-$par-frame) $top.pars.$par set Comp($this-$par-label) $Comp($this-$par-frame).l set Comp($this-$par-entry) $Comp($this-$par-frame).e set Comp($this-$par-unit) $Comp($this-$par-frame).u frame $Comp($this-$par-frame) pack $Comp($this-$par-frame) -side top -expand 1 -fill x label $Comp($this-$par-label) -text $Info($this-$par-label) \ -width 30 -anchor w pack $Comp($this-$par-label) -side left -expand 1 -fill x entry $Comp($this-$par-entry) -relief sunken pack $Comp($this-$par-entry) -side left -expand 1 -fill x label $Comp($this-$par-unit) -text $Info($this-$par-unit) \ -width 15 -anchor c pack $Comp($this-$par-unit) -side left -expand 1 -fill x $Comp($this-$par-entry) insert 0 $Info($this-$par-value) bind $Comp($this-$par-entry) <Return> \ [list forcalc::_recalculate $this $par] } _recalculate $this [lindex $Info($this-pars) 0] wm title $top $title return $this } # ======================================================================== # METHOD: _destroy # ARGUMENTS: # % this # The calculator handle # RESULTS: # * destroys calculator gui # * unregisters calculator handle # ======================================================================== proc _destroy {this} { variable Info variable Comp destroy $Comp($this-top) foreach item [array names Info $this-*] { unset Info($item) } foreach item [array names Comp $this-*] { unset Comp($item) } } # ======================================================================== # PROC: _recalculate # PURPOSE: recalculate parameter values # ARGUMENTS: # % this # the calculator handle # % par_changed # name of parameter which had <Return> in entry # ======================================================================== proc _recalculate {this par_changed} { variable Info variable Comp variable _pi_ variable _tabs_ variable _kb_ variable _kbev_ variable _hbar_ variable _co_ variable _qe_ variable _eo_ variable _eox_ variable _esi_ variable _mo_ foreach par $Info($this-pars) { set $par [$Comp($this-$par-entry) get] } set recalc $Info($this-$par_changed-recalc) if {[info exists Info($this-$recalc-code)]} { eval $Info($this-$recalc-code) } foreach par $Info($this-pars) { $Comp($this-$par-entry) delete 0 end $Comp($this-$par-entry) insert 0 [set $par] } } } namespace import forcalc::ForCalc Here are several examples: A field mowing calculator. Calculates the total time required to mow a field at a constant speed and swath and number of turns, or the number of turns required given total time, swath, and speed. ForCalc mowcalc { n "number of turns" {} 43 t dr "swath" ft 3 n s "speed" ft/s 3 n t "total time" min 100 n } { t {set t [expr {$n*($n+1)*($dr*$_pi_)/($s*60.0)}]} n {set n [expr {sqrt(0.25 + ($s*60.0*$t)/($dr*$_pi_)) - 0.5}]} } -title "field mow calculator" An integrated resistor calculator. Given a target resistance and width, calculates the required resistor length. Given a specified length and width, calculates the resistance. ForCalc rescalc { r "Resistance" ohms 1000 l rho "Sheet Resistance" ohms/square 200 r dw "delta W" um 0.02 r dl "delta L" um -0.10 r l "Length" um 10.0 r w "Width" um 1.0 l } { l {set l [expr {1.0*$r*($w-$dw)/$rho + $dl}]} r {set r [expr {1.0*$rho*($l-$dl)/($w-$dw)}]} } -title "integrated circuit resistance calculator" A fixed-rate mortgage payment calculator. ForCalc mortcalc { principal "Principal" $ 100000 payment years "Years" y 30 payment rate "Rate" % 8.00 payment payments_year "Payments/Year" {} 12 payment payment "Payment" $ 733.76 principal } { payment { set a [expr {1.0e-2*$rate/$payments_year}] set N [expr {1.0*$payments_year*$years}] set per_thousand [expr {1.0e+3*$a/(1-pow(1+$a,-$N))}] set payment [expr {1.0e-3*$principal*$per_thousand}] set payment [format "%-10.2f" $payment] } principal { set a [expr {1.0e-2*$rate/$payments_year}] set N [expr {1.0*$payments_year*$years}] set per_thousand [expr {1.0e+3*$a/(1-pow(1+$a,-$N))}] set principal [expr {1.0e+3*$payment/$per_thousand}] set principal [format "%-10.2f" $principal] } } -title "fixed rate mortgage calculator" A watering calculator. ForCalc watercalc { area "area" ft^2 1000 t gpm "gallons/minute" gal/min 1.5 t d "depth" in 0.5 t t "total time" hour 1.5 d } { t { set ft3_g [expr {231.0/1728.0}] set t [expr {(1.0*$area*(1.0*$d/12))/(60.0*$gpm*$ft3_g)}] } d { set ft3_g [expr {231.0/1728.0}] set d [expr {(12.0*($t*60.0)*$gpm*$ft3_g)/$area}] } } -title "watering calculator"