#!/usr/bin/wish # number # # Written by and copyright Neil Smith ( neil@wimp.freeuk.com ) # Version 1.0, 16 November 2004 # # A simple program for children to learn numbers and their names. # # This program displays a multi-digit display with controls to change # the number. Whenever the number changes, the name of the number # also changes. # # To use this program, ensure Tcl/Tk is installed, and make this file # executable. Then run this program. # # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA # 02111-1307 USA or the Free Software Foundation Europe e.V., # Villa Vogelsang, Antonienallee 1, 45279 Essen, Germany # # For more details, see http://www.gnu.org/ # For the text of the GPL, see http://www.gnu.org/licenses/gpl.txt wm title . "Numbers and Words" global n maxn # Create the frame to hold the GUI. # Put padding at the top, bottom, and on both sides to allow the window # to be resized without adjusting the geometery of the GUI frame .toppadding frame .bottompadding frame .numberframe frame .numberframe.leftpadding frame .numberframe.rightpadding # Create one frame for each digit, plus its increment/decrement buttons frame .numberframe.unitsframe frame .numberframe.tensframe frame .numberframe.hundredsframe frame .numberframe.thousandsframe frame .numberframe.tenthousandsframe button .numberframe.unitsframe.inc -text "^" -command incUnit button .numberframe.unitsframe.dec -text "v" -command decUnit -state disabled label .numberframe.unitsframe.digit -text "0" -font {-size 20} button .numberframe.tensframe.inc -text "^" -command incTen button .numberframe.tensframe.dec -text "v" -command decTen -state disabled label .numberframe.tensframe.digit -font {-size 20} button .numberframe.hundredsframe.inc -text "^" -command incHundred button .numberframe.hundredsframe.dec -text "v" -command decHundred -state disabled label .numberframe.hundredsframe.digit -font {-size 20} button .numberframe.thousandsframe.inc -text "^" -command incThousand button .numberframe.thousandsframe.dec -text "v" -command decThousand -state disabled label .numberframe.thousandsframe.digit -text "" -font {-size 20} button .numberframe.tenthousandsframe.inc -text "^" -command incTenThousand button .numberframe.tenthousandsframe.dec -text "v" -command decTenThousand -state disabled label .numberframe.tenthousandsframe.digit -font {-size 20} # Where the name of the number is held label .name -text "Zero" -width 57 # Create a frame to hold the 'clear' and 'close' buttons frame .buttonframe frame .buttonframe.leftpadding button .buttonframe.clearbutton -text "Clear" -command clearDisplay button .buttonframe.closebutton -text "Close" -command "destroy ." frame .buttonframe.rightpadding # Put the elements in the window pack .toppadding -side top -expand true -fill both pack .numberframe -side top pack .numberframe.leftpadding -side left -expand true -fill both pack .numberframe.tenthousandsframe .numberframe.thousandsframe .numberframe.hundredsframe .numberframe.tensframe .numberframe.unitsframe -side left pack .numberframe.rightpadding -side left -expand true -fill both pack .numberframe.unitsframe.inc .numberframe.unitsframe.digit .numberframe.unitsframe.dec -side top pack .numberframe.tensframe.inc .numberframe.tensframe.digit .numberframe.tensframe.dec -side top pack .numberframe.hundredsframe.inc .numberframe.hundredsframe.digit .numberframe.hundredsframe.dec -side top pack .numberframe.thousandsframe.inc .numberframe.thousandsframe.digit .numberframe.thousandsframe.dec -side top pack .numberframe.tenthousandsframe.inc .numberframe.tenthousandsframe.digit .numberframe.tenthousandsframe.dec -side top pack .name -side top pack .buttonframe -side top pack .buttonframe.leftpadding -side left -expand true -fill both pack .buttonframe.clearbutton .buttonframe.closebutton -side left pack .buttonframe.rightpadding -side left -expand true -fill both pack .bottompadding -side top -expand true -fill both # Capture all keypresses and pass the pressed key to the catchKey procedure bind . {catchKey %K} # One procedure for each increment or decrement button # I'm sure there's a better way to parameterise this, so that there need # only be one procedure for all the increments and decrements, but I # don't know Tcl well enough to do it proc incUnit {} { if {[.numberframe.unitsframe.digit cget -text] == 9 } { if { [.numberframe.tensframe.digit cget -text] != 9 || [.numberframe.hundredsframe.digit cget -text] != 9 || [.numberframe.thousandsframe.digit cget -text] != 9 || [.numberframe.tenthousandsframe.digit cget -text] != 9} { .numberframe.unitsframe.digit configure -text "0" incTen } } else { set x [.numberframe.unitsframe.digit cget -text] set x [expr $x + 1] .numberframe.unitsframe.digit configure -text $x } setZerosAndButtons setName } proc decUnit {} { if {[.numberframe.unitsframe.digit cget -text] == 0} { if { ![string equal [.numberframe.tensframe.digit cget -text] "" ] } { .numberframe.unitsframe.digit configure -text "9" decTen } } else { set x [.numberframe.unitsframe.digit cget -text] set x [expr $x - 1] .numberframe.unitsframe.digit configure -text $x } setZerosAndButtons setName } proc incTen {} { if {[.numberframe.tensframe.digit cget -text] == 9 } { if {[.numberframe.hundredsframe.digit cget -text] != 9 || [.numberframe.thousandsframe.digit cget -text] != 9 || [.numberframe.tenthousandsframe.digit cget -text] != 9} { .numberframe.tensframe.digit configure -text "0" incHundred } } else { set x [.numberframe.tensframe.digit cget -text] set x [expr $x + 1] .numberframe.tensframe.digit configure -text $x } setZerosAndButtons setName } proc decTen {} { if {![string equal [.numberframe.tensframe.digit cget -text] ""]} { if {[.numberframe.tensframe.digit cget -text] == 0 && ![string equal [.numberframe.hundredsframe.digit cget -text] ""]} { .numberframe.tensframe.digit configure -text "9" decHundred } else { set x [.numberframe.tensframe.digit cget -text] set x [expr $x - 1] .numberframe.tensframe.digit configure -text $x setZerosAndButtons setName } } } proc incHundred {} { if {[.numberframe.hundredsframe.digit cget -text] == 9 } { if {[.numberframe.thousandsframe.digit cget -text] != 9 || [.numberframe.tenthousandsframe.digit cget -text] != 9} { .numberframe.hundredsframe.digit configure -text "0" incThousand } } else { set x [.numberframe.hundredsframe.digit cget -text] set x [expr $x + 1] .numberframe.hundredsframe.digit configure -text $x } setZerosAndButtons setName } proc decHundred {} { if {![string equal [.numberframe.hundredsframe.digit cget -text] ""]} { if {[.numberframe.hundredsframe.digit cget -text] == 0 && ![string equal [.numberframe.thousandsframe.digit cget -text] ""]} { .numberframe.hundredsframe.digit configure -text "9" decThousand } else { set x [.numberframe.hundredsframe.digit cget -text] set x [expr $x - 1] .numberframe.hundredsframe.digit configure -text $x setZerosAndButtons setName } } } proc incThousand {} { if {[.numberframe.thousandsframe.digit cget -text] == 9 } { if { [.numberframe.tenthousandsframe.digit cget -text] != 9} { .numberframe.thousandsframe.digit configure -text "0" incTenThousand } } else { set x [.numberframe.thousandsframe.digit cget -text] set x [expr $x + 1] .numberframe.thousandsframe.digit configure -text $x } setZerosAndButtons setName } proc decThousand {} { if {![string equal [.numberframe.thousandsframe.digit cget -text] ""]} { if {[.numberframe.thousandsframe.digit cget -text] == 0 && ![string equal [.numberframe.tenthousandsframe.digit cget -text] ""] } { .numberframe.thousandsframe.digit configure -text "9" decTenThousand } else { set x [.numberframe.thousandsframe.digit cget -text] set x [expr $x - 1] .numberframe.thousandsframe.digit configure -text $x setZerosAndButtons setName } } } proc incTenThousand {} { if {[.numberframe.tenthousandsframe.digit cget -text] != 9 } { set x [.numberframe.tenthousandsframe.digit cget -text] set x [expr $x + 1] .numberframe.tenthousandsframe.digit configure -text $x setZerosAndButtons setName } } proc decTenThousand {} { if {![string equal [.numberframe.tenthousandsframe.digit cget -text] ""] } { set x [.numberframe.tenthousandsframe.digit cget -text] set x [expr $x - 1] .numberframe.tenthousandsframe.digit configure -text $x setZerosAndButtons setName } } proc decTenThousandOld {} { if {![string equal [.numberframe.tenthousandsframe.digit cget -text] ""] } { set x [.numberframe.tenthousandsframe.digit cget -text] set x [expr $x - 1] setZerosAndButtons setName if { $x != 0 } { .numberframe.tenthousandsframe.digit configure -text $x } else { .numberframe.tenthousandsframe.digit configure -text "" if { [.numberframe.thousandsframe.digit cget -text] == 0} { .numberframe.thousandsframe.digit configure -text "" } } } } # Put zeros in the right digits, and enable or disable all the increment and decrement buttons # If the number is currently '5' and the user increment the thousands digit, we need to put # additional zeros in the hundreds and tens digits so that the number reads '1005', not '1 5' # If the number is '1005' and the user decrements the thousands digit, zeros should be removed # so that the number reads ' 5', not '0005' or ' 005' # At the same time, if the count is 90,000 or more, the 'increment ten thousands' button can't do # anything so should be disabled. If the number is less than 10,000, the 'decrement ten thousands' # button should be disabled. proc setZerosAndButtons {} { if {[.numberframe.tenthousandsframe.digit cget -text] == 9} { .numberframe.tenthousandsframe.inc configure -state disabled } else { .numberframe.tenthousandsframe.inc configure -state normal } if {[.numberframe.tenthousandsframe.digit cget -text] == 0} { .numberframe.tenthousandsframe.digit configure -text "" } if {[string equal [.numberframe.tenthousandsframe.digit cget -text] "" ]} { .numberframe.tenthousandsframe.dec configure -state disabled } else { .numberframe.tenthousandsframe.dec configure -state normal } if {[.numberframe.tenthousandsframe.digit cget -text] == 9 && [.numberframe.thousandsframe.digit cget -text] == 9 } { .numberframe.thousandsframe.inc configure -state disabled } else { .numberframe.thousandsframe.inc configure -state normal } if {[.numberframe.thousandsframe.digit cget -text] == 0 && [string equal [.numberframe.tenthousandsframe.digit cget -text] "" ]} { .numberframe.thousandsframe.digit configure -text "" } if {![string equal [.numberframe.tenthousandsframe.digit cget -text] ""] && [string equal [.numberframe.thousandsframe.digit cget -text] "" ]} { .numberframe.thousandsframe.digit configure -text "0" } if {[string equal [.numberframe.thousandsframe.digit cget -text] "" ]} { .numberframe.thousandsframe.dec configure -state disabled } else { .numberframe.thousandsframe.dec configure -state normal } if {[.numberframe.tenthousandsframe.digit cget -text] == 9 && [.numberframe.thousandsframe.digit cget -text] == 9 && [.numberframe.hundredsframe.digit cget -text] == 9} { .numberframe.hundredsframe.inc configure -state disabled } else { .numberframe.hundredsframe.inc configure -state normal } if {[.numberframe.hundredsframe.digit cget -text] == 0 && [string equal [.numberframe.tenthousandsframe.digit cget -text] "" ] && [string equal [.numberframe.thousandsframe.digit cget -text] "" ]} { .numberframe.hundredsframe.digit configure -text "" } if {![string equal [.numberframe.thousandsframe.digit cget -text] ""] && [string equal [.numberframe.hundredsframe.digit cget -text] "" ]} { .numberframe.hundredsframe.digit configure -text "0" } if {[string equal [.numberframe.hundredsframe.digit cget -text] "" ]} { .numberframe.hundredsframe.dec configure -state disabled } else { .numberframe.hundredsframe.dec configure -state normal } if {[.numberframe.tenthousandsframe.digit cget -text] == 9 && [.numberframe.thousandsframe.digit cget -text] == 9 && [.numberframe.hundredsframe.digit cget -text] == 9 && [.numberframe.tensframe.digit cget -text] == 9} { .numberframe.tensframe.inc configure -state disabled } else { .numberframe.tensframe.inc configure -state normal } if {[.numberframe.tensframe.digit cget -text] == 0 && [string equal [.numberframe.tenthousandsframe.digit cget -text] "" ] && [string equal [.numberframe.thousandsframe.digit cget -text] "" ] && [string equal [.numberframe.hundredsframe.digit cget -text] "" ]} { .numberframe.tensframe.digit configure -text "" } if {![string equal [.numberframe.hundredsframe.digit cget -text] ""] && [string equal [.numberframe.tensframe.digit cget -text] "" ]} { .numberframe.tensframe.digit configure -text "0" } if {[string equal [.numberframe.tensframe.digit cget -text] "" ]} { .numberframe.tensframe.dec configure -state disabled } else { .numberframe.tensframe.dec configure -state normal } if {[.numberframe.tenthousandsframe.digit cget -text] == 9 && [.numberframe.thousandsframe.digit cget -text] == 9 && [.numberframe.hundredsframe.digit cget -text] == 9 && [.numberframe.tensframe.digit cget -text] == 9 && [.numberframe.unitsframe.digit cget -text] == 9} { .numberframe.unitsframe.inc configure -state disabled } else { .numberframe.unitsframe.inc configure -state normal } if {[string equal [.numberframe.unitsframe.digit cget -text] ""]} { .numberframe.unitsframe.digit configure -text "0" } if {[.numberframe.unitsframe.digit cget -text] == 0 && [string equal [.numberframe.tensframe.digit cget -text] "" ]} { .numberframe.unitsframe.dec configure -state disabled } else { .numberframe.unitsframe.dec configure -state normal } } # Set everything to zero proc clearDisplay {} { .numberframe.tenthousandsframe.digit configure -text "" .numberframe.thousandsframe.digit configure -text "" .numberframe.hundredsframe.digit configure -text "" .numberframe.tensframe.digit configure -text "" .numberframe.unitsframe.digit configure -text "0" setZerosAndButtons setName } # Find the name of a one-digit number proc oneDigitName {digit} { if {[string equal $digit ""]} { set digit "0" } switch $digit { "0" { return "zero"} "1" { return "one"} "2" { return "two"} "3" { return "three"} "4" { return "four"} "5" { return "five"} "6" { return "six"} "7" { return "seven"} "8" { return "eight"} "9" { return "nine"} } } # Find the name of a two-digit number proc twoDigitName {tens units} { if {[string equal $tens ""]} { set tens "0" } if {[string equal $units ""]} { set units "0" } if {$tens == 0} { return [oneDigitName $units] } elseif {$tens == 1} { switch $units { "0" { return "ten"} "1" { return "eleven"} "2" { return "twelve"} "3" { return "thirteen"} "4" { return "fourteen"} "5" { return "fifteen"} "6" { return "sixteen"} "7" { return "seventeen"} "8" { return "eighteen"} "9" { return "nineteen"} } } else { switch $tens { "2" { set tensname "twenty"} "3" { set tensname "thirty"} "4" { set tensname "forty"} "5" { set tensname "fifty"} "6" { set tensname "sixty"} "7" { set tensname "seventy"} "8" { set tensname "eighty"} "9" { set tensname "ninety"} } if {$units != 0} { append tensname "-" [oneDigitName $units] } return $tensname } } # Build the name of the whole number from the digits proc buildName {} { set tenthousand [.numberframe.tenthousandsframe.digit cget -text] set thousand [.numberframe.thousandsframe.digit cget -text] set hundred [.numberframe.hundredsframe.digit cget -text] set ten [.numberframe.tensframe.digit cget -text] set unit [.numberframe.unitsframe.digit cget -text] if {[string equal $tenthousand ""]} { set tenthousand "0" } if {[string equal $thousand ""]} { set thousand "0" } if {[string equal $hundred ""]} { set hundred "0" } if {[string equal $ten ""]} { set ten "0" } if {[string equal $unit ""]} { set unit "0" } set name "" if {$tenthousand != 0 || $thousand != 0} { append name [twoDigitName $tenthousand $thousand] " thousand" } if {![string equal $name ""] && $hundred != 0} { append name ", " } if {$hundred != 0} { append name [oneDigitName $hundred] " hundred" } if {![string equal $name ""] && ($ten != 0 || $unit != 0)} { append name " and " } if {$ten != 0 || $unit != 0} { append name [twoDigitName $ten $unit] } if {[string equal $name ""]} { set name [oneDigitName $unit] } append newname [string toupper [string index $name 0]] [string range $name 1 200] return $newname } # Build the name of the number and set the GUI text to it. proc setName {} { .name configure -text [buildName] } # Process key presses proc catchKey {key} { # If the user presses a key on the numeric keypad, # convert it to its number equivalent if {[string equal $key "KP_Insert"]} {set key "0"} if {[string equal $key "KP_End"]} {set key "1"} if {[string equal $key "KP_Down"]} {set key "2"} if {[string equal $key "KP_Next"]} {set key "3"} if {[string equal $key "KP_Left"]} {set key "4"} if {[string equal $key "KP_Begin"]} {set key "5"} if {[string equal $key "KP_Right"]} {set key "6"} if {[string equal $key "KP_Home"]} {set key "7"} if {[string equal $key "KP_Up"]} {set key "8"} if {[string equal $key "KP_Prior"]} {set key "9"} # Now we can deal with the keys # clears the display # Delete and Backspace do a shift right # Numeric keys add a new digit: # if there's room, shift the digits left, # and then insert the pressed key in the units column # Up and down arrows increment and decrement switch $key { "Escape" { clearDisplay } "Delete" - "BackSpace" { .numberframe.unitsframe.digit configure -text [.numberframe.tensframe.digit cget -text] .numberframe.tensframe.digit configure -text [.numberframe.hundredsframe.digit cget -text] .numberframe.hundredsframe.digit configure -text [.numberframe.thousandsframe.digit cget -text] .numberframe.thousandsframe.digit configure -text [.numberframe.tenthousandsframe.digit cget -text] .numberframe.tenthousandsframe.digit configure -text "" } "0" - "1" - "2" - "3" - "4" - "5" - "6" - "7" - "8" - "9" { if {[string equal [.numberframe.tenthousandsframe.digit cget -text] ""]} { .numberframe.tenthousandsframe.digit configure -text [.numberframe.thousandsframe.digit cget -text] .numberframe.thousandsframe.digit configure -text [.numberframe.hundredsframe.digit cget -text] .numberframe.hundredsframe.digit configure -text [.numberframe.tensframe.digit cget -text] .numberframe.tensframe.digit configure -text [.numberframe.unitsframe.digit cget -text] .numberframe.unitsframe.digit configure -text $key } } "Up" { incUnit } "Down" {decUnit } } # After processing the key, reset the display setZerosAndButtons setName }