An analogue countdown clock-face

This is adapted from KBK's "An analog clock in Tk".

   proc countdown {seconds} {
       countdown_kernel $seconds

   proc countdown_kernel seconds {
       hands $seconds
       if !$seconds {
          bell; return
       after 1000 [list countdown_kernel [incr seconds -1]]

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

   proc end_coordinate difference {
       set hand_length [expr $::size * .9]
       return [expr $::size + $hand_length * $difference]

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

   proc hands seconds {
       catch {.c delete withtag hands}

       set twopi 6.283185
       set seconds_angle [expr $seconds * $twopi / 60.]
       draw_hand $seconds_angle "-width 1 -tags hands"
       set minutes_angle [expr $seconds_angle / 60.]
       draw_hand $minutes_angle \
                    "-width 3 -capstyle projecting -tags hands"

   proc init {} {
       catch {destroy .c}
       set ::size 30
       set full_diameter [expr 2 * $::size]
       pack [canvas .c -width $full_diameter -height $full_diameter]
       set border 2
       set diameter [expr 2 * $::size - $border]
       .c create oval $border $border \
                      $diameter $diameter \
                      -fill white -outline black

  # Example usage:
   countdown  10 ;# count down 10 seconds
  #countdown 180 ;# count down three minutes.

Should the clock hands advance clockwise, or counterclockwise?

I vote for there being an option to select the direction!

HJG added alarm sound at end of countdown. It looks like this is not reentrant...

I love scripts with interesting behavior. I like the look of this and was going to use it for a pomodoro countdown timer. I thought I'd add a variable "color" to the countdown function, passed to the init function, passed to the fill color of the oval. It works nice, but showed me an interesting behavior in the code.


countdown 1500 LightBlue;

countdown 300 LightGreen;

This will show the lightgreen 300 second count down before the 1500 second lightblue one.


countdown 1500 LightBlue;

after 1505; #when it doesnt work, ALWAYS introduce sleeps :)

countdown 300 LightGreen;

Huh, huh? Now I get a 1500 second countdown but it's drawn in lightgreen not lightblue. Really cool.

So I think it's the threading concepts used (or not used) BUT I can't figure out the last example. The second countdown should never have been started. Someone educate me.

Some features I'll be adding unless someone beats me to it:

  • make scale auto to size of window/frame - it may already
  • antialias using some other examples on wiki
  • better line/pointer graphics, perhaps nice rolex watch hands
  • pie chart it - instead of lines, use pie segments
  • colorize, customizable
  • analog, seconds remaining under clock, toggleable