Feineigle.com - Simply Timers

Home · Projects · 2016 · Simply Timers

Published: September 23, 2016
Tags:  Bash · GUI · Python · Software


A few years ago I would constantly burn whatever I was cooking. The story was always the same: I would put something on the stove, go back to the computer, and wait until the smoke detector reminded me I had burned dinner, again. Eventually I wrote a simple bash script using zenity to act as a sort of egg timer. My timer is hotkeyed to meta-T. Once called it prompts you to type in the number of minutes you want to be reminded in. This worked very well and I haven’t burned any food in years! After a while though, I wanted a few more features.


timer.sh

#!/bin/bash
#Mark Feineigle 2014 03 14
#Wait x time, then play sound/note & prompt for input. On input "0" close, on input "-ge 1" loop 

END_SOUND="/home/egg/.sounds/river_done.wav"
#FUNCTIONS
timer () {
  while [ $TIME -ge 1 ]  2>/dev/null #loop while TIME >0
  do
    if [ $1 == "-m" ]; then
      let TIME=$TIME*60 #multiply for minutes
      TITLE="Minutes";TEXT="minutes" #set labels for use in zenity
    else
      TITLE="Seconds";TEXT="seconds" #set labels for use in zenity
    fi
    sleep $TIME #sleep for TIME
    aplay $END_SOUND > /dev/null 2>&1 & #play sound, suppress output, run in background
    if [ $NOTE != "" ]  2>/dev/null; then #if there is a NOTE, display it
      zenity --title="Reminder" --info --text="$NOTE"
    fi
    export TIME=`zenity --title="$TITLE" --entry --text="More $TEXT?"` #input TIME from GUI
    if [ $TIME == "0" -o $TIME == "" ]  2>/dev/null; then #if TIME = 0 of null
      exit 0
    fi
    NOTE="" #clear old note
    if [ $TIME -ge 11 ] 2>/dev/null; then #if TIME is longer than x
      export NOTE=`zenity --title="Reminder" --entry --text="Note?"` # input NOTE from GUI
    fi
  done
  exit 0
}
#START OF MAIN
if [ "$1" == "-s" -a "$2" -ge 1 ] 2>/dev/null; then  #if the 1st arg is "-s" and 
                                                     #the 2nd arg is >=1
  TIME="$2";NOTE="$3" #set 2nd/3rd args as TIME/NOTE
  timer $1 #pass $1 (-s) to timer function
elif [ "$1" == "-s" -a "$2" == "" ]; then #if no 2nd arg
  export TIME=`zenity --title="Seconds" --entry --text="How many seconds?"` #read SECONDS 
  if [ $TIME == "0" -o $TIME == "" ] 2>/dev/null; then #if 0 or null
    exit 0
  fi
  if [ $TIME -ge 3 ] 2>/dev/null; then #if TIME longer than x
    export NOTE=`zenity --title="Reminder" --entry --text="Note?"` # add NOTE from GUI
  fi
  timer $1 #$TIME $NOTE   #call timer function, exported TIME/NOTE = no passing needed?
elif [ "$1" == "-m" -a  "$2" -ge 1 ] 2>/dev/null; then #if 1st arg is "-m" and 2nd arg is >0
  TIME="$2";NOTE="$3" #set 2nd/3rd args as TIME/NOTE
  timer $1 #pass $1 (-m) to timer function
elif [ "$1" == "-m" -a "$2" == "" ]; then #GUI prompt for integer
  #return MINUTES from GUI
  export TIME=`zenity --title="Minutes" --entry --text="How many minutes?"`
  if [ $TIME == "0" -o $TIME == "" ] 2>/dev/null; then #if 0 of null
    exit 0
  fi
  if [ $TIME -ge 9 ] 2>/dev/null; then #if TIME longer than x
    # add NOTE from GUI
    export NOTE=`zenity --title="Reminder" --entry --text="Note?"`
  fi
  timer $1 #$TIME $NOTE   #call timer function, exported TIME/NOTE = no passing needed?
elif [ "$1" -ge 1 ]  2>/dev/null; then #if 1st arg is >0
  TIME="$1";NOTE="" #set TINE as 1st arg, NOTE as null
  timer -m #call timer function with $1 = "=m"
else
  printf "minutes usage = $0 [MINUTES]\n"
  printf "              = $0 -m [MINUTES]\n\n" #display help on any non-defined useage
  printf "seconds usage = $0 -s [SECONDS]\n\n"
  printf "this help     = $0 -h\n"
  printf "	      = $0 --help\n\n"
fi
exit 0

Finding an excuse to learn some Qt, I wrote a new version of the timer in python. This version works much the same way, but with a few extra features. After an alarm sounds the program starts counting up, letting you know how long ago the initial alarm sounded. Next, it emits its alarm sound every 5 minutes after the initial alarm sounds. This isn’t too important when used as an egg timer, but now it can be issued a 0 minute timer that alerts immediately and then counts up. It keeps track of how long has been spent on a particular task and also sounds off with 5 minute metering.

timer.py

