forked from Marken-Foo/tsumemi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
timer.py
144 lines (118 loc) · 4.12 KB
/
timer.py
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import time
from itertools import accumulate
from math import fsum
from event import Emitter, TimerStartEvent, TimerStopEvent, TimerSplitEvent
def sec_to_hms(seconds):
# Take time in seconds, return tuple of (hours, minutes, seconds).
return (int(seconds // 3600), int((seconds % 3600) // 60), seconds % 60)
def _two_digits(num):
# Take num, make integer part two chars (clock display), return string
return "0" + str(num) if num < 10 else str(num)
def sec_to_str(seconds, places=1):
hms = list(sec_to_hms(seconds))
hms[2] = round(hms[2], places)
return ":".join([_two_digits(i) for i in hms])
class SplitTimer:
'''
Split timer class. Works like a speedrunning split timer.
Stores "lap times", and split times must be calculated from these.
Split time = time elapsed since beginning, lap time = time between splits.
e.g. If I take splits at 0:30, 1:00, 1:30 and 2:00,
I have 4 splits (those four), and 4 lap times (each lap is 30 seconds).
The logic for this class is vaguely "state-based", if that helps.
'''
def __init__(self):
self.is_running = False
self.lap_times = []
# Elapsed (active) time since the start of this lap
self.curr_lap_time = 0
# Time at instant of last start() or split()
self.start_time = None
return
def start(self):
if not self.is_running:
self.is_running = True
self.start_time = time.perf_counter()
return
def stop(self):
if self.is_running:
self.is_running = False
if self.start_time is not None:
self.curr_lap_time += time.perf_counter() - self.start_time
return
def split(self):
if self.start_time is None:
# ill-defined operation
return None
if self.is_running:
lap_time = (self.curr_lap_time + time.perf_counter()
- self.start_time)
self.lap_times.append(lap_time)
self.start_time = time.perf_counter()
self.curr_lap_time = 0
return lap_time
else:
# Taking a split while the timer is paused "has no meaning".
# But we implement it anyway.
lap_time = self.curr_lap_time
self.lap_times.append(lap_time)
self.start_time = None
self.curr_lap_time = 0
return lap_time
def reset(self):
self.is_running = False
self.lap_times = []
self.curr_lap_time = 0
self.start_time = None
return
def read(self):
if self.is_running:
res = (fsum(self.lap_times) + self.curr_lap_time
+ time.perf_counter() - self.start_time)
else:
res = fsum(self.lap_times) + self.curr_lap_time
return res
def get_lap(self):
if self.lap_times:
return self.lap_times[-1]
else:
return None
def get_split_times(self):
# Return a list of split times instead of lap times.
return accumulate(self.lap_times)
class Timer(Emitter):
def __init__(self):
self.clock = SplitTimer()
self.observers = []
return
def read(self):
return self.clock.read()
def reset(self):
self.clock.reset()
self._notify_observers(TimerStopEvent())
return
def split(self):
time = self.clock.split()
if time is not None:
self._notify_observers(TimerSplitEvent(time))
return time # can be None
def start(self):
self.clock.start()
self._notify_observers(TimerStartEvent())
return
def stop(self):
self.clock.stop()
self._notify_observers(TimerStopEvent())
return
def toggle(self):
if self.clock.is_running: # remove this private access
self.stop()
else:
self.start()
return
class CmdReadTimer:
# Command pattern
def __init__(self, timer):
self.timer = timer
def execute(self, invoker):
return self.timer.read()