From e3168b3b12e8eb5f48802f86046715130092547b Mon Sep 17 00:00:00 2001 From: Francesco Gazzetta Date: Sat, 20 Nov 2021 14:27:19 +0100 Subject: [PATCH] Add level app Signed-off-by: Francesco Gazzetta --- README.rst | 4 ++ apps/Level.py | 119 +++++++++++++++++++++++++++++++++++++++++++++ docs/apps.rst | 2 + res/LevelApp.png | Bin 0 -> 4698 bytes res/level_icon.png | Bin 0 -> 392 bytes 5 files changed, 125 insertions(+) create mode 100644 apps/Level.py create mode 100644 res/LevelApp.png create mode 100644 res/level_icon.png diff --git a/README.rst b/README.rst index 6d80324..df68cee 100644 --- a/README.rst +++ b/README.rst @@ -257,3 +257,7 @@ application (and the "blank" white screen is a torch application): .. image:: res/WordClkApp.png :alt: Shows a time as words in the wasp-os simulator :width: 179 + +.. image:: res/LevelApp.png + :alt: Shows a time as words in the wasp-os simulator + :width: 179 diff --git a/apps/Level.py b/apps/Level.py new file mode 100644 index 0000000..11723a5 --- /dev/null +++ b/apps/Level.py @@ -0,0 +1,119 @@ +# SPDX-License-Identifier: LGPL-3.0-or-later +# Copyright (C) 2021 Francesco Gazzetta +"""Level application +~~~~~~~~~~~~~~~~~~~~ + +This app shows a dot that moves depending on the orientation of the watch. +A tap opens a menu with the option to calibrate or reset the level. +To calibrate, place the watch on a flat surface, then tap the "Calibrate" +button while ensuring the watch is stationary. + +.. figure:: res/LevelApp.png + :width: 179 +""" + +import wasp +import watch +import widgets +from micropython import const + +_X_MAX = const(240) +_Y_MAX = const(240) +_X_CENTER = const(120) +_Y_CENTER = const(120) + +class LevelApp(): + NAME = "Level" + # 2-bit RLE, 96x64, generated from res/level_icon.png, 410 bytes + ICON = ( + b'\x02' + b'`@' + b'?\xff\xff\xff\xff\xff\xd0@\xa8L\x80r\x82\xc0\xfd\xf0' + b'\x82L\x13M\x82@\xb0O\xc1\x05\xc1\x04\xc1\x05\xc1O' + b'\x82\x80\xa8\x8d\x11\x8e\xc0r\xc2O@\xfdA\x05A\x04' + b'A\x05A\x80\xb0\x8f\xc2\xc0\xa8\xce\x10\xce@rB\x8f' + b'\x80\xfd\x81\x05\x81\x04\x81\x05\x81\xc0\xb0\xcfB@\xa8N' + b'\x10N\x80r\x82\xcf\xc0\xfd\xc1\x05\xc1\x04\xc1\x05\xc1@' + b'\xb0O\x82\x80\xa8\x8e\x10\x8e\xc0r\xc2O@\xfdA\x05' + b'A\x04A\x05A\x80\xb0\x8f\xc2\xc0\xa8\xce\x10\xce@r' + b'B\x8f\x80\xfd\x81\xc0\xb0\xc1\x04\x81\x04\x81\x04\xc1\x81\xcf' + b'B@\xa8N\x10N\x80r\x82\xcf\xc0\xfd\xc1@\xb0C' + b'\x02\xc1\x04\xc1\x02C\xc1O\x82\x80\xa8\x8e\x10\x8e\xc0r' + b'\xc2O@\xfdA\x80\xb0\x85A\x84A\x85A\x8f\xc2\xc0' + b'\xa8\xce\x10\xce@rB\x8f\x80\xfd\x81\xc0\xb0\xc5\x81\xc4' + b'\x81\xc5\x81\xcfB@\xa8N\x10N\x80r\x82\xcf\xc0\xfd' + b'\xc1@\xb0E\xc1D\xc1E\xc1O\x82\x80\xa8\x8e\x10\x8e' + b'\xc0r\xc2O@\xfdA\x80\xb0\x85A\x84A\x85A\x8f' + b'\xc2\xc0\xa8\xce\x10\xce@rC\x8e\x80\xfd\x81\xc0\xb0\xc5' + b'\x81\xc4\x81\xc5\x81\xceC@\xa8N\x10O\x80r\x82\xce' + b'\xc0\xfd\xc1@\xb0E\xc1D\xc1E\xc1N\x82\x80\xa8\x8f' + b'\x10\x8f\xc0r\xc3M@\xfdA\x80\xb0\x85A\x84A\x85' + b'A\x8d\xc3\xc0\xa8\xcf\x10\xd0@rD\x8b\x80\xfd\x81\xc0' + b'\xb0\xc5\x81\xc4\x81\xc5\x81\xcbD@\xa8P\x10Q\x80r' + b'\xaeQ\x10S\xaaS\x10\x7f\x11\x10\x7f\x11\x10\x7f\x11\x10' + b'\x7f\x11\x10\x7f\x11\x10\x7f\x11\x10\x7f\x11\x10\x7f\x11\x10\x7f' + b'\x11\x10\x7f\x11\x10\x7f\x11\x10\x7f\x11\x11\x7f\x0f\x13\x7f\r' + b'?\xff\xff\xff\xff\xff\xd0' + ) + + def __init__(self): + self.old_xy = (0,0) + self.calibration = (0,0) + self.prompt = False + self.calibrate = widgets.Button(20, 20, 200, 60, 'Calibrate') + self.reset = widgets.Button(20, 90, 200, 60, 'Reset') + self.cancel = widgets.Button(20, 160, 200, 60, 'Cancel') + + def foreground(self): + self.prompt = False # in case the watch went to sleep with prompt on + self._draw() + wasp.system.request_event(wasp.EventMask.TOUCH) + wasp.system.request_tick(125) + + def _draw(self): + wasp.watch.drawable.fill() + self._update() + + def _update(self): + if not self.prompt: + draw = wasp.watch.drawable + draw.fill(None, self.old_xy[0] - 3 + _X_CENTER, self.old_xy[1] - 3 + _Y_CENTER, 6, 6) + # draw guide lines + draw.line(0, _Y_CENTER, _X_MAX, _Y_CENTER, color = wasp.system.theme('mid')) + draw.line(_X_CENTER, 0, _X_CENTER, _Y_MAX, color = wasp.system.theme('mid')) + # We save x as y and -y as x because we use the screen's coordinate + # system. + # We also clamp and scale the values down a bit to make them fit better, + # and apply the calibration + (new_y, new_x, _) = watch.accel.accel_xyz() + new_x = min(_X_CENTER, max(-_X_CENTER, (new_x-self.calibration[0])//-3)) + new_y = min(_Y_CENTER, max(-_Y_CENTER, (new_y-self.calibration[1])//3)) + draw.fill(wasp.system.theme('bright'), new_x - 3 + _X_CENTER, new_y - 3 + _Y_CENTER, 6, 6) + self.old_xy = (new_x, new_y) + + def tick(self, ticks): + self._update() + wasp.system.keep_awake() + + def touch(self, event): + if self.prompt: + # Handle buttons + if self.calibrate.touch(event): + (y, x, _) = watch.accel.accel_xyz() + self.calibration = (x, y) + if self.reset.touch(event): + self.calibration = (0,0) + #if self.cancel.touch(event): + # pass + + # reset the color (buttons set it to blue) and disable prompt + wasp.watch.drawable.set_color(wasp.system.theme('bright')) + self.prompt = False + self._draw() + else: + # Draw menu + self.prompt = True + wasp.watch.drawable.fill() + self.calibrate.draw() + self.reset.draw() + self.cancel.draw() diff --git a/docs/apps.rst b/docs/apps.rst index 0ee4272..6432d3b 100644 --- a/docs/apps.rst +++ b/docs/apps.rst @@ -57,6 +57,8 @@ Applications .. automodule:: apps.haiku +.. automodule:: Level + .. automodule:: Morse .. automodule:: apps.musicplayer diff --git a/res/LevelApp.png b/res/LevelApp.png new file mode 100644 index 0000000000000000000000000000000000000000..b48ed1ec0e20e2fb499b824efbd8a8afbb0b52f2 GIT binary patch literal 4698 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 literal 0 HcmV?d00001 diff --git a/res/level_icon.png b/res/level_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..827e57fb9ab33e4a6f19a8bedfd1589e978097a9 GIT binary patch literal 392 zcmeAS@N?(olHy`uVBq!ia0vp^2|(2Zs>pQoOED3g<^Ai>1RBD9G$ z>W__lW`WN8@HaQ!Y}g&HU*zLv$t>w@Ad%mt`%TRFk+p}y1P290oeNvO%WYk~pL>pv z!vZbwmdF+#olnYTO6q}cOp@xJwNGAq_sfQpch9>k?zRmv^u1CUTV=oE*wsCzR;;G4 zrriD5!ISbSZB^}~Kk*BUL&ZA&J2Ex0fE>V~A^^6a(E%)Q@3QRKoifF8aU2zYp5FP{ ezaQfD1(9VFr9x&YP3V{n67Y2Ob6Mw<&;$Tr{h&tx literal 0 HcmV?d00001