Where: ftp://ftp.procplace.com/pub/tcl/sorted/packages-7.6/math/calculator/calculator.tk.tar.gzDescription: Simple Tk calculator. Updated: 09/1997 Contact: [Richard Booth]
recently revisited rvbThe original calculator dates from pre- "switch" command days (and before too much experience with Tcl/Tk!). It was also "pack append", back then, rather than "pack $window".Here is a slightly more modern, updated version. Some of the changes are:
- different layout
- button procs instead of single punch button dispatch procedure
- backspace for correcting number entry
- OFF button
- ARC and HYP buttons
- bitmaps for pi and sqrt
- menu of additional commands, including random number and logging
- use tk_messageBox for help and error messages

#############################################################################
# NAME : calc.tk
# PURPOSE : calculator
# AUTHOR : rvb
#############################################################################
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# globals
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
set buttons {
OFF HELP const pi AC
DRG MENU put e CE
HYP sin cos tan sqrt
ARC x! 10^x exp x^2
M+ 1/x log ln y^x
M- EE ( ) /
STO 7 8 9 *
RCL 4 5 6 -
XCH 1 2 3 +
CLR 0 . +/- =
}
set buttoncommands {
OFF HELP const pi AC
DRG MENU put e CE
HYP trig trig trig sqrt
ARC fact tenx exp sqr
M+ inv log ln ytox
M- EE lpar rpar mdiv
STO num num num mdiv
RCL num num num pmin
XCH num num num pmin
CLR num pt porm eq
}
set bindings {
Control-Key-q H less numbersign A
d question greater at C
h s c t r
a exclam X x dollar
Control-Key-p percent L l asciicircum
Control-Key-m e parenleft parenright slash
Control-Key-s Key-7 Key-8 Key-9 asterisk
Control-Key-r Key-4 Key-5 Key-6 minus
Control-Key-x Key-1 Key-2 Key-3 plus
Control-Key-c Key-0 period asciitilde equal
}
set menuentries {
"random number" "RAND"
"random seed" "SEED"
"list stack" "STK?"
"set precision" "PREC"
"logging" "LOG"
}
set constants {
"273.16 :Tabs :T(O deg C) deg K"
"1.380622E-23 :kB (J/K) :boltzmann constant"
"8.61708E-05 :kB (eV/K) :boltzmann constant"
"6.58218E-16 :hbar (eV-s) :planck constant"
"2.99792E+10 :co (cm/s) :speed of light in vacuum"
"1.602192E-19 :qe (C) :unit charge"
"8.854215E-14 :eo (F/cm) :permittivity of free space"
"9.10956E-31 :mo (kg) :electron rest mass"
"11.7 :ksi :relative permittivity (Si)"
"3.9 :kox :relative permittivity (SiO2)"
"1.03594315e-12 :esi (F/cm) :permittivity (Si)"
"3.45314385e-13 :eox (F/cm) :permittivity (SiO2)"
}
set Bitmap(pi) [image create bitmap -data "
#define pi_width 16
#define pi_height 16
static unsigned char pi_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x3f, 0x12, 0x04,
0x10, 0x04, 0x10, 0x04, 0x10, 0x04, 0x10, 0x04, 0x10, 0x04, 0x10, 0x04,
0x10, 0x04, 0x10, 0x04, 0x1c, 0x04, 0x00, 0x00
};
"]
set Bitmap(sqrt) [image create bitmap -data "
#define sqrt_width 16
#define sqrt_height 16
static unsigned char sqrt_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x02, 0x00, 0x02, 0x00, 0x03,
0x00, 0x01, 0x00, 0x01, 0x87, 0x01, 0x8c, 0x00, 0x98, 0x00, 0xf0, 0x00,
0x60, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00
};
"]
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# button commands:
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
proc num {button} {
global mode x
switch -- $mode {
> {
set x $button
set mode I
}
I - F {
append x $button
}
E - X {
set e [string index $x end]
set x [string range $x 0 end-2]$e$button
set mode X
}
}
dispx
}
proc pt {button} {
global mode x
switch -- $mode {
> {
set x 0.
set mode F
}
I {
append x .
set mode F
}
}
dispx
}
proc EE {button} {
global mode x
switch -- $mode {
I - F {
append x E+00
set mode E
}
}
dispx
}
proc porm {button} {
global mode x
switch -- $mode {
> - I - F {
set s [string index $x 0]
if {$s == "-"} {
set x [string range $x 1 end]
} else {
set x -$x
}
}
E - X {
set s [string index $x end-2]
if {$s == "+"} {
set s -
} else {
set s +
}
set x [string replace $x end-2 end-2 $s]
}
}
dispx
}
proc OFF {button} {
destroy .
return
}
proc CE {button} {
CLX
}
proc AC {button} {
global PTR drg arc hyp tcl_precision
CLX
CLM
set PTR 0
set drg 0
set hyp 0
set arc 0
set tcl_precision 16
dispa
}
proc pi {button} {
STX acos(-1)
}
proc e {button} {
STX exp(1)
}
proc const {button} {
set u [constant]
if {$u != "NULL"} {
STX $u
}
}
proc put {button} {
global constants
set x [GTX]
lappend constants "$x : :saved result"
}
proc sqr {button} {
set x [GTX]
STX $x*$x
}
proc sqrt {button} {
set x [GTX]
if {$x < 0} {
messagebox "error: x<0"
} else {
STX sqrt($x)
}
}
proc inv {button} {
set x [GTX]
if {$x == 0} {
messagebox "error: x=0"
} else {
STX 1.0/$x
}
}
proc tenx {button} {
set x [GTX]
STX pow(10.0,$x)
}
proc exp {button} {
set x [GTX]
STX exp($x)
}
proc log {button} {
set x [GTX]
if {$x <= 0} {
messagebox "error: x<=0"
} else {
STX log10($x)
}
}
proc ln {button} {
set x [GTX]
if {$x <= 0} {
messagebox "error: x<=0"
} else {
STX log($x)
}
}
proc ytox {button} {
global mode
set x [GTX]
PSH $x
PSH **
set mode >
dispx
}
proc fact {button} {
set x [GTX]
if {$x < 0 || $x > 170} {
messagebox "error: x<0 or x>170"
} else {
set u 1.0
for {set i 1} {$i <= $x} {incr i} {
set u [expr $u*$i]
}
STX $u
}
}
proc trig {button} {
global drg hyp arc
set x [GTX]
if {!$hyp} {
set f [lindex "[expr acos(-1)/180.0] 1.0 [expr acos(-1)/200.0]" $drg]
if {!$arc} {
STX ${button}($f*$x)
} else {
STX a${button}($x)/$f
}
} else {
if {!$arc} {
STX ${button}h($x)
} else {
STX [a${button}h $x]
}
}
set arc 0
set hyp 0
}
proc pmin {button} {
global mode
switch -- $mode {
E {
global x
set x [string replace $x end-2 end-2 $button]
dispx
}
default {
PSH [GTX]
POW
STX [PEX {[(]}]
PSH [GTX]
PSH $button
}
}
}
proc mdiv {button} {
PSH [GTX]
POW
STX [PEX {[-+(]}]
PSH [GTX]
PSH $button
}
proc lpar {button} {
global mode
if {$mode == ">"} {
PSH $button
}
}
proc rpar {button} {
PSH [GTX]
POW
STX [PEX {[(]}]
POP
}
proc eq {button} {
PSH [GTX]
POW
STX [PEX]
}
proc HELP {button} {
global buttons bindings
set text "Button:\tKey-binding:\n"
append text "----------------------------\n"
foreach button $buttons binding $bindings {
append text "$button\t$binding\n"
}
append text "=\t<Return>\n"
append text "=\t<space>\n"
messagebox $text
}
proc DRG {button} {
global drg
set drg [expr ($drg+1)%3]
dispa
}
proc ARC {button} {
global arc
set arc [expr !$arc]
dispa
}
proc HYP {button} {
global hyp
set hyp [expr !$hyp]
dispa
}
proc M+ {button} {
set x [GTX]
set m [GTM]
STM $m+$x
}
proc M- {button} {
set x [GTX]
set m [GTM]
STM $m-$x
}
proc STO {button} {
set x [GTX]
STM $x
}
proc RCL {button} {
set m [GTM]
STX $m
}
proc XCH {button} {
set x [GTX]
set m [GTM]
STX $m
STM $x
}
proc CLR {button} {
CLM
}
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# binding and menu commands
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
proc BS {} {
global mode x
if {[string length $x] == 1} {
CLX
return
}
switch -- $mode {
I {
set c [string index $x end-1]
if {$c == "+" || $c == "-"} {
CLX
} else {
set x [string range $x 0 end-1]
}
}
F {
set c [string index $x end-1]
if {$c == "."} {
set x [string range $x 0 end-2]
set mode I
} else {
set x [string range $x 0 end-1]
}
}
X - E {
set x [string range $x 0 end-4]
if {[string first "." $x] > 0} {
set mode F
} else {
set mode I
}
}
}
dispx
}
proc RAND {} {
STX [expr rand()]
}
proc SEED {} {
global x
set s [expr int($x)]
expr srand($s)
}
proc STK? {} {
global STK PTR x
set text "LOC:\tVALUE:\nX:\t$x\n"
append text "----------------------------\n"
for {set i $PTR} {$i>0} {incr i -1} {
append text "$i:\t$STK($i)\n"
}
messagebox $text
}
proc PREC {} {
global x
if {$x < 1 || $x >= 18} {
messagebox "error: x<1 or x>=18"
} else {
set p [expr int($x)]
set tcl_precision $p
messagebox "PRECISION = $p"
}
}
proc LOG {} {
global _log _logfid
set _log [expr !$_log]
set logfile calc.tk.log
if {$_log} {
if {[catch {set _logfid [open $logfile w]}]} {
messagebox "error: cannot open log file \"$logfile\""
set _log 0
return
} else {
messagebox "log file \"$logfile\" opened"
}
} else {
catch {close $_logfid}
messagebox "log file \"$logfile\" closed"
}
}
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# basic calculator functions
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#============================================================================
# PSH: push token onto stack
#============================================================================
proc PSH {token} {
global STK PTR
incr PTR
set STK($PTR) $token
}
#============================================================================
# POP: pop token from stack
#============================================================================
proc POP {} {
global STK PTR
set token $STK($PTR)
if {$PTR>0} {
incr PTR -1
}
return $token
}
#============================================================================
# PEX: pop expression from stack (stop before $stop)
#============================================================================
proc PEX {{stop ""}} {
global PTR
set e ""
while {$PTR > 0} {
set t [POP]
if {[string match $stop $t]} {
PSH $t
break
}
if {$t != "NOP"} {
set e $t$e
}
}
return $e
}
#============================================================================
# STX: set accumulator
#============================================================================
proc STX {expression} {
global x mode
global _log _logfid
set x [expr $expression]
set mode >
dispx
if {$_log} {
puts $_logfid "$expression: $x"
flush $_logfid
}
}
#============================================================================
# STM: set memory
#============================================================================
proc STM {expression} {
global m mem mode
set m [expr $expression]
set mem 1
dispm
set mode >
dispx
}
#============================================================================
# CLX: clear accumulator
#============================================================================
proc CLX {} {
global x mode
set x 0
set mode >
dispx
}
#============================================================================
# CLM: clear memory
#============================================================================
proc CLM {} {
global m mem
set m 0
set mem 0
dispm
}
#============================================================================
# GTX: return x
# * put decimal point on end of x if still in integer mode
# before further processing
#============================================================================
proc GTX {} {
global x mode
if {$mode == "I"} {
set x $x.
set mode F
dispx
}
return $x
}
#============================================================================
# GTM: return m
#============================================================================
proc GTM {} {
global m mem
if {!$mem} {
set m 0
set mem 1
dispm
}
return $m
}
#============================================================================
# POW: process powers in stack
# to change x**y into pow(x,y)
#============================================================================
proc POW {} {
global STK PTR
for {set i $PTR} {$i>2} {incr i -1} {
set j [expr $i-1]
set k [expr $i-2]
set e $STK($i)
set p $STK($j)
set b $STK($k)
if {$p == "**"} {
set STK($i) pow($b,$e)
set STK($j) NOP
set STK($k) NOP
}
}
}
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# extra math functions
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
proc asinh {x} {
return [expr log($x+sqrt($x*$x+1.0))]
}
proc acosh {x} {
return [expr log($x+sqrt($x*$x-1.0))]
}
proc atanh {x} {
if {$x == 1.0} {
return 100.0
}
return [expr log(sqrt((1.0+$x)/(1.0-$x)))]
}
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# windows
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#============================================================================
# dispx: re-display x, mode
#============================================================================
proc dispx {} {
global x mode
.display.e configure -state normal
.display.e delete 0 end
.display.e insert end $x
.display.e configure -state disabled
.states.s configure -text $mode
}
#============================================================================
# dispm: re-display mem
#============================================================================
proc dispm {} {
global mem
.states.m configure -text [lindex "{} MEM" $mem]
}
#============================================================================
# dispa: re-display drg, hyp, arc
#============================================================================
proc dispa {} {
global drg hyp arc
.states.d configure -text [lindex "DEG RAD GRD" $drg]
.states.a configure -text [lindex "{} ARC" $arc]
.states.h configure -text [lindex "{} HYP" $hyp]
}
#============================================================================
# messagebox: display error or info
#============================================================================
proc messagebox {message} {
if {[regexp error $message]} {
set icon error
} else {
set icon info
}
tk_messageBox -message $message -parent . -icon $icon
}
#============================================================================
# constant: constant toplevel
#============================================================================
proc constant {} {
global constants SELECTION
set top .constants
catch {destroy $top}
toplevel $top
label $top.title -text "select constant with <Double-Button-1>"
scrollbar $top.scrolly -command "$top.list yview" -orient vertical
scrollbar $top.scrollx -command "$top.list xview" -orient horizontal
button $top.cancel -width 10 -text "cancel" \
-command "set SELECTION NULL;destroy $top"
listbox $top.list -relief raised -height 15 -width 60 \
-yscroll "$top.scrolly set" -xscroll "$top.scrollx set"
pack $top.title -side top -fill x
pack $top.cancel -side top
pack $top.scrollx -side top -fill x
pack $top.scrolly -side right -fill y
pack $top.list -side left -expand yes -fill both
bind $top.list <Double-Button-1> \
"set SELECTION \[selection get\];after 200;destroy $top"
foreach constant $constants {
$top.list insert end $constant
}
wm title $top "constants"
tkwait window $top
return [lindex [string trim $SELECTION "{}"] 0]
}
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# calculator display
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
frame .display
frame .states
frame .buttons
pack .display -side top -fill x -expand yes
pack .states -side top -fill x -expand yes
pack .buttons -side top -fill both -expand yes
entry .display.e -bd 2 -relief sunken -state disabled \
-font -adobe-courier-bold-r-normal-*-14-* \
-background GhostWhite -foreground black
pack .display.e -side top -padx 1 -pady 1 -expand yes -fill both
foreach state {a h d m s} {
label .states.$state -width 4 \
-background "steel blue" -foreground white
pack .states.$state -side left -fill x -expand yes
}
set brows [split $buttons "\n"]
set nrows [expr [llength $brows]-2]
set ncols [llength [lindex $brows 1]]
set i -1
foreach button $buttons buttoncommand $buttoncommands binding $bindings {
incr i
set x [expr ($i%$ncols)*1.0/$ncols]
set y [expr ($i/$ncols)*1.0/$nrows]
set w .buttons.$i
if {$button == "MENU"} {
menubutton $w -text $button -menu $w.m \
-relief raised -pady 2 -padx 2 -highlightthickness 0 -bd 2
menu $w.m -background red -foreground white
foreach {label command} $menuentries {
$w.m add command -label $label -command $command
}
} else {
button $w -command [list $buttoncommand $button] \
-pady 1 -padx 1 -highlightthickness 0 -bd 2
if {[info exists Bitmap($button)]} {
$w configure -image $Bitmap($button) -width 36 -height 16
} else {
$w configure -text $button -width 5 -height 1
}
}
$w configure -font -adobe-helvetica-medium-r-normal-*-12-* \
-background "dark khaki" -foreground black \
-relief raised -highlightthickness 0 -bd 2
place $w -relx $x -rely $y
bind all <$binding> [list $buttoncommand $button]
}
update idletasks
set W [winfo reqwidth .buttons.0]
set H [winfo reqheight .buttons.0]
.buttons configure -width [expr $ncols*$W] -height [expr $nrows*$H]
foreach window {all .display.e} {
bind $window <Return> "eq ="
bind $window <space> "eq ="
bind $window <BackSpace> "BS"
bind $window <Delete> "BS"
bind $window <Double-Button-2> "STX \[selection get\]; eq ="
}
set _log 0
AC AC
tkwait window .
exit 0See also:
- formula calculators
- t-Calc
- A small calculator
- A little calculator
- A fancier little calculator
- stack-based calculator
- A bc-like calculator
- HP Calculator Simulations
- Programmable RPN Calculator
- tclsh as a powerful calculator


