1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
|
import time
from typing import Any, Optional
from moderngl_window.timers.base import BaseTimer
class Timer(BaseTimer):
"""Timer based on python ``time``."""
def __init__(self, **kwargs: Any) -> None:
self._start_time: Optional[float] = None
self._stop_time: Optional[float] = None
self._pause_time: Optional[float] = None
self._last_frame = 0.0
self._offset = 0.0
self._frames = 0 # similar to ticks
self._fps = 0.0
@property
def is_paused(self) -> bool:
"""bool: The pause state of the timer"""
return self._pause_time is not None
@property
def is_running(self) -> bool:
"""bool: Is the timer currently running?"""
return self._pause_time is None
@property
def time(self) -> float:
"""Get or set the current time.
This can be used to jump around in the timeline.
Returns:
The current time in seconds
"""
if self._start_time is None:
return 0.0
if self.is_paused and self._pause_time is not None:
return self._pause_time - self._offset - self._start_time
return time.time() - self._start_time - self._offset
@time.setter
def time(self, value: float) -> None:
if value < 0:
value = 0.0
self._offset += self.time - value
@property
def fps_average(self) -> float:
"""The average fps since the timer was started"""
if self._frames == 0:
return 0.0
return self._frames / self.time
@property
def fps(self) -> float:
"""Get the current frames per second."""
return self._fps
def next_frame(self) -> tuple[float, float]:
"""
Get the time and frametime for the next frame.
This should only be called once per frame.
Returns:
tuple[float, float]: current time and frametime
"""
self._frames += 1
current = self.time
delta, self._last_frame = current - self._last_frame, current
# Avoid division by zero on first frame
if delta > 0:
self._fps = 1.0 / delta
else:
self._fps = 0.0
return current, delta
def start(self) -> None:
"""Start the timer by recoding the current ``time.time()``
preparing to report the number of seconds since this timestamp.
"""
if self._start_time is None:
self._start_time = time.time()
self._last_frame = 0.0
elif self._pause_time is not None:
self._offset += time.time() - self._pause_time
self._pause_time = None
else:
print("The timer is already started")
def pause(self) -> None:
"""Pause the timer by setting the internal pause time using ``time.time()``"""
self._pause_time = time.time()
def toggle_pause(self) -> None:
"""Toggle the paused state"""
if self.is_paused:
self.start()
else:
self.pause()
def stop(self) -> tuple[float, float]:
"""
Stop the timer. Should only be called once when stopping the timer.
Returns:
tuple[float, float]: Current position in the timer, actual running duration
"""
if self._start_time is None:
return 0.0, 0.0
self._stop_time = time.time()
return (
self._stop_time - self._start_time - self._offset,
self._stop_time - self._start_time,
)
|