Timer.pl - Perl/Tk Lab Timer

This clock has a seconds hand and a minutes hand. It provides a time countdown, so the hands run counter-clockwise. It is a type of timer that is commonly used in darkrooms.

Download the code

The TomaCorp (We're not a corporation) Perl Index


#!/usr/bin/perl -w

=head1 NAME

timer - A Perl Tk Timer
Reminiscent of an old Gralab darkroom timer.

=head1 USAGE

  ./timer.pl duration [-n bells -s size -h height -w width -u update_rate -t title]
  ./timer.pl 600 -t"10 minute break" -n20 -fred -bgrey

=head1 AUTHOR

Copyright 2001 Tom Anderson     http://tomacorp.com
Perlmonks: toma

Thanks to predecessor codebase from Internet-time clock, which is
Copyright 1999 Walter C. Mankowski

=head1 COPYING

You may copy and distribute this program under the
same terms as Perl iteself.  

=head1 SEE ALSO

Tk, Getopt::Std

=cut

use vars qw( $VERSION );
$VERSION = '1.0';

use strict;
use Tk;
use Getopt::Std;

my $duration= shift;

my %opts;
getopt('b:f:h:n:s:t:u:w:', \%opts);

my $pi = 4.0 * atan2(1,1);

# Option handling

my $maxbell;
if (exists $opts{n}) 
{ $maxbell= $opts{n}} else {$maxbell=3}

my ($canvas_height, $canvas_width);

if (exists $opts{s} and $opts{s} >= 40 ) 
{ 
  $canvas_height= $opts{s};
  $canvas_width= $opts{s};
} 
else 
{
  $canvas_height= 210;
  $canvas_width= 210;
}

if (exists $opts{h} and $opts{h} >= 40 ) 
{ $canvas_height= $opts{h}} else {$canvas_height=210}

if (exists $opts{w} and $opts{w} >= 40 ) 
{ $canvas_width= $opts{w}} else {$canvas_width=210}

my $update_rate;
if (exists $opts{u}) 
{ $update_rate= $opts{u}} else {$update_rate=200}

my $title;
if (exists $opts{t}) 
{ $title= $opts{t}} else {$title='PerlLab'}

my $foreground;
if (exists $opts{f}) 
{ $foreground= $opts{f}} else {$foreground='green'}

my $background;
if (exists $opts{b}) 
{ $background= $opts{b}} else {$background='black'}

my $canvas_margin = 5;

my $numbell=0;

my $tstop;
if ($duration =~ /([0-9]+):([0-9]+)/) 
{ 
  $tstop= 60*$1+$2 
}
elsif ($duration =~ /([0-9]+)/) 
{ 
  $tstop= $1 
}
elsif ($duration eq "")
{
  usage();
  exit;
}
else 
{ 
  print "Error: Time `$duration' not in format seconds or minutes:seconds\n\n" ; 
  usage(); 
  exit;
}

my $timestart= time + $tstop;

# Computed Tk parameters
my $clock_center_y;
my $clock_center_x;
my $face_y_radius;
my $face_x_radius;

my $canvas_max;
my $hand_len;
my $sec_width;
my $min_width;
my $center_circle_r;
my $tick_width;

my $mw = MainWindow->new;
my ($canvas, $hand1, $hand2);
$mw->title($title);
$mw->client("labtimer");
drawclock();

&MainLoop;

sub drawclock
{
  # Draws the timer
  $clock_center_y= $canvas_height/2;
  $clock_center_x= $canvas_width/2;
  $face_y_radius= $canvas_height/2 - $canvas_margin;
  $face_x_radius = $canvas_width/2 - $canvas_margin;
  
  $canvas_max= ($canvas_height < $canvas_width) ? $canvas_height:$canvas_width;
  $hand_len= $canvas_max/2 - $canvas_margin;
  $sec_width = $hand_len/50+1;
  $min_width = $hand_len/30+1;
  $center_circle_r= int($min_width/sqrt(2))+1;
  $tick_width=$canvas_max/200+1;

  # $canvas is where the analog clock goes
  $canvas = $mw->Canvas(-height   => $canvas_height, 
                        -background => $background,
                        -width    => $canvas_width)
               ->pack  (-side     => 'top',
                        -expand   => 1);

  # the outer circle
  $canvas->createOval($canvas_margin, $canvas_margin, 
                      $canvas_width-$canvas_margin, 
                      $canvas_height-$canvas_margin, 
                      -width   => 5, 
                      -fill    => $background, 
                      -outline => $foreground ); 

  # the inner circle where the hands meet
  $canvas->createOval($clock_center_x-$center_circle_r,$clock_center_y-$center_circle_r, 
                      $clock_center_x+$center_circle_r,$clock_center_y+$center_circle_r, 
                      -fill => $foreground, 
                      -outline => $foreground );  

  # ticks around the outside hands
  &DrawTicks;
  &DrawHands ($canvas, $timestart-time, 1);

  # periodically move the hands
  $canvas->repeat( $update_rate, \&RedrawHands, $canvas);
}


sub DrawHands 
{
  my ($canvas, $t) = @_;

  # Second hand
  $hand1 = &DrawHand ($canvas, 
                      $t / 60, 
                      $hand_len * 0.90, 
                      $sec_width);

  # Minute hand
  $hand2 = &DrawHand ($canvas, 
                      $t / 3600, 
                      $hand_len * 0.75, 
                      $min_width);
}

sub DrawHand 
{
  my ($canvas, $frac, $length, $line_width) = @_;
  my $height;
  my $width;
  my $theta;
  my $hand;

  $theta  = ($frac * 2 * $pi) - ($pi / 2);
  $height = $length * sin($theta);
  $width  = $length * cos($theta);

  $hand = $canvas->createLine($clock_center_x, 
                              $clock_center_y, 
                              $clock_center_x+$width, 
                              $clock_center_y+$height,
                              -arrow => "last",
                              -width => $line_width,
                              -fill  => $foreground);
}

sub RedrawHands 
{
  my $canvas = shift;
  if ($timestart - time < 0)
  {
    exit if $numbell >= $maxbell;
    $canvas->bell();
    $numbell++;
  }
  else
  {
    $canvas->delete ($hand1, $hand2);
    &DrawHands ($canvas, $timestart-time, 1);
  }
}

sub DrawTicks 
{
  my $i;
  my ($y1, $x1, $y2, $x2);
  my $theta;
  my $scale;

  foreach $i (1..60) 
  {
    if ($i % 15 == 0)     # Major ticks
    {
      $scale = 0.80;
    } 
    elsif ($i % 5 == 0)   # Medium ticks
    {
      $scale = 0.90;
    } 
    else                  # Minor ticks
    {
      $scale = 0.95;
    }
    $theta = ($i/60 * 2 * $pi) - ($pi / 2);

    $x1 = $face_x_radius * $scale * cos($theta) + $clock_center_x;
    $y1 = $face_y_radius * $scale * sin($theta) + $clock_center_y;
    $x2 = $face_x_radius *          cos($theta) + $clock_center_x;
    $y2 = $face_y_radius *          sin($theta) + $clock_center_y;

    $canvas->createLine($x1, $y1, $x2, $y2, 
                        -fill => $foreground, 
                        -width => $tick_width);
  }
}

sub usage
{
  print 'timer.pl duration [-n bells -s size -h height -w width -u update_rate -t title]',"\n",
        "Example:\n",'  timer.pl 7:00 -t"flip bbq"', "\n";
}

10/13/2001

By toma