Source code for SimpleGUICS2Pygame.simpleguics2pygame.timer

# -*- coding: latin-1 -*-

"""
simpleguics2pygame module: simpleguics2pygame/timer.

Class Timer.

**Don't require Pygame.**

Piece of SimpleGUICS2Pygame.
https://bitbucket.org/OPiMedia/simpleguics2pygame

:license: GPLv3 --- Copyright (C) 2015, 2020 Olivier Pirson
:author: Olivier Pirson --- http://www.opimedia.be/
:version: May 20, 2020
"""

from __future__ import division
from __future__ import print_function

# print('IMPORT', __name__)


__all__ = ('Timer',
           'create_timer')


import atexit
import threading

try:
    from typing import Any, Callable, Dict, Optional, Union
except ImportError:
    pass

from SimpleGUICS2Pygame.simpleguics2pygame._arguments import _CONFIG  # pylint: disable=no-name-in-module  # noqa


#
# "Private" variable
####################
_STOP_TIMERS = _CONFIG['--stop-timers']
"""
If `True`
then stop all timers when program ending.

If `False`
then timers keep running when program ending.
(This is the default behavior.)
"""


#
# "Private" function
####################
def __timer_exit():  # type: () -> None
    """
    If `_STOP_TIMERS` is True
    then stop all running timers,
    else do nothing.

    Automatically called when program ending.
    """
    if _STOP_TIMERS:
        Timer._stop_all()  # pylint: disable=protected-access


#
# Class
#######
[docs]class Timer: """ Timer similar to SimpleGUI `Timer` of CodeSkulptor. **Don't require Pygame.** """ _timers_running = dict() # type: Dict[int, 'Timer'] """ `Dict` {(Timer id): `Timer`} of all timers are running. """
[docs] @classmethod def _running_nb(cls): # type: () -> int """ Return the number of running timers. :return: int >= 0 """ return len(cls._timers_running)
[docs] @classmethod def _running_some(cls): # type: () -> bool """:return: True if at least one timer running, else False""" return bool(cls._timers_running)
[docs] @classmethod def _stop_all(cls): # type: () -> None """ Stop all timers. **(Not available in SimpleGUI of CodeSkulptor.)** Side effect: Empty `Timer._timers_running`. """ for timer in tuple(cls._timers_running.values()): timer.stop()
[docs] def __init__(self, interval, timer_handler): # type: (Union[int, float], Callable[[], Any]) -> None """ Set a time. **Don't use directly**, use `create_timer()`. :param interval: int or float > 0 :param timer_handler: function () -> * """ assert isinstance(interval, (int, float)), type(interval) assert interval > 0, interval assert callable(timer_handler), type(timer_handler) self._interval = interval self._is_running = False self._timer = None # type: Optional[threading.Timer] def repeat_handler(): """Function to create and start a new timer.""" Timer._timers_running[id(self)] = self self._timer = threading.Timer(self._interval / 1000, self._handler) self._timer.start() timer_handler() self._handler = repeat_handler
[docs] def __repr__(self): # type: () -> str """ Return `'<Timer object>'`. :return: str """ return '<Timer object>'
[docs] def get_interval(self): # type: () -> Union[int, float] """ Return the interval of this timer. (Maybe available in SimpleGUI of CodeSkulptor but *not in CodeSkulptor documentation*!) :return: (int or float) > 0 """ return self._interval
[docs] def is_running(self): # type: () -> bool """ If this timer is running then return `True`, else return `False`. :return: bool """ return self._timer is not None
[docs] def start(self): # type: () -> None """ Start this timer. .. warning:: With SimpleGUICS2Pygame, ``Frame.start()`` is blocking until ``Frame.stop()`` execution or closing window. So timers must be started *before*, and states must be initialized *before*. (Or maybe after by a handler function.) (Side effect: Add `id(self)`: `self` in `Timer._timers_running`.) """ if self._timer is None: Timer._timers_running[id(self)] = self self._timer = threading.Timer(self._interval / 1000, self._handler) self._timer.start()
[docs] def stop(self): # type: () -> None """ Stop this timer. (Side effect: Remove `id(self)` of `Timer. _timers_running`.) """ if self._timer is not None: self._timer.cancel() self._timer = None del Timer._timers_running[id(self)]
# # SimpleGUI function ####################
[docs]def create_timer(interval, timer_handler): # type: (Union[int, float], Callable[[], Any]) -> Timer """ Create and return a timer that will execute the function `timer_handler` every `interval` milliseconds. The first execution of `time_handler` will take place after the first period. (The timer can be started by `Timer.start()`.) :param interval: int or float > 0 :param timer_handler: function () -> * :return: Timer """ assert isinstance(interval, (int, float)), type(interval) assert interval > 0, interval assert callable(timer_handler), type(timer_handler) return Timer(interval, timer_handler)
# # Main ###### atexit.register(__timer_exit)