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.