
package require Tk
wm title . Calculator
grid [entry .e -textvar e -just right] -columnspan 5
bind .e <Return> =
set n 0
foreach row {
{7 8 9 + -}
{4 5 6 * /}
{1 2 3 ( )}
{C 0 . = }
} {
foreach key $row {
switch -- $key {
= {set cmd =}
C {set cmd {set clear 1; set e ""}}
default {set cmd "hit $key"}
}
lappend keys [button .[incr n] -text $key -command $cmd]
}
eval grid $keys -sticky we ;#-padx 1 -pady 1
set keys [list]
}
grid .$n -columnspan 2 ;# make last key (=) double wide
proc = {} {
regsub { =.+} $::e "" ::e ;# maybe clear previous result
if [catch {lappend ::e = [set ::res [expr 1.0*$::e]]}] {
.e config -fg red
}
.e xview end
set ::clear 1
}
proc hit {key} {
if $::clear {
set ::e ""
if ![regexp {[0-9().]} $key] {set ::e $::res}
.e config -fg black
.e icursor end
set ::clear 0
}
.e insert end $key
}
set clear 0
focus .e ;# allow keyboard input
wm resizable . 0 0[Rob Hegt] - By adding the below 'fix' proc and changing the line doing the expr to the one given below, it is no longer necessary to add a . in case of a division. proc fix {str} {
if {[string first "." "$str"] < 0} {
if {[string first "/" "$str"] > -1} {
set str "$str."
}
}
return "$str"
}
…
if [catch {lappend ::e = [set ::res [expr [fix $::e]]]}] {RS: Yes, but this fix raises an error with well-formed expressions like 1/(2+3). It is simpler, and more bullet-proof, to just prefix 1.0* to the expression when giving it to expr.2-4-2004 MDD: How does one get around the internal int conversion in, for example, 1 / (2 * 4 / 5), which produces an answer of 1.0, rather than the correct answer of 0.625 ? One way to do it might be to go through the entire formula string and replace all ints with doubles, prior to passing it to expr, but that seems a little clunky. Anybody have a cleaner way to do it?RS: Thank you for finding this subtle bug! Isn't Wiki code review great?- I admit that prefixing 1.0* is not bulletproof enough: evaluation goes not always from left to right, parenthesized sub-expressions come first. The precision loss occurs only with division, so it is sufficient to force at least one operand of "/" to double. The following modification passes your test, and I can't presently think of situations where it would produce bad syntax:
set ::res [expr [string map {/ *1.0/} $::e]]At least it's short, compared to the alternative of tokenizing and parsing the expression, and applying a double() to one of "/"'s operands...MDD: That seems to do it. Thanks.Programmable scientific calculator in two lines: (why, it can do asin and log ;-)
pack [entry .e -textvar e -width 50]
bind .e <Return> {catch {expr [string map {/ *1./} $e]} res; set e $res} ;# RS & FRFR suggests changing the binding to: bind .e <Return> {catch {expr [string map {/ *1./} $e]} res; set e $res;.e selection range 0 end}to allow easy typing in of a new calculation without having to delete everything first. RS: In principle yes, but the standard selection on Windows (-fg white -bg darkblue} makes the result much less conspicuous... Also, the ::clear variable makes sure that the entry is cleared when you start a new calculation; but you many also edit the original, or re-use its result by typing an operator.And, as CL noted, this thingy is programmable: enter for example [proc fac x {expr {$x<2? 1: $x*[fac [incr x -1]]}}]into the entry, disregard warnings; now you can do[fac 10]and receive [fac 10] = 3628800.0 as result...
See also: Programmable RPN calculator - t-Calc
AM I used to use a UNIX program, called bc, that allows you to type in expressions like "1+2.3" and then you get the result, 3.3. But that is not available on PC ... so, I wrote me a little Tcl script a few years ago to compensate for that. I have revised it, as one of its features was not very satisfactory. Here it is: A bc-like calculator
Derek Peschel 2005-04-27 See A fancier little calculator. The code has big bugs, so I hope a separate page will make reviewing/debugging easier. I wanted to leave a nice finished present but I'm too new to Tcl so I need help. But there are still some potentially good ideas.
[Lectus] - 2011-04-30 09:26:47Here is another simple programmable calculator written using Itcl:
package req math
package req Itcl
namespace import itcl::*
proc func {name arguments body} {
proc tcl::mathfunc::$name $arguments [list expr $body]
}
class calculator {
# if we pass init arg we're using it as a standalone program.
constructor {{init '}} {
if {$init eq "init"} {
calculator::REPL
}
}
method eval {line} {
if {$line eq "exit"} {exit}
set funcname {}
set funcargs {}
set funcbody {}
if { [regexp {(.+)\((.+)\)=(.+)} $line -> funcname funcargs funcbody] } {
func $funcname $funcargs $funcbody
return $funcname
} else {
if {[catch {expr $line} res]} {
puts stderr "Error in expression!"
return 0
} else { return $res }
}
}
method read {} {
gets stdin line
calculator::eval $line
}
method REPL {} {
while 1 {
puts -nonewline "\n>> "
puts [calculator::read]
}
}
}
calculator calc initUsage:$ tclsh calc.tcl >> f(x y)= $x > $y ? $x : $y f >> f(2,3) 3 >> f(5,1) 5 >> g(x)=$x*2 g >> 1 + g(5) 11Explanation: I used an Itcl class to create a reusable calculator. The eval method evaluates expressions, and if the expression is a function it will define it in tcl::mathfunc namespace, so that it's available in expr. The REPL method defines the prompt that keeps reading and evaluating expressions. Type 'exit' to exit the calculator.

