class StopwatchApp(ZeroApp): def __init__(self, i, o): super(StopwatchApp, self).__init__(i, o) self.menu_name = "Stopwatch" self.counter = Chronometer() self.refresher = None self.__instructions = [ "", "UP/ENTER to start/pause", "RIGHT : restart", "DOWN : reset" ] def on_start(self): self.refresher = Refresher( self.refresh_function, self.i, self.o, .1, { "KEY_UP": self.counter.toggle, "KEY_RIGHT": self.counter.start, "KEY_ENTER": self.counter.toggle, "KEY_DOWN": self.counter.stop }) self.refresher.activate() def refresh_function(self): self.counter.update() text_rows = [ "{} {}".format(self.get_char(), round(self.counter.elapsed, 2)).center(self.o.cols) ] text_rows.extend([ instruction.center(self.o.cols) for instruction in self.__instructions ]) return text_rows def get_char(self): return "|>" if self.counter.active else "||"
class Throbber(LoadingIndicator, CenteredTextRenderer): """A throbber is a circular LoadingIndicator, similar to those used on websites or in smartphones. Suitable for graphical displays and looks great on them!""" def __init__(self, i, o, *args, **kwargs): self._current_angle = 0 self._current_range = 0 # range or width of the throbber self.rotation_speed = 360 # degree per second # We use a counter to make the rotation speed independent of the refresh-rate self.counter = Chronometer() self.start_time = 0 self.message = kwargs.pop("message", None) LoadingIndicator.__init__(self, i, o, refresh_interval=0.01, *args, **kwargs) def activate(self): self.start_time = time() self.counter.start() return Refresher.activate(self) @to_be_foreground def refresh(self): self.update_throbber_angle() c = canvas(self.o.device) c.__enter__() draw = c.draw self.draw_throbber(c, draw) if self.message: self.draw_message(draw) self.o.display_image(c.image) def draw_message(self, draw): # type: (ImageDraw) -> None bounds = self.get_centered_text_bounds(draw, self.message, self.o.device.size) draw.text((bounds.left, 0), self.message, fill=True) # Drawn top-centered (with margin) def draw_throbber(self, c, draw): c.__enter__() x, y = c.device.size radius = min(x, y) / 4 draw.arc((x / 2 - radius, y / 2 - radius, x / 2 + 1 + radius, y / 2 + radius), start=(self._current_angle - self._current_range / 2) % 360, end=(self._current_angle + self._current_range / 2) % 360, fill=True) def update_throbber_angle(self): self.counter.update() self._current_angle += self.rotation_speed * self.counter.elapsed time_since_activation = time() - self.start_time self._current_range = cos(time_since_activation * math.pi) / 2 + 0.5 self._current_range = (self._current_range * 170) + 10 self.counter.restart()