def __init__(self, i, o, *args, **kwargs): super(ClockApp, self).__init__(i, o) self.menu_name = "Clock" self.refresher = Refresher(self.on_refresh, i, o) default_config = '{}' config_filename = "config.json" self.config = read_or_create_config(local_path(config_filename), default_config, self.menu_name+" app")
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 ClockApp(ZeroApp, Refresher, CenteredTextRenderer): def __init__(self, i, o, *args, **kwargs): super(ClockApp, self).__init__(i, o) self.menu_name = "Clock" self.refresher = Refresher(self.on_refresh, i, o) default_config = '{}' config_filename = "config.json" self.config = read_or_create_config(local_path(config_filename), default_config, self.menu_name+" app") def draw_analog_clock(self, draw, time, radius="min(*self.o.device.size) / 3", clock_x = "center_x+32", clock_y = "center_y+5", h_len = "radius / 2", m_len = "radius - 5", s_len = "radius - 3", **kwargs): """Draws the analog clock, with parameters configurable through config.txt.""" center_x, center_y = self.get_center(self.o.device.size) clock_x = eval(clock_x) clock_y = eval(clock_y) radius = eval(radius) draw.ellipse((clock_x - radius, clock_y - radius, clock_x + radius, clock_y + radius), fill=False, outline="white") self.draw_needle(draw, 60 - time.second / 60, eval(s_len), clock_x, clock_y, 1) self.draw_needle(draw, 60 - time.minute / 60, eval(m_len), clock_x, clock_y, 1) self.draw_needle(draw, 24 - time.hour / 24, eval(h_len), clock_x, clock_y, 1) def draw_text(self, draw, time, text_x="10", text_y="center_y-5", time_format = "%H:%M:%S", **kwargs): """Draws the digital clock, with parameters configurable through config.txt.""" time_str = time.strftime(time_format) center_x, center_y = self.get_center(self.o.device.size) bounds = self.get_centered_text_bounds(draw, time_str, self.o.device.size) x = eval(text_x) y = eval(text_y) draw.text((x, y), time_str, fill="white") def on_refresh(self): current_time = datetime.now() return self.render_clock(current_time, **self.config) def render_clock(self, time, **kwargs): c = canvas(self.o.device) c.__enter__() width, height = c.device.size draw = c.draw self.draw_text(draw, time, **kwargs) self.draw_analog_clock(draw, time, **kwargs) return c.image def draw_needle(self, draw, progress, radius, x, y, width): # type: (ImageDraw, float, float, float, float, int) -> None hour_angle = math.pi * 2 * progress + math.pi draw.line( ( x, y, x + radius * math.sin(hour_angle), y + radius * math.cos(hour_angle) ), width=width, fill=True ) def on_start(self): super(ClockApp, self).on_start() self.refresher.activate()
def callback(): keymap = { "KEY_ENTER": pomodoro_options_menu, "KEY_KPENTER": pomodoro_options_menu } refresher = Refresher(status_refresher_data, i, o, 1, keymap, "Pomodoro monitor") refresher.activate()
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 status_monitor(): keymap = { "KEY_ENTER": wireless_status, "KEY_RIGHT": lambda: scan(False), "KEY_UP": lambda: reconnect() } refresher = Refresher(status_refresher_data, i, o, 0.5, keymap, "Wireless monitor") refresher.activate()
def callback(): global refresher, keys_called keys_called = [] i.set_streaming(process_key) refresher = Refresher(get_keys, i, o, 1, name="Key monitor") refresher.keymap.pop( "KEY_LEFT") #Removing deactivate callback to show KEY_LEFT PrettyPrinter("To exit this app, press the same key 3 times", i, o) refresher.activate() i.remove_streaming()
def detect_loop(self): """ The detect loop. Will exit on RefresherExitException (raised by ``show_chip_status`` or on KEY_LEFT from the user. """ r = Refresher(self.get_current_status_data, self.i, self.o, name="Avrdude chip detect loop") r.activate()
def __init__(self, *args, **kwargs): ZeroApp.__init__(self, *args, **kwargs) self.active = Event() self.pop_on_event = Event() self.pop_on_event.set() self.c = Canvas(self.o) device_manager.register_monitor_callback(self.process_dm_event) self.i.set_streaming(self.deactivate) self.state = None self.status_image = "No image" self.r = Refresher(self.get_status_image, self.i, self.o, name="Keyboard fallback status refresher")
def init_app(input, output): global callback, i, o i = input o = output #Getting references to output and input device objects and saving them as globals time_refresher = Refresher(show_time, i, o, 1, name="Timer") counter_refresher = Refresher( count, i, o, 1, keymap={"KEY_KPENTER": time_refresher.activate}, name="Counter") callback = counter_refresher.activate
def test_exit_label_leakage(self): """tests whether the keymaps (and keymap entries) of different Refresher leak one into another""" i = get_mock_input() o = get_mock_output() r1 = Refresher(lambda: "Hello", i, o, name=r_name + "1", keymap={"KEY_LEFT":lambda: True}) r2 = Refresher(lambda: "Hello", i, o, name=r_name + "2", keymap={"KEY_LEFT":lambda: False}) r3 = Refresher(lambda: "Hello", i, o, name=r_name + "3") assert (r1.keymap != r2.keymap) assert (r1.keymap["KEY_LEFT"] != r2.keymap["KEY_LEFT"]) assert (r2.keymap != r3.keymap) assert (r2.keymap["KEY_LEFT"] != r3.keymap["KEY_LEFT"]) assert (r1.keymap != r3.keymap) assert (r1.keymap["KEY_LEFT"] != r3.keymap["KEY_LEFT"])
class LectureHelper(object): refresher = None position = 0 started_at = None def __init__(self, file, interval): self.filename = file with open(self.filename, 'r') as f: self.contents = [ line.rstrip('\n') for line in f.readlines() if line.rstrip('\n') ] self.contents.append("STOP") self.interval = interval #In minutes def move_left(self): if self.position == 0: self.refresher.deactivate() return self.position -= 1 def move_right(self): if self.position == len(self.contents) - 1: return self.position += 1 def get_keymap(self): keymap = {"KEY_LEFT": self.move_left, "KEY_RIGHT": self.move_right} return keymap def get_displayed_data(self): data = [] data_rows = o.rows - 1 current_data = self.contents[self.position] for i in range(data_rows): data.append(current_data[o.cols * i:][:o.cols * (i + 1)]) total_seconds_since_start = (datetime.now() - self.started).total_seconds() total_seconds_till_end = self.interval * 60 - total_seconds_since_start minutes_till_end, seconds_till_end = map( int, (total_seconds_till_end / 60, total_seconds_till_end % 60)) time_str = "{}:{}".format(minutes_till_end, seconds_till_end).center(o.cols) data.append(time_str) return data def start(self): self.started = datetime.now() self.refresher = Refresher(self.get_displayed_data, i, o, 1, self.get_keymap()) self.refresher.activate()
def test_set_interval(self): """ Tests whether the refresh_interval of Refresher is set correctly when using set_refresh_interval. """ i = get_mock_input() o = get_mock_output() r = Refresher(lambda: "Hello", i, o, name=r_name, refresh_interval=1) assert(r.refresh_interval == 1) assert(r.sleep_time == 0.1) assert(r.iterations_before_refresh == 10) # Refresh intervals up until 0.1 don't change the sleep time r.set_refresh_interval(0.1) assert(r.refresh_interval == 0.1) assert(r.sleep_time == 0.1) assert(r.iterations_before_refresh == 1) # Refresh intervals less than 0.1 change sleep_time to match refresh interval r.set_refresh_interval(0.01) assert(r.refresh_interval == 0.01) assert(r.sleep_time == 0.01) assert(r.iterations_before_refresh == 1) # Now setting refresh_interval to a high value r.set_refresh_interval(10) assert(r.refresh_interval == 10) assert(r.sleep_time == 0.1) # Back to normal assert(r.iterations_before_refresh == 100)
def __init__(self, i, o, *args, **kwargs): super(ClockApp, self).__init__(i, o) self.menu_name = "Clock" self.countdown = None self.refresher = Refresher( self.on_refresh, i, o, keymap={"KEY_RIGHT": self.countdown_settings}) default_config = '{}' config_filename = "config.json" self.config = read_or_create_config(local_path(config_filename), default_config, self.menu_name + " app")
def test_refresher_exit_exception(self): """ Tests whether the Refresher deactivates when it receives an exit exception. """ i = get_mock_input() o = get_mock_output() def get_data(): raise RefresherExitException r = Refresher(get_data, i, o, name=r_name, refresh_interval=0.1) #Doing what an activate() would do, but without a loop r.to_foreground() #Should've caught the exception and exited, since to_foreground() calls refresh() assert r.in_foreground == False
def init_app(input, output): global refresher, callback, i, o i = input o = output i.set_streaming(process_key) refresher = Refresher(get_keys, i, o, 1, name="Key monitor") callback = refresher.activate
def test_constructor(self): """Tests constructor""" r = Refresher(lambda: "Hello", get_mock_input(), get_mock_output(), name=r_name) self.assertIsNotNone(r)
def test_string_keymap(self): """Tests the keymap entries""" k = {"KEY_LEFT":'refresh', "KEY_RIGHT":'deactivate'} r = Refresher(lambda: "Hello", get_mock_input(), get_mock_output(), keymap=k, name=r_name) for key_name, callback in r.keymap.iteritems(): self.assertIsNotNone(callback) assert (r.keymap["KEY_LEFT"] == r.refresh) assert (r.keymap["KEY_RIGHT"] == r.deactivate)
def test_keymap(self): """Tests the keymap entries""" r = Refresher(lambda: "Hello", get_mock_input(), get_mock_output(), name=r_name) self.assertIsNotNone(r.keymap) for key_name, callback in r.keymap.iteritems(): self.assertIsNotNone(callback)
def test_shows_data_on_screen(self): """Tests whether the Refresher outputs data on screen when it's ran""" i = get_mock_input() o = get_mock_output() r = Refresher(lambda: "Hello", i, o, name=r_name) def scenario(): r.refresh() r.deactivate() with patch.object(r, 'idle_loop', side_effect=scenario) as p: r.activate() #The scenario should only be called once assert r.idle_loop.called assert r.idle_loop.call_count == 1 assert o.display_data.called assert o.display_data.call_count == 2 #One in to_foreground, and one in patched idle_loop assert o.display_data.call_args_list[0][0] == ("Hello", ) assert o.display_data.call_args_list[1][0] == ("Hello", )
def test_left_key_exits(self): r = Refresher(lambda: "Hello", get_mock_input(), get_mock_output(), name=r_name) r.refresh = lambda *args, **kwargs: None # This test doesn't actually test whether the Refresher exits # It only tests whether the in_foreground attribute is set # Any ideas? Maybe use some kind of "timeout" library? def scenario(): r.keymap["KEY_LEFT"]() assert not r.in_foreground # If the test fails, either the assert will trigger a test failure, # or the idle loop will just run indefinitely # The exception thrown should protect from the latter raise KeyboardInterrupt with patch.object(r, 'idle_loop', side_effect=scenario) as p: try: r.activate() except KeyboardInterrupt: pass #Test succeeded
def show_pinouts(i, o): zp_pinout = zp_pinouts_page(o).get_image() ua_pinout = uap_pinouts_page(o).get_image() isp_pinout = isp_pinouts_page(o).get_image() pk2_pinout = pk2_pinouts_page(o).get_image() show_pinout = lambda x: Refresher( lambda: x, i, o, name="Pinout display refresher").activate() contents = [["ZeroPhone side header", lambda: show_pinout(zp_pinout)], ["USB ASP", lambda: show_pinout(ua_pinout)], ["AVR ISP", lambda: show_pinout(isp_pinout)], ["PICkit2", lambda: show_pinout(pk2_pinout)]] Menu(contents, i, o, name="Pinout gallery page").activate()
def test_keymap_restore_on_resume(self): """Tests whether the Refresher re-sets the keymap upon resume.""" i = get_mock_input() o = get_mock_output() r = Refresher(lambda: "Hello", i, o, name=r_name, refresh_interval=0.1) r.refresh = lambda *args, **kwargs: None r.to_foreground() assert i.set_keymap.called assert i.set_keymap.call_count == 1 assert i.set_keymap.call_args[0][0] == r.keymap r.pause() assert i.set_keymap.call_count == 1 #paused, so count shouldn't change i.set_keymap(None) assert i.set_keymap.call_args[0][0] != r.keymap r.resume() assert i.set_keymap.call_count == 3 #one explicitly done in the test right beforehand assert i.set_keymap.call_args[0][0] == r.keymap
def learn_about_5_buttons(self): c = Canvas(self.o) c.centered_text( "Let's go through\nthe main buttons\nand their meanings") GraphicsPrinter(c.get_image(), self.i, self.o, 5, invert=False) c.clear() c.centered_text("Press the buttons\nto test\nThen ENTER\nto continue") GraphicsPrinter(c.get_image(), self.i, self.o, 5, invert=False) c.clear() # First, show the left/right/up/down buttons # TODO: different behaviour for ZP and emulator? c.text("Enter", (48, 22)) c.text("Continue", (39, 30)) c.text("Left", (2, 18)) c.text("Back", (2, 26)) c.text("Cancel", (2, 34)) c.text("Right", (92, 22)) c.text("Option", (90, 30)) c.text("Up", (56, 5)) c.text("Down", (52, "-18")) image = c.get_image() def process_key(key, state): # invert/deinvert areas on the canvas when buttons are pressed/released # on drivers that don't support key states, will toggle inversion on every press # on drivers that support key states, will "highlight" the buttons pressed print(key, state) if state != KEY_HELD: if key == "KEY_UP": c.invert_rect((64 - 20, 2, 64 + 20, 22)) elif key == "KEY_DOWN": c.invert_rect((64 - 20, "-2", 64 + 20, "-22")) elif key == "KEY_LEFT": c.invert_rect((2, 32 - 15, 38, 32 + 15)) elif key == "KEY_RIGHT": c.invert_rect(("-2", 32 - 10, "-40", 32 + 10)) keys = ["KEY_UP", "KEY_DOWN", "KEY_LEFT", "KEY_RIGHT"] keymap = {"KEY_ENTER": "deactivate"} for key in keys: cb = cb_needs_key_state(lambda st, x=key: process_key(x, st)) keymap[key] = cb Refresher(c.get_image, self.i, self.o, override_left=False, keymap=keymap).activate() return True
def test_update_keymap(self): """Tests whether the Refresher updates the keymap correctly.""" i = get_mock_input() o = get_mock_output() r = Refresher(lambda: "Hello", i, o, name=r_name, refresh_interval=0.1) r.refresh = lambda *args, **kwargs: None # We need to patch "process_callback" because otherwise the keymap callbacks # are wrapped and we can't test equivalence with patch.object(r, 'process_callback', side_effect=lambda keymap:keymap) as p: keymap1 = {"KEY_LEFT": lambda:1} r.update_keymap(keymap1) assert(r.keymap == keymap1) keymap2 = {"KEY_RIGHT": lambda:2} r.update_keymap(keymap2) keymap2.update(keymap1) assert(r.keymap == keymap2)
def learn_about_help_icon(self): o = HelpOverlay("test") c = Canvas(self.o) c.text("See this icon?->", (1, 1)) c.text("When it appears,", (3, 10)) c.text("press F5 to get help", (3, 19)) c.text("Try now, or", (3, 28)) c.text("press ENTER to skip", (3, 37)) if not is_emulator(): c.text("F5:", (5, 51)) c.paste(local_path("f5_button_location.png"), (30, 50), invert=True) o.draw_icon(c) Refresher(c.get_image, self.i, self.o, keymap={ "KEY_ENTER": "deactivate", "KEY_F5": self.on_help_button_press }).activate() return True
def learn_about_zeromenu(self): c = Canvas(self.o) if is_emulator(): c.text("Press F11 to get", (1, 1)) else: c.text("Press PROG2 to get", (1, 1)) c.text("a shortcut menu -", (3, 10)) c.text("ZeroMenu. You can", (3, 19)) c.text("add apps to it", (3, 28)) c.text("for quick launch", (3, 37)) if not is_emulator(): c.text("PROG2:", (5, 51)) c.paste(local_path("prog2_button_location.png"), (40, 50), invert=True) Refresher(c.get_image, self.i, self.o, keymap={ "KEY_ENTER": "deactivate", "KEY_PROG2": self.on_zeromenu_button_press }).activate() return True
def i2c_read_ui(address, reg=None): global last_values if reg == True: reg = UniversalInput(i, o, message="Register:", charmap="hex").activate() if reg is None: # User picked "cancel" return if isinstance(reg, basestring): reg = int(reg, 16) last_values = [] values_on_screen = o.cols def read_value( ): # A helper function to read a value and format it into a list global last_values try: if reg: answer = "{} {}".format( hex(reg), hex(current_bus.read_byte_data(address, reg))) else: answer = hex(current_bus.read_byte(address)) except IOError: answer = "{} error".format(reg) if reg else "error" last_values.append(answer) last_values = last_values[:values_on_screen] return list(reversed(last_values)) r = Refresher(read_value, i, o) def change_interval( ): # A helper function to adjust the Refresher's refresh interval while it's running new_interval = IntegerAdjustInput( r.refresh_interval, i, o, message="Refresh interval:").activate() if new_interval is not None: r.set_refresh_interval(new_interval) r.update_keymap({"KEY_RIGHT": change_interval}) r.activate()
def callback(): Refresher(show_temp, i, o, 0.1, name="Temperature monitor").activate()
def start(self): self.started = datetime.now() self.refresher = Refresher(self.get_displayed_data, i, o, 1, self.get_keymap()) self.refresher.activate()
def callback(): keymap = {"KEY_ENTER":pomodoro_options_menu, "KEY_KPENTER":pomodoro_options_menu} refresher = Refresher(status_refresher_data, i, o, 1, keymap, "Pomodoro monitor") refresher.activate()
def status_monitor(): keymap = {"KEY_ENTER":wireless_status, "KEY_KPENTER":wireless_status} refresher = Refresher(status_refresher_data, i, o, 0.5, keymap, "Wireless monitor") refresher.activate()