#!/usr/bin/perl -w
#
# ktimer v0.03 --crb3 21aug02/01apr07
#
# simple kitchen countdown-timer app. be sure to change the
# WAVs to ones you have on your system. ditto the sound-player.
# as written, this is a one-shot timer; it doesn't have the
# state-logic to rerun anything.
#
# This program is copyright (C) 2002,2003 C. R. Bryan III
# (crb3@stormbringer.org), All Rights Reserved. It is released under
# the GNU General Public License v2. There is no warranty. Using
# this program to trigger high explosives is a Really Bad Idea,
# even if you need your five- and ten-minute warnings nonmaskable.
#
# v0.01: --crb3 21aug02
#       first version. it runs. the kids think it's annoying: that's
#       proof that it works.
# v0.02: --crb3 17may03
#       spruced it up for release.
# v0.03: --crb3 01Apr07
#       - added commandline options for scripted use
#       - debug: 'show' on $hrs was allowing only 00
#       - button label sorted out, is now what it will do.
#
use Tk;

#-------config area---------

#
# "pest" controls behavior at zero-hour. if pest==0, the noise is
# sounded _once_. if pest!=0, it keeps doing it, with "pestdelay"
# seconds wait between repeats.
#
$pest=1;                                # be a pest at timer-zero
$pestdelay=5;                           # seconds btwn pest repeats

$what="";                               # startup "what's being timed" string
$hrs="00";                              # preload hours value
$mns="10";                              # preload minutes value
$scs="05";                              # preload seconds value
$startgo=0;                             # start running immediately?
#
# the program slaps two halves of a commandline together in order
# to make a sound, so you've got a lot of flexibility here. if
# you don't happen to have a soundcard, you can use "beep" or "morbeep"
# to make rude noises on the PC speaker, with different command args
# for each sound. the listed WAVs are from kitchentimer.
#
$play="/usr/local/bin/wavplay -q";      # command-line to invoke soundmaker
$wav00="/usr/share/sounds/min00.wav";   # sound to play when time's up
$wav05="/usr/share/sounds/min05.wav";   # sound to play at 5-minute warning
$wav10="/usr/share/sounds/min10.wav";   # sound to play at 10-minute warning
$bgalt="red";                           # display turns this color at 0-hour
$bgwht="white";                         # display is this color when running
$bggry="lightgrey";                     # display is this color when editable
$butlblrun="run";                       # button legends. They don't need
$butlblstp="stop";                      # translating, do they? I mean, Perl
$butlbldun="done";                      # _is_ the Universal Language, right?

#---------------------------

