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.
#!/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.
#!/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()