Updated 2010-11-01 18:53:48 by tomk

After watching a Space Shuttle launch I decide I had to have a count down clock. This clock includes spinboxs to set the countdown time and buttons that start, pause, continue and reset the clock. See An analog clock in Tk which was the starting point for this code.

Tom Krehbiel
 set ::secs_togo 0

 proc countdown_start { } {
   countdown_reset
   countdown_continue
 }

 proc countdown_continue { } {
   .c itemconfigure face -fill white
   draw_hands
   if !$::secs_togo {
     .start configure -text "Start" -command countdown_start
     .c itemconfigure face -fill green
     bell
     return
   }
   .start configure -text "Pause" -command countdown_pause
   incr ::secs_togo -1
   after 1000 countdown_continue
 }
 proc countdown_pause { } {
   catch {after cancel countdown_continue}
   .c itemconfigure face -fill blue
   .start configure -text "Continue" -command countdown_continue
 }
 proc countdown_reset { } {
   catch {after cancel countdown_continue}
   set ::secs_togo [expr $::seconds+60*$::minutes]
   .c itemconfigure face -fill $::bg
   .start configure -text "Start" -command countdown_start
   draw_hands
 }

 proc draw_hand { hand angle decorations } {
   set end_xy [get_xy $hand $angle]
   eval .c create line $::size $::size $end_xy $decorations
 }

 proc end_coordinate {hand difference} {
   set length [expr {$hand == "seconds" ? .9 : .8}]
   set hand_length [expr $::size * $length]
   return [expr $::size + $hand_length * $difference]
 }

 proc get_xy { hand angle } {
   return [list [end_coordinate $hand [expr sin($angle)]] \
                      [end_coordinate $hand [expr -cos($angle)]]]
 }

 proc draw_hands {} {
   set seconds $::secs_togo
   catch {.c delete withtag hands}
   set twopi 6.283185
   set seconds_angle [expr $seconds * $twopi / 60.]
   draw_hand seconds $seconds_angle "-width 1 -tags hands"
   set minutes_angle [expr $seconds_angle / 60.]
   draw_hand minutes $minutes_angle \
       "-width 2 -capstyle projecting -arrow last -tags hands"
 }

 proc init {} {
   catch {destroy .c}
   set ::size 30
   set full_diameter [expr 2 * $::size]

   canvas  .c -width $full_diameter -height $full_diameter
   button  .start -text "Start" -command countdown_start
   button  .reset -text "Reset" -command countdown_reset
   label   .l -text "mm:ss"
   spinbox .m -textvariable ::minutes -width 2 -justify right \
       -from 0 -to 59 -validate key -vcmd {expr "%P<0||%P>59?0:1"}
   spinbox .s -textvariable ::seconds -width 2 -justify right \
       -from 0 -to 59 -validate key -vcmd {expr "%P<0||%P>59?0:1"}

   set ::bg [.c cget -background]

   grid .l .m .s -sticky ew
   grid .c - -
   grid .start - .reset -sticky ew

   set border 2
   set diameter [expr 2 * $::size - $border]
   .c create oval $border $border $diameter $diameter \
       -fill $::bg -outline black -tag face
 }

 # Example usage:
 init