Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Multiple Timers
#1
Hello,

I am currently in the design phase of a app that will be used to time the start, stop and elapsed time of certain events as they are triggered by a user clicking on a button.

I want to have several timers (counting durations in hh:mm format) displaying their values each minute. Each of the timers will start, pause and reset based on events triggered by the user clicking on certain GUI buttons.

Coming from a vb.net event driven world I had envisioned each timer being an object with methods of aTimer.Start, aTimer.Stop, aTimer.Reset and these objects would be on a form and accumulating time and updating the display constantly.

Can someone give me some pointers on how I would approach this using Python?

Thank you,

Scott
Reply
#2
assuming you want these timers to be non blocking? as in running one timer wont stop the program from continuing untill it has completed, and will instead happily run in the background.

if this is the case you're going to have make a multithreaded program...which can be painful to say the least in python.
Reply
#3
I think all GUI toolkits have the concept of a timer. In tk you us "after". In Qt you use QTimer. You could use multiple threads or processes, but if this is a GUI app, use what the GUI provides.

I would have only one timer and have it process a list of event_timer. An event timer is essentially a function to execute and a time to execute. The can run periodically, checking each event timer and executing the function when the event time is reached. Another choice is to set the timer to execute when the next event is scheduled. The logic is more complicated (you need to find the next event each time an event is added or executed and use that to set the timer wait), but it may result in better precision and less overhead.

You may want two types of event timers, periodic and one shot. A periodic event executes after some delay and continues to do so until stopped. A one shot executes after some delay and removes itself from the event timer list. Another possibility is to make everything a one shot and implement a periodic event by having the callback function add a new event timer.

This is an event timer I wrote for an application that uses PySide2 (Qt GUI).
"""Event is a scheduled function call
Events are added to a list that is processed during processor free time.  The start
time is compared to the current time, and the event is run if the elapsed
time is greater than the event delay.

Running an event calls the event callback with the event arguments.  These
are set at creation or by calling the "connect" or "execute" methods.  The
event does not run again unless the "resume method is called

"execute" is a one-time event that is removed from the event list after it
is run.  Use execute to do a delayed function call.  Use "start" for
events that run periodically.
"""

import time

class Event:
    """Delayed function call"""

    events = []
    timer = None

    @classmethod
    def add_event(cls, event):
        """Add event to cycle"""
        if not event in cls.events:
            cls.events.append(event)

    @classmethod
    def remove_event(cls, event):
        """Remove event from cycle"""
        cls.events.remove(event)

    @classmethod
    def timer_tic(cls):
        """Run periodically to schedule and fire events"""
        current_time = time.time()
        [event.tic(current_time) for event in cls.events]

    def __init__(self):
        """Initialize Event."""
        self.delay = 0.0
        self.callback = None
        self.args = ()
        self.kwargs = {}
        self.time = time.time()
        self.oneshot = False
        self.active = False
        self._resume = False

    def __repr__(self):
        """Return string representation of event"""
        return f'(Event {self.callback}) delay {self.delay}, active {self.active}'

    def tic(self, current_time):
        """Event countdown"""
        if not self._resume:
            self.time = current_time
        elif current_time - self.time >= self.delay:
            self._resume = False
            if self.callback is not None:
                self.callback(*self.args, **self.kwargs)
            if self.oneshot:
                self.stop()

    def connect(self, callback, *args, **kwargs):
        """Function to execute when event fires"""
        self.callback = callback
        self.args = args
        self.kwargs = kwargs
        return self

    def start(self):
        """Add event to the cycle"""
        if not self.active:
            self.oneshot = False
            self.active = True
            Event.add_event(self)
        self.resume()
        return self

    def stop(self):
        """Remove event from the cycle"""
        if self.active:
            self.active = False
            self.remove_event(self)
        return self

    def execute(self, callback, *args, **kwargs):
        """Use event as a delayed function call.  Runs once and is removed from event loop.
        Option args and kwargs must be list or tuple
        """
        self.callback = callback
        self.args = args
        self.kwargs = kwargs
        self.oneshot = True
        self.active = True
        self.resume()
        Event.add_event(self)

    def resume(self):
        """Run event again"""
        self.time = time.time()
        self._resume = True
        return self

    def setdelay(self, sec):
        """Set delay between events"""
        self.delay = sec
        return self

    def dump(self):
        """Print list of events"""
        for event in self.events:
            print(event)
This is the code in the main program that creates the Event timer.
    event_timer = QtCore.QTimer(None)
    event_timer.timeout.connect(Event.timer_tic)
    event_timer.start(1)
Reply
#4
The Gui would be responsible for calling the method get_duration every update interval.
import datetime
import time


class Timer:
    def __init__(self):
        self.timedelta = datetime.timedelta()
        self.running = False

    def start(self):
        self.start_time = datetime.datetime.now()
        self.running = True

    def stop(self):
        if not self.running:
            return
        self.timedelta += datetime.datetime.now() - self.start_time
        self.running = False

    def reset(self):
        self.timedelta = datetime.timedelta()
        self.running = False

    def get_duration(self):
        duration = self.timedelta
        if self.running:
            duration += datetime.datetime.now() - self.start_time
        return duration


timer = Timer()
timer2 = Timer()
timer.start()
timer2.start()
time.sleep(1)
print(f'Timer1: {timer.get_duration()}')
print(f'Timer2: {timer2.get_duration()}')
time.sleep(1)
print(f'Timer1: {timer.get_duration()}')
print(f'Timer2: {timer2.get_duration()}')
timer2.stop()
print('Timer2 stopped')
time.sleep(1)
print(f'Timer1: {timer.get_duration()}')
print(f'Timer2: {timer2.get_duration()}')
timer2.start()
print('Timer2 started')
time.sleep(1)
print(f'Timer1: {timer.get_duration()}')
print(f'Timer2: {timer2.get_duration()}')
time.sleep(1)
print(f'Timer1: {timer.get_duration()}')
print(f'Timer2: {timer2.get_duration()}')
timer.reset()
timer.start()
print('timer1 reset & restarted')
time.sleep(1)
print(f'Timer1: {timer.get_duration()}')
print(f'Timer2: {timer2.get_duration()}')
time.sleep(1)
print(f'Timer1: {timer.get_duration()}')
print(f'Timer2: {timer2.get_duration()}')
Output:
Timer1: 0:00:01.000916 Timer2: 0:00:01.000916 Timer1: 0:00:02.001026 Timer2: 0:00:02.001026 Timer2 stopped Timer1: 0:00:03.001449 Timer2: 0:00:02.001026 Timer2 started Timer1: 0:00:04.001503 Timer2: 0:00:03.001080 Timer1: 0:00:05.001634 Timer2: 0:00:04.001211 timer1 reset & restarted Timer1: 0:00:01.000573 Timer2: 0:00:05.001784 Timer1: 0:00:02.001267 Timer2: 0:00:06.002478
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Quick Help - Timers - Need Small Solution for Working Code EwH006 5 3,978 Nov-17-2020, 04:09 AM
Last Post: EwH006
  Timers In python ShruthiLS 3 2,847 Jun-11-2018, 05:31 AM
Last Post: ShruthiLS

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020