#!/usr/bin/env python
#Author: Mark Feineigle
#Create Date: 2016/08/21
#Last Modify: 2016/08/22
'''
A replacement for my bash timer script, timer.sh
'''
#TODO: better variable names
# layout
# hide title bar DONE
#  disables esc to close
# icon
# highlight tab order
import commands #use aplay to play sound
import sys #argv for qt
from PyQt4 import QtGui, QtCore
import time #asctime

END_SOUND = "/home/USER/.sounds/quiet_river_done.wav"

class myDialog(QtGui.QDialog):
  '''The 'main' timer window'''
  def __init__(self, parent=None):
    super(myDialog, self).__init__(parent)
    #variables
    self.counter = 0 #counter
    self.flag = False #flag for counting or not
    #text inputs
    self.inp = QtGui.QLineEdit("0")
    self.connect(self.inp, QtCore.SIGNAL("returnPressed()"), self.sleepTimerOn) 
    self.note = QtGui.QLineEdit("Note")
    self.connect(self.note, QtCore.SIGNAL("returnPressed()"), self.sleepTimerOn) 
    #buttons
    #NOTE this button has no purpose, without it the first button created is
    # called after sleepTimerOn, no idea...
    #NOTE now that i've broken sleep into 2 functions, this is required to not 
    # start the counter when a non-int is entered...
    #NOTE adding a quit button seems to work...
    self.countbtn = QtGui.QPushButton("Quit", self)
    #TODO how to link to esc?
    self.connect(self.countbtn, QtCore.SIGNAL("escapePressed()"), self.quit) 
    #self.countbtn.hide()
    self.countbtn.clicked.connect(self.quit)
    self.startbtn = QtGui.QPushButton("On/Off", self)
    self.startbtn.clicked.connect(self.countToggle)
    self.sleepbtn = QtGui.QPushButton("Sleep", self)
    self.sleepbtn.clicked.connect(self.sleepTimerOn)
    #labels
    self.msgLbl = QtGui.QLabel("")
    t = self.formatTime(self.counter)
    self.counterLbl = QtGui.QLabel(
		   "<font color=white size=6 style='font-family:Monospace;'><b>"
                   +str(t)+
                   "</b></font>")
    #grid
    grid = QtGui.QGridLayout()
    grid.addWidget(self.counterLbl, 1, 2) #matplot like coords
    #TODO better layout for changing label size
    grid.addWidget(self.msgLbl, 1, 1) #matplot like coords
    grid.addWidget(self.inp, 1, 4) #matplot like coords
    grid.addWidget(self.note, 2, 1) #matplot like coords
    grid.addWidget(self.countbtn, 2, 2)
    grid.addWidget(self.startbtn, 2, 3)
    grid.addWidget(self.sleepbtn, 2, 4)
    self.setLayout(grid)

    self.setWindowTitle("Tick tock mother fucker")
    self.inp.setFocus()
    self.show()

  def sleepTimerOn(self):
    '''Connected to sleepbtn and inp box. 
    Hides the window and sleeps.'''
    #print self.flag, "1"
    self.startTime = time.asctime().split()[3]
    try:
      t = int(self.inp.displayText()) #get input
      self.hide() #if successful in converting to input to int
      QtCore.QTimer.singleShot(t*60*1000, self.sleepTimerOff) #wait milliseconds then..loop
    except:
      sys.exit() #input was not an int
    finally:
      self.inp.setFocus()

  def sleepTimerOff(self):
    '''Called from sleepTimerOn
    Resets counter when it wakes up.'''
    self.stopTime = time.asctime().split()[3]
    msg = "Slept {} mins\n{}\n{}".format(self.inp.displayText(),
      self.startTime, self.stopTime)
    self.msgLbl.setText(msg)
    self.counter = 0 #start counting up from 0 after timer expires
    #print self.flag, "2"
    if self.flag == False:
      self.countToggle()
    commands.getoutput("aplay "+END_SOUND)
    self.show()
    #print self.flag, "7"

  def countToggle(self):
    '''Connected to startbtn'''
    #print self.flag, "3" 
    self.flag = not self.flag
    if self.flag:
      self.countUp()
    #print self.flag, "6"

  def countUp(self):
    '''Counting loop'''
    #print self.flag, "4"
    if self.flag:
      self.counter+=1
      if not self.counter%300:
	print "minute passed"
	commands.getoutput("aplay "+END_SOUND)
      t = self.formatTime(self.counter)
      self.counterLbl.setText(
                    "<font color=white size=6 style='font-family:Monospace'><b>"
                    +str(t)+
                    "</b></font>")
      QtCore.QTimer.singleShot(1000, self.countUp) #counts the (milli)seconds
    #print self.flag, "5"

  def formatTime(self, t):
    '''Helper to display nicely formatted times'''
    minutes = t/60
    seconds = t%60
    if seconds < 10:
      seconds = "0"+str(seconds)
    return str(minutes)+":"+str(seconds)

  def quit(self):
    sys.exit()

def main():
  app = QtGui.QApplication(sys.argv)
  myDia = myDialog()
  #TODO hide titlebar
  # add icon
  #TODO highlight selected element w/ tab
  #myDia.setWindowFlags(QtCore.Qt.SplashScreen) #hides the default titlebar
  #TODO center window on screen and link esc
  #TODO spacebar to start/stop button
  myDia.show()
  app.exec_()

if __name__ == '__main__':
  main()