I have here a very basic timer, just a standalone working snippet of a large project.
It takes an integer (15 in this demo) and counts down in seconds to 0.
The display is H:M:S. As the integer will never be higher than 20 the display is largely redundant, it really needs to be: mins:secs:10ths. Can this be achieved? Thanks.
import time
import wx
########################################################################
class MyFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title='Timer')
panel = wx.Panel(self)
self.countdown = 15 #seconds
lbl = '00:00:' + str(self.countdown)
font = wx.Font(24, wx.FONTFAMILY_ROMAN,
wx.FONTSTYLE_NORMAL,
wx.FONTWEIGHT_BOLD)
self.lbl = wx.StaticText(panel, label=lbl)
self.lbl.SetFont(font)
btn = wx.Button(panel, label='Start Countdown')
btn.Bind(wx.EVT_BUTTON, self.start)
self.timer1 = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.prevTimer, self.timer1)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.lbl, 0, wx.ALL, 5)
sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5)
panel.SetSizer(sizer)
self.Show()
def start(self, event):
self.timer1.Start(1000)
def prevTimer(self, evt):
self.countdown -= 1
seg = time.strftime("%H:%M:%S", time.gmtime(self.countdown))
if seg == "00:00:00": self.timer1.Stop()
self.lbl.SetLabel(str(seg))
if __name__ == '__main__':
app = wx.App(False)
frame = MyFrame()
app.MainLoop()
You could try the wx.TimeSpan class.
import wx
ts = wx.TimeSpan(0, sec=15)
while ts.IsPositive():
print(ts.Format("%M:%S:%l"))
# Snip off last character, if preferred:
# print(ts.Format("%M:%S:%l")[:-1])
ts -= wx.TimeSpan(0, msec=500)
The "%M:%S:%l"
format will return the value in minutes, seconds and milliseconds.
This example decrements by 500msec just to show the milliseconds are changing. In your example you would use wx.TimeSpan(0, sec=1)
.
If 3 digits for milliseconds are too many, you could use string slicing to snip off unwanted trailing characters (as in the commented out line).
I think the format such as 00:00:500
where 500
indicates milliseconds is a bit strange to me. Maybe 00:00.500
?
The timedelta
module can be another option.
>>> from datetime import timedelta
>>> '{}'.format(timedelta(seconds=11.111)).rstrip('0')
'0:00:11.111'
EDIT Sorry that rstrip('0')
was a bad idea; it will return the wrong string if seconds is an integer.
Sorry, I don’t understand. How is making use of the wx.timer?
My code was just demonstrating how to format the time string.
In your example, you could do something like:
import wx
########################################################################
class MyFrame(wx.Frame):
""""""
# ----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title='Timer')
panel = wx.Panel(self)
self.countdown = wx.TimeSpan(0, sec=15)
lbl = self.getTimeStr()
font = wx.Font(24, wx.FONTFAMILY_ROMAN,
wx.FONTSTYLE_NORMAL,
wx.FONTWEIGHT_BOLD)
self.lbl = wx.StaticText(panel, label=lbl)
self.lbl.SetFont(font)
btn = wx.Button(panel, label='Start Countdown')
btn.Bind(wx.EVT_BUTTON, self.start)
self.timer1 = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.prevTimer, self.timer1)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.lbl, 0, wx.ALL, 5)
sizer.Add(btn, 0, wx.ALL | wx.CENTER, 5)
panel.SetSizer(sizer)
self.Show()
def start(self, event):
self.timer1.Start(1000)
def getTimeStr(self):
return self.countdown.Format("%M:%S:%l")[:-1]
def prevTimer(self, evt):
self.countdown -= wx.TimeSpan(0, sec=1)
seg = self.getTimeStr()
self.lbl.SetLabel(seg)
if seg == "00:00:00": self.timer1.Stop()
if __name__ == '__main__':
app = wx.App(False)
frame = MyFrame()
app.MainLoop()
I’m not getting any 10ths with this script. Just the seconds counting down.
Maybe make some changes???
In start() use:
self.timer1.Start(100)
In prevTimer() use:
self.countdown -= wx.TimeSpan(0, msec=100)
must the event queue be in between ?
I had need for a timer a while back so I wrote this up. I’ve attached the project as a zip. There is a MarkDownPad file with a brief explanation.
timer 2022-09-02 14-00.zip (1.5 MB)
da-dada
September 2, 2022, 8:42pm
10
@ReverendJim I have to say it’s getting moving: why not use wx.SingleInstanceChecker ? too boring ? but I hope it’s more general !
RE: SingleInstanceChecker - I didn’t use it because I didn’t know about it.
da-dada
September 4, 2022, 8:16pm
12
well @floatingshed , before that large project is drowning your float try this slight modification
import time
import wx
########################################################################
class MyFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title='Timer')
panel = wx.Panel(self)
self.countdown = 15 #seconds
lbl = '00:00:' + str(self.countdown)
font = wx.Font(24, wx.FONTFAMILY_ROMAN,
wx.FONTSTYLE_NORMAL,
wx.FONTWEIGHT_BOLD)
self.lbl = wx.StaticText(panel, label=lbl)
self.lbl.SetFont(font)
btn = wx.Button(panel, label='Start Countdown')
btn.Bind(wx.EVT_BUTTON, self.start)
self.timer1 = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.prevTimer, self.timer1)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.lbl, 0, wx.ALL, 5)
sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5)
panel.SetSizer(sizer)
self.Show()
def start(self, event):
self.timer1.Start(100)
self.st = time.time()
def prevTimer(self, evt):
# self.countdown -= 1
# seg = time.strftime("%H:%M:%S", time.gmtime(self.countdown))
seg = round(self.countdown - time.time() + self.st, 1)
# if seg == "00:00:00": self.timer1.Stop()
if not seg:
self.timer1.Stop()
self.lbl.SetLabel(str(seg))
if __name__ == '__main__':
app = wx.App(False)
frame = MyFrame()
app.MainLoop()
da-dada
September 5, 2022, 10:08pm
13
@komoto48g the pythonista version (I’m an alien )
import time
from threading import Thread, Event
import wx
########################################################################
class MyFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title='Timer')
panel = wx.Panel(self)
self.countdown = 15 #seconds
self.lbl = '00:00:' + str(self.countdown)
font = wx.Font(24, wx.FONTFAMILY_ROMAN,
wx.FONTSTYLE_NORMAL,
wx.FONTWEIGHT_BOLD)
self.lbl = wx.StaticText(panel, label=self.lbl)
self.lbl.SetFont(font)
btn = wx.Button(panel, label='Start Countdown')
btn.Bind(wx.EVT_BUTTON, self.start)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.lbl, 0, wx.ALL, 5)
sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5)
panel.SetSizer(sizer)
self.Show()
def start(self, event):
if 'evt' in vars(self):
self.evt.set()
self.evt = Event()
Counter(self.evt, self.countdown, self.lbl).start()
class Counter(Thread):
def __init__(self, evt, cnt_down, label):
Thread.__init__(self)
self.cnt_down = cnt_down
self.lbl = label
self.finished = evt
def run(self):
st = time.time()
try:
while not self.finished.is_set():
self.finished.wait(0.1)
seg = round(self.cnt_down - time.time() + st, 1)
if seg < 0.0:
self.finished.set()
else:
self.lbl.SetLabel(str(seg))
except Exception:
pass
if __name__ == '__main__':
app = wx.App(False)
frame = MyFrame()
app.MainLoop()
da-dada
September 6, 2022, 8:23pm
14
@floatingshed and if the QC is breathing down your neck (even pylint could smell it, but I didn’t change the overall ambiance of your float) here is something more acceptable to them: more involved, but they always gave me the feeling that is their main & only objective (just look at that beautiful & effective try construct)
import time
from threading import Thread, Event
import wx
########################################################################
class MyFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title='Timer')
panel = wx.Panel(self)
self.countdown = 15 #seconds
self.lbl = '00:00:' + str(self.countdown)
font = wx.Font(24, wx.FONTFAMILY_ROMAN,
wx.FONTSTYLE_NORMAL,
wx.FONTWEIGHT_BOLD)
self.lbl = wx.StaticText(panel, label=self.lbl)
self.lbl.SetFont(font)
btn = wx.Button(panel, label='Start Countdown')
btn.Bind(wx.EVT_BUTTON, self.start)
self.evt = None
def on_destroy(_):
if self.evt:
self.evt.set()
self.Bind(wx.EVT_WINDOW_DESTROY, on_destroy)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.lbl, 0, wx.ALL, 5)
sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5)
panel.SetSizer(sizer)
self.Show()
def start(self, _):
if self.evt:
self.evt.set()
self.evt = Event()
Counter(self.evt, self.countdown, self.lbl).start()
class Counter(Thread):
def __init__(self, evt, cnt_down, label):
Thread.__init__(self)
self.cnt_down = cnt_down
self.lbl = label
self.finished = evt
def run(self):
st = time.time()
while True:
self.finished.wait(0.1)
seg = round(self.cnt_down - time.time() + st, 1)
if seg < 0.0 or self.finished.is_set():
break
self.lbl.SetLabel(str(seg))
if __name__ == '__main__':
app = wx.App(False)
frame = MyFrame()
app.MainLoop()