Исходный код EJIO.utils.component.clock.timer
## /utils/component/clock/timer.py
from .time_class import Time
from .err import ClockError
from typing import Callable
from datetime import datetime
from threading import Lock, Thread
from time import sleep
__all__ = [
'Timer',
]
[документация]
class Timer:
"""
Таймер, отслеживающий заданную по времени продолжительность
и выполняющий указанную функцию по окончании отчета.
"""
__slots__ = ('duration', '_func', '_elapsed_time', '_running', '_lock', '_thread')
def __init__(self, duration: Time, func: Callable) -> None:
"""
Создает таймер с указанной продолжительностьюи и функцией для выполнения по истечении таймера.
:param duration: Продолжительность таймера типа Time.
:param func: Функция, которую нужно выполнить по завершении таймера.
:raises TypeError: Если продолжительность не является объектом Time.
:raises ValueError: Если функция не callable.
"""
if not isinstance(duration, Time):
raise TypeError("duration must be of type Time")
if not callable(func):
raise ValueError("func must be of type Callable")
self.duration: Time = duration
self._func: Callable = func
self._elapsed_time: int = 0
self._running: bool = False
self._lock = Lock()
self._thread: Thread | None = None
def _run(self) -> None:
"""Внутренняя функция, которая выполняет отсчет времени."""
start_time = datetime.now()
while self._running and self._elapsed_time < self.duration.as_nanoseconds():
sleep(0.01)
with self._lock:
self._elapsed_time = int((datetime.now() - start_time).total_seconds() * int(1e9))
with self._lock:
if self._elapsed_time >= self.duration.as_nanoseconds():
self._func()
self._running = False
[документация]
def start(self) -> 'Timer':
"""
Запускает таймер.
:return: Объект таймера.
"""
with self._lock:
if self._running:
raise ClockError("Timer already started")
self._running = True
self._thread = Thread(target=self._run, daemon=True)
self._thread.start()
return self
[документация]
def stop(self) -> 'Timer':
"""
Останавливает таймер.
:return: Объект таймера.
"""
with self._lock:
if not self._running:
raise ClockError("Timer is not started")
self._running = False
self._thread = None
return self
[документация]
def prolong(self, additional_time: Time) -> 'Timer':
"""
Увеличивает длительность таймера.
:param additional_time: Время, на которое нужно увеличить таймер.
:return: Объект таймера.
:raises TypeError: Если additional_time не является объектом Time.
"""
if not isinstance(additional_time, Time):
raise TypeError("additional_time must be of type Time")
with self._lock:
self.duration += additional_time
return self
@property
def remaining_time(self) -> Time:
"""
Возвращает оставшееся время.
:return: Оставшееся время в формате Time.
"""
with self._lock:
remaining_ns = max(self.duration.as_nanoseconds() - self._elapsed_time, 0)
return Time(nanoseconds=remaining_ns)
@property
def completed(self) -> bool:
"""
Проверяет, завершился ли таймер.
:return: True, если таймер завершен, иначе False.
"""
with self._lock:
return self._elapsed_time >= self.duration.as_nanoseconds()