[EKB] This is a simple task timer I built using [clock.tcl]. Here's what it looks like: [http://www.kb-creative.net/screenshots/Timer.gif] It's set to be [always on top], at least on Windows. It combines a clock with a timer. The "copy to clipboard" feature pastes the date, start time, and time elapsed to the clipboard, separated by tabs, then resets the timer: Tue, 13 Sep 2005 17:30 00:13 The reason for this is that it can then be pasted into a table (e.g., [TkTable], Excel, OpenOffice.org Calc) for easy manipulation. It can also be pasted straight into a text file. I then type in the project & task information, and keep going. I use this to keep track of my billed time. Here's a typical workflow: 1. Click the "start/pause/stop" button when starting a task. 1. Click the "start/pause/stop" to pause & restart when taking a break. 1. When done with the task, click "paste to clipboard" and paste into log. If there's an error, then click "reset" to reset the timer. Here's the code: ###################################################### ## ## Always on top, no resize ## ###################################################### wm attributes . -topmost 1 wm resizable . 0 0 ###################################################### ## ## Buttons ## ###################################################### image create photo resetclock16 -data { R0lGODlhEAAQAIYAAPwCBDzSdDzGdGTWlETOfEzSfGTKjFzOhFTOhFzSjEzS hJz2vCyqXHTWlPT+/NT65MT23Lz21KzyxKTuvIzmrDS+bCSiVHzapITytHzy rFzulEzmjETafDTCbDSyZCyuXBR+PHTWnMz65HTupGTunFTmjDzafETOdBSC RARCHHzipLz61CSaVByOTAx2PCSmVAQ6HJTyvKz6zDyGXAQKBAQeDAxmLCx2 TEzGfBSGRAQqFARWLByGPBxuNMTu1HzGlITipFS6fGzWlByWTAxSJFy6hFTW hDzKdCSeVByKRBx6PBx2PAxeNAReLARKJARGHAQiDAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAA LAAAAAAQABAAAAe1gACCg4MBhIeIAgOHBAUGBwcICQUICgsMhA0ODw8QERIT FBQNFRaDFw8YGRkaGxwBHR4eHyCCISIjJCUbJicVHgwMKCmCKiskHSwtKCgu Li8oMIMxMiYGMzSCNc42LjeDFDgUOTWCOjs2PCg9gj4/QCZBQgUFpRZDNkSC RUABAkYFcPzyYIGHDWIAUjA4UgrJkCQolLhYwgRhQgs5bNhownGHxyZOCD1x UQ6RyRRQTKpcOchPIAAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJz aW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVz ZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7 } image create photo appclock16 -data { R0lGODlhEAAQAIUAAPwCBHy+/HS6/Gy2/ITC/Pz+/Fyu/FSm/Pz6/ESW7Pzm hPzqnPzurOz2/OTu/PzmjPzqpAR2/PzyxPz2xPz2zEye9NTm/ARy/Pz21Mzi /Lza/DyK3PzutPzyvPzyzPz63Pz67DyO5KzS/Pz23KTO/DSC1DSC3JzK/CR6 zPz+9Iy+/FSq/IS6/BxyxPT2/Hy2/HSy/MTe/Gyu/CRyxJTG/IS+/DSG1AAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAaz QIBQGAgIBIPBcAkgBArQqOHAJESRA0MBgUgMrYWjYrFgHA4NhxfwFCgeC0hE MqFULN5nGF6+SDAYCRkaG21wEBwdER4fICAhIhsCBQNkDBwRFxgjjhskJZMD iBeZF44gBSYnKAMFBpd/m44pKSUqrFArE4yNqKkqLC0ABi4HHhinBSkFLS/C ACtpFQmOUCYlLDDPABUOGTEhJeIvLzIzTAkJIiInNDXO20wbNiYoKOdLfkEA If5oQ3JlYXRlZCBieSBCTVBUb0dJRiBQcm8gdmVyc2lvbiAyLjUNCqkgRGV2 ZWxDb3IgMTk5NywxOTk4LiBBbGwgcmlnaHRzIHJlc2VydmVkLg0KaHR0cDov L3d3dy5kZXZlbGNvci5jb20AOw== } image create photo record16 -data { R0lGODlhEAAQAIYAAPwCBHSSxJyu1DxOdPS6RPTSjPTOdPzSfKyijMza9PT2 /Nzm/ISChNyeLOSmLIxiHPzCZPTy5Pz2xKSqtFx6xHSOzFyG1FRWZPyODPRq DEQaBOy+XPzmvMS2pIx6bIxyVIRqRKxmLOxaBDQSBPTq1PzqrPzupPzijPzS ZOyuPOSWLNR2HNRuJMRmJEQuLPzq1PTirPzObPyuLLSabKzK/LTO/KTO/BxK hPTCbPzalPzWfPy+TPSeDLSSXNTq/Mzi/LTS/FSm/AQ6fOy+ZPTalPzKXPSq HLyabMzm/LzW/IzC/DyS/ByO/PS2PNTi/MTa/Mze/Jy+/Fyu/PS+ZPzmxPzO dPTCXLyedKzO/JzC/Iyy/JS2/GSy/MTe/HS6/PS6TPy6RLyebKTG/PzqtPzS bPzCRLyqhHy+/JRSLIROJIxKHIRCHHRmbLzq/Lzi/LTa/Lze/GS6/AQWNARS pARKlARKnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAA LAAAAAAQABAAAAfagACCggECA4OIAAQFBgcICQoLDA0OD4gQERITFAEVARYX GBkagxsRHB0MDB4fICEZIiODECQcJSYnKCkqKywtLqUvMDAFMTIzNAk1NTY3 gjgcOQU6Ozw9Pj9ANTRBQgBDHERERUY8R0gLSUlKS0wA0DkxBE1GRz5OT0lQ UVIAU1RVrMwzcgUJFCxZtGzh8o4DhB0Dr/joQiNLlCxeAGyA8QVME3phkHQR U5FGxipjdJBBUaSMGR9JaIiZeUYQmjRq1uRk0wabmzdw4iQaJGcOnaN16MwZ yhSRn0AAIf5oQ3JlYXRlZCBieSBCTVBUb0dJRiBQcm8gdmVyc2lvbiAyLjUN CqkgRGV2ZWxDb3IgMTk5NywxOTk4LiBBbGwgcmlnaHRzIHJlc2VydmVkLg0K aHR0cDovL3d3dy5kZXZlbGNvci5jb20AOw== } ###################################################### ## ## "Minimalist" balloon help from Tcler's Wiki ## ## With modest change so that there isn't much delay ## while browsing over buttons. ## ###################################################### namespace eval balloon { variable long_delay 750 variable short_delay 50 variable delay $long_delay variable family {} } bind . { if {$balloon::family != ""} { if {[lsearch -exact $balloon::family %W] == -1} { set balloon::family {} set balloon::delay $balloon::long_delay } } } proc set_balloon {w help} { bind $w "+after \$balloon::delay [list balloon::show %W [list $help]]; set balloon::delay $balloon::short_delay; set balloon::family \[balloon::getwfamily %W\]" bind $w "+destroy %W.balloon" } # Add these to the namespace proc balloon::getwfamily {w} { return [winfo children [winfo parent $w]] } proc balloon::show {w arg} { if {[eval winfo containing [winfo pointerxy .]]!=$w} {return} set top $w.balloon catch {destroy $top} toplevel $top -bd 1 -bg black wm attributes $top -topmost 1 wm overrideredirect $top 1 if {$::tcl_platform(platform) == "macintosh"} { unsupported1 style $top floating sideTitlebar } pack [message $top.txt -aspect 10000 -bg lightyellow -padx 1 -pady 0 \ -text $arg] set wmx [expr [winfo rootx $w]+5] set wmy [expr [winfo rooty $w]+[winfo height $w]+7] wm geometry $top \ [winfo reqwidth $top.txt]x[winfo reqheight $top.txt]+$wmx+$wmy raise $top } ###################################################### ## ## Timer procs ## ###################################################### proc every {ms body} { eval $body after $ms [list every $ms $body] } proc timeElapsed {sec} { set hours [expr {$sec / 3600}] set mins [expr {($sec / 60) % 60}] return [format "%02d:%02d" $hours $mins] } # Are we timing, or not? set timer(state) 0 # Has the timer been reset? set timer(reset) 1 # Store previous time elapsed set timer(prev) 0 proc toggleTimer {b} { if {$::timer(state)} { $b config -relief flat set ::timer(state) 0 set ::timer(prev) [expr {$::timer(prev) + [clock sec] - $::timer(init)}] } else { $b config -relief sunken if {$::timer(reset)} { set ::timer(reset) 0 } set ::timer(init) [clock sec] set ::timer(state) 1 } } proc resetTimer {b} { set ::timer(reset) 1 set ::timer(elapsed) "00:00" set ::timer(init) [clock sec] set ::timer(prev) 0 } proc recordTime {b} { # Stop timing if {$::timer(state)} { toggleTimer $b } # Save info set date [clock format [clock sec] -format "%a, %d %b %Y"] set start "--:--" if [info exists ::timer(init)] { set start [fmtTime $::timer(init)] } set elapsed $::timer(elapsed) # Reset the timer resetTimer $b clipboard clear clipboard append -type STRING "$date\t$start\t$elapsed" } proc fmtTime {sec} { return [clock format $sec -format %H:%M] } proc updateTimer {} { set ::timer(now) [fmtTime [clock sec]] if {$::timer(state)} { set elapsed_sec [expr {$::timer(prev) + [clock sec] - $::timer(init)}] set ::timer(elapsed) [timeElapsed $elapsed_sec] } } ## -- Top frame pack [frame .top] -side top pack [label .top.l -textvar timer(now) -font {Tahoma 18}] -side left # Buttons pack [frame .top.buttons] -side right set clockbutton [button .top.buttons.t -image appclock16 -relief flat] pack $clockbutton -side left set recordbutton [button .top.buttons.rec -image record16 -relief flat] pack $recordbutton -side left set resetbutton [button .top.buttons.close -image resetclock16 -relief flat] pack $resetbutton -side left ## -- Bottom frame pack [frame .bottom] -side bottom pack [label .bottom.l -textvar timer(elapsed) -font {Tahoma 10}] $clockbutton config -command "toggleTimer $clockbutton" set_balloon $clockbutton "Start/stop timer" $recordbutton config -command "recordTime $clockbutton" set_balloon $recordbutton "Save to clipboard" $resetbutton config -command "resetTimer $clockbutton" set_balloon $resetbutton "Reset timer" ###################################################### ## ## Start program! ## ###################################################### set timer(elapsed) "00:00" every 1000 updateTimer ---- [EKB] With the magic of FreeWrap, this is now available to the world as a Windows executable: [http://tasktimer.kb-creative.net/]