Reputation: 413
In this example how can I start all 4 stop watches at the same time?
This example code is over 12 years old but it is the best stopwatch example I have been able to find via Google. You can see that I have 4 instances of the class in use. I need to be able to start all the instances at the exact same time. Tinker button doesn't allow for calling multiple functions. Even if it did it would be one function before the next so technically they wouldn't all start at the exact same time.
I will need to stop each stopwatch at different times but that is easy by just calling each Stop function in the class. But I can't figure out how to start them all at the same time.
from Tkinter import *
import time
class StopWatch(Frame):
""" Implements a stop watch frame widget. """
def __init__(self, parent=None, **kw):
Frame.__init__(self, parent, kw)
self._start = 0.0
self._elapsedtime = 0.0
self._running = 0
self.timestr = StringVar()
self.makeWidgets()
def makeWidgets(self):
""" Make the time labels. """
l = Label(self, textvariable=self.timestr)
l.pack(fill=X, expand=NO, pady=2, padx=2)
self._setTime(self._elapsedtime)
def _update(self):
""" Update the label with elapsed time. """
self._elapsedtime = time.time() - self._start
self._setTime(self._elapsedtime)
self._timer = self.after(50, self._update)
def _setTime(self, elap):
""" Set the time string to Minutes:Seconds:Hundreths """
minutes = int(elap/60)
seconds = int(elap - minutes*60.0)
hseconds = int((elap - minutes*60.0 - seconds)*100)
self.timestr.set('%02d:%02d:%02d' % (minutes, seconds, hseconds))
def Start(self):
global sw2
""" Start the stopwatch, ignore if running. """
if not self._running:
self._start = time.time() - self._elapsedtime
self._update()
self._running = 1
def Stop(self):
""" Stop the stopwatch, ignore if stopped. """
if self._running:
self.after_cancel(self._timer)
self._elapsedtime = time.time() - self._start
self._setTime(self._elapsedtime)
self._running = 0
def Reset(self):
""" Reset the stopwatch. """
self._start = time.time()
self._elapsedtime = 0.0
self._setTime(self._elapsedtime)
def main():
root = Tk()
sw1 = StopWatch(root)
sw1.pack(side=TOP)
sw2 = StopWatch(root)
sw2.pack(side=TOP)
sw3 = StopWatch(root)
sw3.pack(side=TOP)
sw4 = StopWatch(root)
sw4.pack(side=TOP)
Button(root, text='Start', command=sw1.Start).pack(side=LEFT)
Button(root, text='Stop', command=sw1.Stop).pack(side=LEFT)
Button(root, text='Reset', command=sw1.Reset).pack(side=LEFT)
Button(root, text='Quit', command=root.quit).pack(side=LEFT)
root.mainloop()
if __name__ == '__main__':
main()
Upvotes: 1
Views: 1342
Reputation: 413
Here is what I ended up doing based on Bryan's idea of having one instance of the counter and then taking splits of the time. This works but I have not figured out a way to just use on Getspit function to grab each time. Maybe passing in the a,b,c,d and then an if to get the time? Right now I am doing this with buttons but once implemented it will be grabbing them via events that happen in the main program I am writing. If anyone has any improvements on this please let me know. Thanks to everyone for the help.
from Tkinter import *
import time
import tkMessageBox
class StopWatch(Frame):
""" Implements a stop watch frame widget. """
def __init__(self, parent=None, **kw):
Frame.__init__(self, parent, kw)
self._start = 0.0
self._elapsedtime = 0.0
self._running = 0
self.timestr = StringVar()
self.lapastr = StringVar()
self.lapbstr = StringVar()
self.lapcstr = StringVar()
self.lapdstr = StringVar()
self.makeWidgets()
def makeWidgets(self):
""" Make the time labels. """
la = Label(self, textvariable=self.timestr)
la.pack(fill=X, expand=NO, pady=2, padx=2)
#self._setTime(self._elapsedtime)
lb = Label(self, textvariable=self.timestr)
lb.pack(fill=X, expand=NO, pady=2, padx=2)
#self._setTime(self._elapsedtime)
lc = Label(self, textvariable=self.timestr)
lc.pack(fill=X, expand=NO, pady=2, padx=2)
#self._setTime(self._elapsedtime)
ld = Label(self, textvariable=self.timestr)
ld.pack(fill=X, expand=NO, pady=2, padx=2)
lsplita = Label(self, textvariable=self.lapastr)
lsplita.pack(fill=X, expand=NO, pady=2, padx=2)
lsplitb =Label(self, textvariable=self.lapbstr)
lsplitb.pack(fill=X, expand=NO, pady=2, padx=2)
lsplitc = Label(self, textvariable=self.lapcstr)
lsplitc.pack(fill=X, expand=NO, pady=2, padx=2)
lsplitd = Label(self, textvariable=self.lapdstr)
lsplitd.pack(fill=X, expand=NO, pady=2, padx=2)
self._setTime(self._elapsedtime)
def _update(self):
""" Update the label with elapsed time. """
self._elapsedtime = time.time() - self._start
self._setTime(self._elapsedtime)
self._timer = self.after(50, self._update)
def _setTime(self, elap):
""" Set the time string to Minutes:Seconds:Hundreths """
minutes = int(elap/60)
seconds = int(elap - minutes*60.0)
hseconds = int((elap - minutes*60.0 - seconds)*100)
self.timestr.set('%02d:%02d:%02d' % (minutes, seconds, hseconds))
def Start(self):
""" Start the stopwatch, ignore if running. """
if not self._running:
self._start = time.time() - self._elapsedtime
self._update()
self._running = 1
def Stop(self):
""" Stop the stopwatch, ignore if stopped. """
if self._running:
self.after_cancel(self._timer)
self._elapsedtime = time.time() - self._start
self._setTime(self._elapsedtime)
self._running = 0
def Getsplita(self):
""" Stop the stopwatch, ignore if stopped. """
if self._running:
self._lapstr = time.time() - self._start
self._setTime(self._elapsedtime)
self.lapastr.set(self._lapstr)
def Getsplitb(self):
""" Stop the stopwatch, ignore if stopped. """
if self._running:
self._lapstr = time.time() - self._start
self._setTime(self._elapsedtime)
self.lapbstr.set(self._lapstr)
def Getsplitc(self):
""" Stop the stopwatch, ignore if stopped. """
if self._running:
self._lapstr = time.time() - self._start
self._setTime(self._elapsedtime)
self.lapcstr.set(self._lapstr)
def Getsplitd(self):
""" Stop the stopwatch, ignore if stopped. """
if self._running:
self._lapstr = time.time() - self._start
self._setTime(self._elapsedtime)
self.lapdstr.set(self._lapstr)
def Reset(self):
""" Reset the stopwatch. """
self._start = time.time()
self._elapsedtime = 0.0
self._setTime(self._elapsedtime)
def main():
root = Tk()
sw1 = StopWatch(root)
sw1.pack(side=TOP)
Button(root, text='Start', command=sw1.Start).pack(side=LEFT)
Button(root, text='Stop', command=sw1.Stop).pack(side=LEFT)
Button(root, text='Reset', command=sw1.Reset).pack(side=LEFT)
Button(root, text='Quit', command=root.quit).pack(side=LEFT)
Button(root, text='Get Split A', command=sw1.Getsplita).pack(side=LEFT)
Button(root, text='Get Split B', command=sw1.Getsplitb).pack(side=LEFT)
Button(root, text='Get Split C', command=sw1.Getsplitc).pack(side=LEFT)
Button(root, text='Get Split D', command=sw1.Getsplitd).pack(side=LEFT)
root.mainloop()
if __name__ == '__main__':
main()
Upvotes: 1
Reputation: 385960
"Tinker [sic] button doesn't allow for calling multiple functions."
No, but it can call a single function which can call multiple functions.
def start():
for sw in (sw1, sw2, sw3, sw4):
sw.Start()
...
Button(root, text='Start', command=start).pack(side=LEFT)
You are correct that it is impossible to start them at precisely the same time. Though, they will all be within a millisecond or two of each other so for most situations it's quite good enough. Since you're only displaying the time to the granularity of a whole second, the timers should always show the same time.
If you really need them to be synchronized, you need them to share the same exact start time. You can do that by allowing the stopwatch to be given a start time. Then, you can compute the start time once and pass the same value to all four watches:
class Stopwatch(Frame):
...
def Start(self, starttime=None):
...
if not self._running:
if starttime is None:
self._start = time.time() - self._elapsedtime
else:
self._start = starttime - self.elapsedtime
...
...
t = time.time()
sw1.Start(t)
sw2.Start(t)
sw3.Start(t)
sw4.Start(t)
You could also have a single timer object that all of the stopwatches share. They then aren't so much stopwatches as they are "split timers" -- they can't control the starting of the watch, they can only record the interval that they were stopped at.
Upvotes: 1
Reputation: 22001
The following program may be close to want you want. Please note that since it takes time to start and stop the stopwatches, you may find small discrepancies among the times they are showing.
#! /usr/bin/env python3
import tkinter
import time
class StopWatch(tkinter.Frame):
@classmethod
def main(cls):
tkinter.NoDefaultRoot()
root = tkinter.Tk()
root.title('Stop Watch')
root.resizable(True, False)
root.grid_columnconfigure(0, weight=1)
padding = dict(padx=5, pady=5)
widget = StopWatch(root, **padding)
widget.grid(sticky=tkinter.NSEW, **padding)
root.mainloop()
def __init__(self, master=None, cnf={}, **kw):
padding = dict(padx=kw.pop('padx', 5), pady=kw.pop('pady', 5))
super().__init__(master, cnf, **kw)
self.grid_columnconfigure(1, weight=1)
self.grid_rowconfigure(1, weight=1)
self.__total = 0
self.__label = tkinter.Label(self, text='Total Time:')
self.__time = tkinter.StringVar(self, '0.000000')
self.__display = tkinter.Label(self, textvariable=self.__time)
self.__button = tkinter.Button(self, text='Start', command=self.click)
self.__label.grid(row=0, column=0, sticky=tkinter.E, **padding)
self.__display.grid(row=0, column=1, sticky=tkinter.EW, **padding)
self.__button.grid(row=1, column=0, columnspan=2,
sticky=tkinter.NSEW, **padding)
def click(self):
if self.__button['text'] == 'Start':
self.__button['text'] = 'Stop'
self.__start = time.clock()
self.__counter = self.after_idle(self.__update)
else:
self.__button['text'] = 'Start'
self.after_cancel(self.__counter)
def __update(self):
now = time.clock()
diff = now - self.__start
self.__start = now
self.__total += diff
self.__time.set('{:.6f}'.format(self.__total))
self.__counter = self.after_idle(self.__update)
class ManyStopWatch(tkinter.Tk):
def __init__(self, count):
super().__init__()
self.title('Stopwatches')
padding = dict(padx=5, pady=5)
tkinter.Button(self, text='Toggle All', command=self.click).grid(
sticky=tkinter.NSEW, **padding)
for _ in range(count):
StopWatch(self, **padding).grid(sticky=tkinter.NSEW, **padding)
def click(self):
for child in self.children.values():
if isinstance(child, StopWatch):
child.click()
if __name__ == '__main__':
ManyStopWatch(4).mainloop()
Upvotes: 1