$chatty=$debug=0;       # no real use here, but kept just in case
while(defined($ARGV[0]) and index($ARGV[0],'-')==0){
  $arg=shift(@ARGV);                    # get any the switches
  $key=substr($arg,1,1);                # get no-arg switches first
  substr($arg,0,2)="";
  if($key eq 'v'){                      # verbose?
    $chatty ^= 1;
    next;
  }elsif($key eq 'D'){                  # debug: enable any ad-hoc tracers
    $debug^=1;
    next;
  }elsif($key eq 'S'){                  # toggle: start immediately?
    $startgo^=1;
    next;
  }elsif($key eq '?'){                  # show helps and leave.
    print <<EOT;
 ktimer Perl/Tk kitchen-timer applet --crb3 21aug02/01apr07
     ktimer [options] [00:00:00]
   Options:
       -l 'the text'    'timing what' descriptive text legend
       -P '/p/app -q'   player tool plus switch-args for chimes
       -p n             switch/delay: pest-loop at timeout (Default: 5 secs)
       -x /p/f.n        ten-minute chime soundfile
       -y /p/f.n        five-minute chime soundfile
       -z /p/f.n        zero-minute chime soundfile
       -S               toggle: start running immediately
       -t 00:00:00      load timer with this (or tail-arg it)
       -?               show helps, then leave
EOT
#       -v               toggle: script gets chatty
#       -D               toggle: turn on ad-hoc debug tracers
    exit(0);
  }
  $arg =~ s/^\=//;                      # handles switch=arg
  $arg=shift(@ARGVif($arg eq "" and ($ARGV[0] !~ /^\-\w/) );
#                     # handles space-separated switch/arg
  if($key eq 'l'){                      # text to display in 'what' field
    $what=$arg;
  }elsif($key eq 'P'){                  # player command to use
    $play=$arg;
  }elsif($key eq 'p'){                  # be-a-pest switch/delay
    $pestdelay=0+$arg;                  # coerce to numeric
    $pest=($pestdelay ? 1 : 0);         # i want it boolean
  }elsif($key eq 'x'){                  # ten-minute warning WAVfile
    $wav10=$arg;
  }elsif($key eq 'y'){                  # five-minute warning WAVfile
    $wav05=$arg;
  }elsif($key eq 'z'){                  # zero-minute warning WAVfile
    $wav00=$arg;
  }elsif($key eq 't'){
    ($hrs,$mns,$scs)=split(':',$arg);
    $scs='00' unless defined $scs;
  }else{
    warn "$0: unrecognized option -$key $arg\n";
  }
}
if(defined($ARGV[0])){
  ($hrs,$mns,$scs)=split(':',$ARGV[0]);
  $scs='00' unless defined $scs;
}

$bgrun=$bgwht;
$bgstop=$bggry;
$toggle=0;                              # misnamed. this is the pest-counter.
$running=0;                             # state boolean

#
# put references up top, for scope of visibility
#
$ref_button_1=$ref_entry_2=$ref_entry_3=$ref_entry_4=0;

my($top) = MainWindow->new();
$top->title("ktimer $what");
my($main_frame) = $top->Frame()->pack(
        -fill=>'both',
        -expand=>1
);


#
# chg_butstate.
# change the button's labeling to reflect what clicking it will do.
# this also starts and stops the countdown timer.
#
sub chg_butstate {
  if($running != 0){    # RUN -> STOP
    $ref_button_1->configure(-text => $butlblrun);
    $ref_entry_2->configure(-state => 'normal');
    $ref_entry_3->configure(-state => 'normal');
    $ref_entry_4->configure(-state => 'normal');
    $ref_entry_2->configure(-background => $bgstop);
    $ref_entry_3->configure(-background => $bgstop);
    $ref_entry_4->configure(-background => $bgstop);
    $running =0;
  }else{                # STOP -> RUN
    $ref_button_1->configure(-text => $butlblstp);
    $ref_entry_2->configure(-state => 'disabled');
    $ref_entry_3->configure(-state => 'disabled');
    $ref_entry_4->configure(-state => 'disabled');
    $ref_entry_2->configure(-background => $bgrun);
    $ref_entry_3->configure(-background => $bgrun);
    $ref_entry_4->configure(-background => $bgrun);
    $v=0+$hrs$hrs=sprintf("%2.02d",$v);
    $v=0+$mns$mns=sprintf("%2.02d",$v);
    $v=0+$scs$scs=sprintf("%2.02d",$v);
    $running =1;
  }
}

#
# timer.
# callbacked once a second. the value in the display actually
# is the countdown value.
#
sub timer {
  if($pest and $scs eq "00" and $mns eq "00" and $hrs eq "00"){
    $toggle++; $toggle %= $pestdelay;
    return if $toggle;
    system("$play $wav00 &");
    return;
  }
  return unless $running;

  if($scs eq "01" and $mns eq "00" and $hrs eq "00"){   # time's up!
    $scs="00";
    &chg_butstate;              # turn off run/stop switches
    $ref_button_1->configure(-text => $butlbldun);
    $ref_entry_2->configure(-background => $bgalt);
    $ref_entry_3->configure(-background => $bgalt);
    $ref_entry_4->configure(-background => $bgalt);
    system("$play $wav00 &");
    $ref_button_1->flash;       # flash the button
  }else{
    $v = 0+$scs;        #coerce to numeric
    if($v != 0){
      $v--;
      $scs=sprintf("%0.02d",$v);

      if($scs eq "00" and $hrs eq "00"){
        if($mns eq "10"){               # do 10-minute chime
          system("$play $wav10 &");
        }elsif($mns eq "05"){           # do 5-minute chime
          system("$play $wav05 &");
        }
      }
      return;
    }

    $scs="59";
    $v = 0+$mns;        #coerce to numeric
    if($v != 0){
      $v--;
      $mns=sprintf("%0.02d",$v);
      return;
    }
    $mns="59";
    $v=0+$hrs;
    if($v != 0){
      $v--;
      $mns=sprintf("%0.02d",$v);
    }
  }
}

#
# ktimer_ui.
# interface generated by SpecTix (Perl enabled) version 1.2 from ktimer.ui
# For use with Tk400.202, using the gridbag geometry manager
#
sub ktimer_ui {
        my($root) = @_;

        # widget creation

        my($button_1) = $root->Button (
                -font => '-*-helvetica-Medium-R-Normal-*-*-120-*-*-*-*-*-*',
                -text => $butlblrun,
                -width => 4,
                -command => \&chg_butstate,
        );
        my($entry_1) = $root->Entry (
                -font => '-*-helvetica-Medium-R-Normal-*-*-120-*-*-*-*-*-*',
                -textvariable => \$what,
                -background => $bgwht,
        );
        my($entry_2) = $root->Entry (
                -font => '-*-helvetica-Medium-R-Normal-*-*-120-*-*-*-*-*-*',
                -textvariable => \$hrs,
                -width => '2',
        );
        my($label_1) = $root->Label (
                -font => '-*-helvetica-Medium-R-Normal-*-*-120-*-*-*-*-*-*',
                -padx => '0',
                -text => ':',
        );
        my($entry_3) = $root->Entry (
                -font => '-*-helvetica-Medium-R-Normal-*-*-120-*-*-*-*-*-*',
                -textvariable => \$mns,
                -width => '2',
        );
        my($label_2) = $root->Label (
                -font => '-*-helvetica-Medium-R-Normal-*-*-120-*-*-*-*-*-*',
                -padx => '0',
                -text => ':',
        );
        my($entry_4) = $root->Entry (
                -font => '-*-helvetica-Medium-R-Normal-*-*-120-*-*-*-*-*-*',
                -textvariable => \$scs,
                -width => '2',
        );

        # Geometry management

        $button_1->grid(
                -in => $root,
                -column => '1',
                -row => '1'
        );
        $entry_1->grid(
                -in => $root,
                -column => '2',
                -row => '1'
        );
        $entry_2->grid(
                -in => $root,
                -column => '3',
                -row => '1'
        );
        $label_1->grid(
                -in => $root,
                -column => '4',
                -row => '1'
        );
        $entry_3->grid(
                -in => $root,
                -column => '5',
                -row => '1'
        );
        $label_2->grid(
                -in => $root,
                -column => '6',
                -row => '1'
        );
        $entry_4->grid(
                -in => $root,
                -column => '7',
                -row => '1'
        );

        # Resize behavior management

        # container $root (rows)
        $root->gridRowconfigure(1-weight  => 0, -minsize  => 30);

        # container $root (columns)
        $root->gridColumnconfigure(1-weight => 0, -minsize => 30);
        $root->gridColumnconfigure(2-weight => 0, -minsize => 30);
        $root->gridColumnconfigure(3-weight => 0, -minsize => 2);
        $root->gridColumnconfigure(4-weight => 0, -minsize => 2);
        $root->gridColumnconfigure(5-weight => 0, -minsize => 30);
        $root->gridColumnconfigure(6-weight => 0, -minsize => 12);
        $root->gridColumnconfigure(7-weight => 0, -minsize => 30);

        # additional interface code

        $button_1->configure(-command => \&chg_butstate);
        $root->repeat(1000,\&timer);

        $root->bind('<Control-q>'subexit; });

        # end additional interface code
#
# save off some outside-access hooks to the parts we need
#

    $ref_button_1=$button_1;
    $ref_entry_2=$entry_2;
    $ref_entry_3=$entry_3;
    $ref_entry_4=$entry_4;

}


ktimer_ui $main_frame;
&chg_butstate if $startgo;
Tk::MainLoop;

1;
Grab a
gzipped
copy
here
 
Syntax highlighting using Syntax::Highlight::Engine::Kate