From 5ea9e6bc18d3177fec3dafde1810ba374c9be4e5 Mon Sep 17 00:00:00 2001 From: Naomi Pentrel <5212232+npentrel@users.noreply.github.com> Date: Sun, 16 Jun 2024 18:32:40 +0100 Subject: [PATCH] Updates --- docs/hexpansions/.flake8 | 4 - docs/hexpansions/eeprom.md | 12 +- docs/hexpansions/writing-hexpansion-apps.md | 27 +-- docs/tildagon-apps/development.md | 40 ++-- docs/tildagon-apps/examples/api.md | 12 +- .../examples/detect-hexpansion.md | 30 ++- docs/tildagon-apps/examples/snake.md | 77 ++++--- .../tildagon-apps/reference/badge-hardware.md | 135 +++++++----- docs/tildagon-apps/reference/ctx.md | 144 +++++++------ docs/tildagon-apps/reference/eventbus.md | 15 +- docs/tildagon-apps/reference/ui-elements.md | 203 ++++++++++-------- docs/tildagon-apps/simple_tildagon.md | 7 +- docs/tildagon-apps/simulate.md | 4 +- 13 files changed, 407 insertions(+), 303 deletions(-) delete mode 100644 docs/hexpansions/.flake8 diff --git a/docs/hexpansions/.flake8 b/docs/hexpansions/.flake8 deleted file mode 100644 index 94e74f5..0000000 --- a/docs/hexpansions/.flake8 +++ /dev/null @@ -1,4 +0,0 @@ -[flake8] -extend-ignore = F704, F821, F401, F841 -exclude = .git,build,dist -max-complexity = 20 \ No newline at end of file diff --git a/docs/hexpansions/eeprom.md b/docs/hexpansions/eeprom.md index 58890a0..f8c8bd5 100644 --- a/docs/hexpansions/eeprom.md +++ b/docs/hexpansions/eeprom.md @@ -110,17 +110,17 @@ The header is 32 bytes long and contains the following values: An example implementation of the checksum algorithm in Python: ```python - -def calc_checksum(header): #header assumed to be of type bytes - value=0x55 +def calc_checksum(header): # header assumed to be of type bytes + value = 0x55 for b in header[1:]: - value= value ^ b + value = value ^ b return value -header_w_checksum = header+bytes([calc_checksum(header)]) #to generate a checksum -calc_checksum(header_w_checksum) # should return 0 if checksum is correct +# to generate a checksum +header_w_checksum = header+bytes([calc_checksum(header)]) +calc_checksum(header_w_checksum) # should return 0 if checksum is correct ``` The header format uses little-endian byte ordering for the individual values, e.g. 0x0123 = 0x01 as least significant byte and 0x23 as most significant byte, resulting in a value of 8961. diff --git a/docs/hexpansions/writing-hexpansion-apps.md b/docs/hexpansions/writing-hexpansion-apps.md index ba5b2c7..5857f68 100644 --- a/docs/hexpansions/writing-hexpansion-apps.md +++ b/docs/hexpansions/writing-hexpansion-apps.md @@ -55,7 +55,7 @@ Below is an example of how you find which port your hexpansion is plugged in to def draw(self, ctx): ctx.save() clear_background(ctx) - ctx.rgb(0,1,0).move_to(-90,-40).text("Hello from your\nhexpansion!") + ctx.rgb(0, 1, 0).move_to(-90, -40).text("Hello from your\nhexpansion!") ctx.restore() return None @@ -109,7 +109,7 @@ Below is an example of how you find which port your hexpansion is plugged in to def draw(self, ctx): ctx.save() clear_background(ctx) - ctx.rgb(0,1,0).move_to(-90,-40).text(self.text) + ctx.rgb(0, 1, 0).move_to(-90, -40).text(self.text) ctx.restore() def scan_for_hexpansion(self): @@ -132,7 +132,7 @@ Below is an example of how you find which port your hexpansion is plugged in to # You can add some logic here to check the PID and VID match your hexpansion return HexpansionConfig(port) - self.color = (1,0,0) + self.color = (1, 0, 0) self.text = "No hexpansion found." return None @@ -181,12 +181,12 @@ Below is an example of how you find which port your hexpansion is plugged in to # This might look weird, but we're just drawing a shape as a port indicator. ctx.save() ctx.font_size = 22 - ctx.rgb(*colors["dark_green"]).rectangle(-120,-120, 240, 100).fill() + ctx.rgb(*colors["dark_green"]).rectangle(-120, -120, 240, 100).fill() ctx.rgb(*colors["dark_green"]).rectangle(-120, 20, 240, 100).fill() rotation_angle = self.menu.position*pi/3 - ctx.rgb(*colors["mid_green"]).rotate(rotation_angle).rectangle(80,-120,40,240).fill() + ctx.rgb(*colors["mid_green"]).rotate(rotation_angle).rectangle(80, -120, 40, 240).fill() prompt_message = "Select hexpansion port:" - ctx.rgb(1,1,1).rotate(-rotation_angle).move_to(0,-45).text(prompt_message) + ctx.rgb(1, 1, 1).rotate(-rotation_angle).move_to(0, -45).text(prompt_message) ctx.restore() else: @@ -194,7 +194,7 @@ Below is an example of how you find which port your hexpansion is plugged in to ctx.font_size = 24 msg = "Hexpansion in port " + str(self.hexpansion_config.port) msg_width = ctx.text_width(msg) - ctx.rgb(1,1,1).move_to(-msg_width/2,0).text(msg) + ctx.rgb(1, 1, 1).move_to(-msg_width/2, 0).text(msg) ctx.restore() __app_export__ = ExampleApp @@ -206,12 +206,13 @@ In all of these examples, the `HexpansionConfig` object is used to provide infor The `HexpansionConfig` object that you get after following the examples is where the magic all happens. It allows you to access the following: -| Object | Description | Example Usage | -| --------------------------- | -------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | -| `HexpansionConfig.port` | The port number your hexpansion is connected to. | | -| `HexpansionConfig.pin[]` | A list of 4 `Pin` objects. These are the high-speed, direct GPIO pins for this hexpansion port. | [See MicroPython Docs](https://docs.micropython.org/en/latest/library/machine.Pin.html) | -| `HexpansionConfig.ls_pin[]` | A list of 5 `ePin` objects for this hexpansion port. These are the emulated, low-speed GPIO pins for this hexpansion port. | [See eGPIO](../tildagon-apps/reference/badge-hardware.md#egpio) | -| `HexpansionConfig.i2c` | The dedicated `I2C` object for this hexpansion port. | [See I2C](../tildagon-apps/reference/badge-hardware.md#i2c) | + +| Object | Description | Example Usage | +| ------ | ----------- | ------------- | +| `HexpansionConfig.port` | The port number your hexpansion is connected to. | | +| `HexpansionConfig.pin[]` | A list of 4 `Pin` objects. These are the high-speed, direct GPIO pins for this hexpansion port. | [See MicroPython Docs](https://docs.micropython.org/en/latest/library/machine.Pin.html) | +| `HexpansionConfig.ls_pin[]` | A list of 5 `ePin` objects for this hexpansion port. These are the emulated, low-speed GPIO pins for this hexpansion port. | [See eGPIO](../tildagon-apps/reference/badge-hardware.md#egpio) | +| `HexpansionConfig.i2c` | The dedicated `I2C` object for this hexpansion port. | [See I2C](../tildagon-apps/reference/badge-hardware.md#i2c) | ### Pin vs ePin diff --git a/docs/tildagon-apps/development.md b/docs/tildagon-apps/development.md index 4d6faee..d874273 100644 --- a/docs/tildagon-apps/development.md +++ b/docs/tildagon-apps/development.md @@ -22,17 +22,18 @@ class ExampleApp(app.App): def update(self, delta): if self.button_states.get(BUTTON_TYPES["CANCEL"]): # The button_states do not update while you are in the background. - # Calling clear() ensures the next time you open the app, it stays open. - # Without it the app would close again immediately. + # Calling clear() ensures the next time you open the app, it stays + # open. Without it the app would close again immediately. self.button_states.clear() self.minimise() def draw(self, ctx): ctx.save() - ctx.rgb(0.2,0,0).rectangle(-120,-120,240,240).fill() - ctx.rgb(1,0,0).move_to(-80,0).text("Hello world") + ctx.rgb(0.2, 0, 0).rectangle(-120, -120, 240, 240).fill() + ctx.rgb(1, 0, 0).move_to(-80, 0).text("Hello world") ctx.restore() + __app_export__ = ExampleApp ``` @@ -171,8 +172,8 @@ Afterwards, you'll learn how to [update state while an app is minimized](#update self.counter = self.counter + 1 def draw(self, ctx): - ctx.rgb(0,0.2,0).rectangle(-120,-120,240,240).fill() - ctx.rgb(0,1,0).move_to(-80,0).text(str(self.counter)) + ctx.rgb(0, 0.2, 0).rectangle(-120, -120, 240, 240).fill() + ctx.rgb(0, 1, 0).move_to(-80, 0).text(str(self.counter)) __app_export__ = RightButtonCounterApp ``` @@ -195,8 +196,8 @@ Afterwards, you'll learn how to [update state while an app is minimized](#update self.counter = self.counter + 1 def draw(self, ctx): - ctx.rgb(0,0.2,0).rectangle(-120,-120,240,240).fill() - ctx.rgb(0,1,0).move_to(-80,0).text(str(self.counter)) + ctx.rgb(0, 0.2, 0).rectangle(-120, -120, 240, 240).fill() + ctx.rgb(0, 1, 0).move_to(-80, 0).text(str(self.counter)) __app_export__ = RightButtonCounterApp ``` @@ -225,8 +226,8 @@ Afterwards, you'll learn how to [update state while an app is minimized](#update self.counter = self.counter + 1 def draw(self, ctx): - ctx.rgb(0,0.2,0).rectangle(-120,-120,240,240).fill() - ctx.rgb(0,1,0).move_to(-80,0).text(str(self.counter)) + ctx.rgb(0, 0.2, 0).rectangle(-120, -120, 240, 240).fill() + ctx.rgb(0, 1, 0).move_to(-80, 0).text(str(self.counter)) __app_export__ = RightButtonCounterApp ``` @@ -244,6 +245,7 @@ import app from events.input import Buttons, BUTTON_TYPES + class TimeCounterApp(app.App): def __init__(self): self.button_states = Buttons(self) @@ -254,13 +256,14 @@ class TimeCounterApp(app.App): self.button_states.clear() self.minimise() - def background_update(): + def background_update(self): # if self.button_states.get(BUTTON_TYPES["RIGHT"]): self.counter = self.counter + 1 def draw(self, ctx): - ctx.rgb(0,0.2,0).rectangle(-120,-120,240,240).fill() - ctx.rgb(0,1,0).move_to(-80,0).text(str(self.counter)) + ctx.rgb(0, 0.2, 0).rectangle(-120, -120, 240, 240).fill() + ctx.rgb(0, 1, 0).move_to(-80, 0).text(str(self.counter)) + __app_export__ = TimeCounterApp ``` @@ -280,6 +283,7 @@ import random from app_components import clear_background from events.input import Buttons, BUTTON_TYPES + class Rectangle(object): def __init__(self): self.r = random.random() @@ -287,7 +291,7 @@ class Rectangle(object): self.b = random.random() def draw(self, ctx): - ctx.rgb(self.r, self.g, self.b).rectangle(-60,-60,120,120).fill() + ctx.rgb(self.r, self.g, self.b).rectangle(-60, -60, 120, 120).fill() class OverlaysApp(app.App): @@ -315,6 +319,7 @@ class OverlaysApp(app.App): clear_background(ctx) self.draw_overlays(ctx) + __app_export__ = OverlaysApp ``` @@ -354,9 +359,11 @@ class BasicApp(app.App): # Create a yes/no dialogue, add it to the overlays dialog = YesNoDialog("Change the colour?", self) self.overlays = [dialog] - # Wait for an answer from the dialogue, and if it was yes, randomise colour + # Wait for an answer from the dialogue, and if it was yes, + # randomise colour if await dialog.run(render_update): - self.color = (random.random(), random.random(), random.random()) + self.color = ( + random.random(), random.random(), random.random()) # Remove the dialogue and re-render self.overlays = [] @@ -371,6 +378,7 @@ class BasicApp(app.App): self.draw_overlays(ctx) + __app_export__ = BasicApp ``` diff --git a/docs/tildagon-apps/examples/api.md b/docs/tildagon-apps/examples/api.md index cb9d7a8..d9a149a 100644 --- a/docs/tildagon-apps/examples/api.md +++ b/docs/tildagon-apps/examples/api.md @@ -35,7 +35,8 @@ class FilmScheduleApp(app.App): # When we load, grab all the API data in JSON format # Requests will automatically convert this to a python dict # for us, it really is that good! - self.schedule = requests.get("https://emffilms.org/schedule.json").json() + self.schedule = requests.get( + "https://emffilms.org/schedule.json").json() self.button_states = Buttons(self) # Setup lists to hold our film titles and timings main_menu_items = [] @@ -56,7 +57,8 @@ class FilmScheduleApp(app.App): self.notification = None def select_handler(self, item, position): - self.notification = Notification('Showing at ' + self.timings[position] + '!') + self.notification = Notification( + 'Showing at ' + self.timings[position] + '!') def back_handler(self): self.button_states.clear() @@ -67,7 +69,6 @@ class FilmScheduleApp(app.App): if self.notification: self.notification.update(delta) - def draw(self, ctx): clear_background(ctx) # Display the menu on the device @@ -76,6 +77,7 @@ class FilmScheduleApp(app.App): if self.notification: self.notification.draw(ctx) + __app_export__ = FilmScheduleApp ``` @@ -136,6 +138,7 @@ def FetchWeather(): print("Error fetching weather data") raise Exception("Error fetching weather data") + class WeatherType: id: int main: str @@ -172,6 +175,7 @@ class WeatherInfo: def human_readable(self): return f"{self.weather.main}, {round(self.temp, 1)}°C" + class WeatherApp(app.App): text: str connected: bool @@ -217,7 +221,7 @@ class WeatherApp(app.App): try: weather = FetchWeather() self.text = weather.human_readable() - except: + except Exception: self.text = "API failure.\nTry again." diff --git a/docs/tildagon-apps/examples/detect-hexpansion.md b/docs/tildagon-apps/examples/detect-hexpansion.md index 35b3afb..9ea269a 100644 --- a/docs/tildagon-apps/examples/detect-hexpansion.md +++ b/docs/tildagon-apps/examples/detect-hexpansion.md @@ -13,18 +13,26 @@ from machine import I2C from app_components import clear_background from events.input import Buttons, BUTTON_TYPES from system.eventbus import eventbus -from system.hexpansion.events import HexpansionRemovalEvent, HexpansionInsertionEvent +from system.hexpansion.events import \ + HexpansionRemovalEvent, HexpansionInsertionEvent from system.hexpansion.util import read_hexpansion_header, detect_eeprom_addr + class ExampleApp(app.App): def __init__(self): self.button_states = Buttons(self) self.text = "No hexpansion found." - self.color = (1,0,0) + self.color = (1, 0, 0) self.scan_for_hexpansion() - eventbus.on(HexpansionInsertionEvent, self.handle_hexpansion_insertion, self) - eventbus.on(HexpansionRemovalEvent, self.handle_hexpansion_removal, self) + eventbus.on( + HexpansionInsertionEvent, + self.handle_hexpansion_insertion, + self) + eventbus.on( + HexpansionRemovalEvent, + self.handle_hexpansion_removal, + self) def handle_hexpansion_insertion(self, event): self.scan_for_hexpansion() @@ -39,8 +47,8 @@ class ExampleApp(app.App): def draw(self, ctx): ctx.save() clear_background(ctx) - x,y,z = self.color - ctx.rgb(x,y,z).move_to(-90,-40).text(self.text) + x, y, z = self.color + ctx.rgb(x, y, z).move_to(-90, -40).text(self.text) ctx.restore() def scan_for_hexpansion(self): @@ -60,21 +68,21 @@ class ExampleApp(app.App): continue else: print("Read header: " + str(header)) - self.text = "Hexp. found.\nvid: {}\npid: {}\nat port: {}".format(hex(header.vid), hex(header.pid), port) + self.text = "Hexp. found.\nvid: {}\npid: {}\nat port: {}".format( + hex(header.vid), hex(header.pid), port) found = True # Swap 0xCAFE with your EEPROM header vid # Swap 0xCAFF with your EEPROM header pid - if (header.vid is 0xCAFE) and (header.pid is 0xCAFF): + if (header.vid == 0xCAFE) and (header.pid == 0xCAFF): print("Found the desired hexpansion in port " + str(port)) - self.color = (0,1,0) + self.color = (0, 1, 0) else: print() if not found: - self.color = (1,0,0) + self.color = (1, 0, 0) self.text = "No hexpansion found." - return None ``` diff --git a/docs/tildagon-apps/examples/snake.md b/docs/tildagon-apps/examples/snake.md index c5d78b3..d2f0107 100644 --- a/docs/tildagon-apps/examples/snake.md +++ b/docs/tildagon-apps/examples/snake.md @@ -37,9 +37,9 @@ The badge simulator simulates all apps in the [`sim/apps/`](https://github.com/e def draw(self, ctx): ctx.save() - ctx.rgb(0.2,0,0).rectangle(-120,-120,240,240).fill() + ctx.rgb(0.2, 0, 0).rectangle(-120, -120, 240, 240).fill() ctx.font_size = 14 - ctx.rgb(1,0,0).move_to(-90,0).text("This will be my snake game soon!") + ctx.rgb(1, 0, 0).move_to(-90, 0).text("This will be my snake game soon!") ctx.restore() __app_export__ = SnakeApp @@ -118,7 +118,7 @@ def draw(self, ctx): clear_background(ctx) ctx.save() - ctx.translate(-80,-80) + ctx.translate(-80, -80) # draw game board ctx.rgb(0, 0, 0).rectangle(0, 0, 160, 160).fill() @@ -147,7 +147,7 @@ class SnakeApp(app.App): clear_background(ctx) ctx.save() - ctx.translate(-80,-80) + ctx.translate(-80, -80) # draw game board ctx.rgb(0, 0, 0).rectangle(0, 0, 160, 160).fill() @@ -218,7 +218,7 @@ class SnakeApp(app.App): clear_background(ctx) ctx.save() - ctx.translate(-80,-80) + ctx.translate(-80, -80) # draw game board ctx.rgb(0, 0, 0).rectangle(0, 0, 160, 160).fill() @@ -228,6 +228,7 @@ class SnakeApp(app.App): ctx.restore() + __app_export__ = SnakeApp ``` @@ -242,12 +243,16 @@ Next, you'll make your snake move up, down, left, and right using the respective ```python def update(self, delta): if self.button_states.get(BUTTON_TYPES["RIGHT"]): + print("right") # do something elif self.button_states.get(BUTTON_TYPES["LEFT"]): + print("left") # do something elif self.button_states.get(BUTTON_TYPES["UP"]): + print("up") # do something elif self.button_states.get(BUTTON_TYPES["DOWN"]): + print("down") # do something elif self.button_states.get(BUTTON_TYPES["CANCEL"]): self.button_states.clear() @@ -349,7 +354,7 @@ class SnakeApp(app.App): clear_background(ctx) ctx.save() - ctx.translate(-80,-80) + ctx.translate(-80, -80) # draw game board ctx.rgb(0, 0, 0).rectangle(0, 0, 160, 160).fill() @@ -359,6 +364,7 @@ class SnakeApp(app.App): ctx.restore() + __app_export__ = SnakeApp ``` @@ -404,7 +410,6 @@ class SnakeApp(app.App): self.direction = "" self.step = 0 - def update(self, delta): if self.button_states.get(BUTTON_TYPES["RIGHT"]): self.direction = "RIGHT" @@ -443,7 +448,7 @@ class SnakeApp(app.App): clear_background(ctx) ctx.save() - ctx.translate(-80,-80) + ctx.translate(-80, -80) # draw game board ctx.rgb(0, 0, 0).rectangle(0, 0, 160, 160).fill() @@ -453,6 +458,7 @@ class SnakeApp(app.App): ctx.restore() + __app_export__ = SnakeApp ``` @@ -486,17 +492,17 @@ Then, define a `_generate_food()` method that creates random coordinates for a n ```python def _generate_food(self): coordinates = (random.randrange(32), random.randrange(32)) - if not coordinates in self.food: + if coordinates not in self.food: self.food = self.food + [coordinates] ``` You could call this method in multiple ways. I suggest you use the `background_task()` method which allows you to add food asynchronously every few seconds: ```python - async def background_task(self): - while True: - await asyncio.sleep(5) - self._generate_food() +async def background_task(self): + while True: + await asyncio.sleep(5) + self._generate_food() ``` Add the `asyncio` package at the top of your file: @@ -583,7 +589,7 @@ class SnakeApp(app.App): def _generate_food(self): coordinates = (random.randrange(32), random.randrange(32)) - if not coordinates in self.food: + if coordinates not in self.food: self.food = self.food + [coordinates] async def background_task(self): @@ -595,7 +601,7 @@ class SnakeApp(app.App): clear_background(ctx) ctx.save() - ctx.translate(-80,-80) + ctx.translate(-80, -80) # draw game board ctx.rgb(0, 0, 0).rectangle(0, 0, 160, 160).fill() @@ -609,6 +615,7 @@ class SnakeApp(app.App): ctx.restore() + __app_export__ = SnakeApp ``` @@ -704,10 +711,9 @@ class SnakeApp(app.App): self.food.remove(self.snake[0]) self.snake = self.snake + [self.snake[0]] - def _generate_food(self): coordinates = (random.randrange(32), random.randrange(32)) - if not coordinates in self.food: + if coordinates not in self.food: self.food = self.food + [coordinates] async def background_task(self): @@ -719,7 +725,7 @@ class SnakeApp(app.App): clear_background(ctx) ctx.save() - ctx.translate(-80,-80) + ctx.translate(-80, -80) # draw game board ctx.rgb(0, 0, 0).rectangle(0, 0, 160, 160).fill() @@ -733,6 +739,7 @@ class SnakeApp(app.App): ctx.restore() + __app_export__ = SnakeApp ``` @@ -777,7 +784,7 @@ Draw the score before you call the `translate()` method: # draw score ctx.font_size = 12 width = ctx.text_width("Score: {}".format(self.score)) -ctx.rgb(1,0,0).move_to(0 - width/2,100).text("Score: {}".format(self.score)) +ctx.rgb(1, 0, 0).move_to(0 - width/2, 100).text("Score: {}".format(self.score)) ``` Your app should now resemble this: @@ -802,7 +809,6 @@ class SnakeApp(app.App): self.step = 0 self.score = 0 - def update(self, delta): if self.button_states.get(BUTTON_TYPES["RIGHT"]): self.direction = "RIGHT" @@ -843,10 +849,9 @@ class SnakeApp(app.App): self.snake = self.snake + [self.snake[0]] self.score = self.score + 1 - def _generate_food(self): coordinates = (random.randrange(32), random.randrange(32)) - if not coordinates in self.food: + if coordinates not in self.food: self.food = self.food + [coordinates] async def background_task(self): @@ -861,9 +866,10 @@ class SnakeApp(app.App): # draw score ctx.font_size = 12 width = ctx.text_width("Score: {}".format(self.score)) - ctx.rgb(1,0,0).move_to(0 - width/2,100).text("Score: {}".format(self.score)) + ctx.rgb(1, 0, 0).move_to(0 - width/2, 100).text( + "Score: {}".format(self.score)) - ctx.translate(-80,-80) + ctx.translate(-80, -80) # draw game board ctx.rgb(0, 0, 0).rectangle(0, 0, 160, 160).fill() @@ -877,6 +883,7 @@ class SnakeApp(app.App): ctx.restore() + __app_export__ = SnakeApp ``` @@ -998,7 +1005,7 @@ Define the `_reset()` method and make it reset the game state: ```python def _reset(self): - self.snake = [(16,16)] + self.snake = [(16, 16)] self.food = [] self.direction = "" self.score = 0 @@ -1048,7 +1055,7 @@ class SnakeApp(app.App): self.dialog = None def _reset(self): - self.snake = [(16,16)] + self.snake = [(16, 16)] self.food = [] self.direction = "" self.score = 0 @@ -1059,7 +1066,6 @@ class SnakeApp(app.App): self.button_states.clear() self.minimise() - def update(self, delta): if self.button_states.get(BUTTON_TYPES["RIGHT"]): self.direction = "RIGHT" @@ -1091,7 +1097,8 @@ class SnakeApp(app.App): on_no=self._exit, app=self, ) - # Reset the game variable to ensure this dialog is only created once + # Reset the game variable to ensure this dialog is only created + # once self.game = "" def _move_snake(self): @@ -1124,7 +1131,7 @@ class SnakeApp(app.App): def _generate_food(self): coordinates = (random.randrange(32), random.randrange(32)) - if not coordinates in self.food: + if coordinates not in self.food: self.food = self.food + [coordinates] async def background_task(self): @@ -1140,9 +1147,10 @@ class SnakeApp(app.App): # draw score ctx.font_size = 12 width = ctx.text_width("Score: {}".format(self.score)) - ctx.rgb(1,0,0).move_to(0 - width/2,100).text("Score: {}".format(self.score)) + ctx.rgb(1, 0, 0).move_to(0 - width/2, 100).text( + "Score: {}".format(self.score)) - ctx.translate(-80,-80) + ctx.translate(-80, -80) # draw game board ctx.rgb(0, 0, 0).rectangle(0, 0, 160, 160).fill() @@ -1159,6 +1167,7 @@ class SnakeApp(app.App): if self.dialog: self.dialog.draw(ctx) + __app_export__ = SnakeApp ``` @@ -1223,11 +1232,13 @@ def update(self, delta): else: # Use Y coordinate to go left or right if self.acc_read[1] > 0: - # A positive Y coordinate indicates the badge is tilted to the right + # A positive Y coordinate indicates the badge is tilted to the + # right self.direction = "RIGHT" self.game = "ON" else: - # A positive Y coordinate indicates the badge is tilted to the left + # A positive Y coordinate indicates the badge is tilted to the + # left self.direction = "LEFT" self.game = "ON" diff --git a/docs/tildagon-apps/reference/badge-hardware.md b/docs/tildagon-apps/reference/badge-hardware.md index b4622c0..40a180e 100644 --- a/docs/tildagon-apps/reference/badge-hardware.md +++ b/docs/tildagon-apps/reference/badge-hardware.md @@ -15,13 +15,15 @@ from app_components import clear_background from events.input import Buttons, BUTTON_TYPES from tildagonos import tildagonos from system.eventbus import eventbus -from system.patterndisplay.events import * +from system.patterndisplay.events import PatternDisable + class LEDExampleApp(app.App): def __init__(self): self.button_states = Buttons(self) - # This disables the patterndisplay system module, which does the default colour spinny thing + # This disables the patterndisplay system module, which does the + # default colour spinny thing eventbus.emit(PatternDisable()) def update(self, delta): @@ -44,12 +46,13 @@ class LEDExampleApp(app.App): tildagonos.leds[4] = (255, 0, 255) tildagonos.leds[5] = (255, 0, 255) else: - for i in range(0,12): + for i in range(0, 12): tildagonos.leds[i+1] = (0, 0, 0) def draw(self, ctx): clear_background(ctx) + __app_export__ = LEDExampleApp ``` @@ -98,6 +101,7 @@ from app_components import clear_background from events.input import Buttons, BUTTON_TYPES from tildagonos import tildagonos + class LEDExampleApp(app.App): def __init__(self): self.button_states = Buttons(self) @@ -122,12 +126,13 @@ class LEDExampleApp(app.App): tildagonos.leds[4] = (255, 0, 255) tildagonos.leds[5] = (255, 0, 255) else: - for i in range(0,12): + for i in range(0, 12): tildagonos.leds[i+1] = (0, 0, 0) def draw(self, ctx): clear_background(ctx) + __app_export__ = LEDExampleApp ``` @@ -170,7 +175,8 @@ You can also use the `ButtonDownEvent` and the `ButtonUpEvent` directly with an 1. Import the `events.input` package: ```python - from events.input import Button, BUTTON_TYPES, ButtonDownEvent, ButtonUpEvent + from events.input import \ + Button, BUTTON_TYPES, ButtonDownEvent, ButtonUpEvent from system.eventbus import eventbus ``` @@ -231,7 +237,7 @@ Select a hexpansion port, then press the **UP** button to toggle the eGPIO value ```python import app -from system.hexpansion.config import * +from system.hexpansion.config import HexpansionConfig from app_components import clear_background, Menu from app_components.tokens import colors from events.input import Buttons, BUTTON_TYPES @@ -239,9 +245,11 @@ from math import pi menu_items = ["1", "2", "3", "4", "5", "6"] + class ExampleApp(app.App): def __init__(self): - self.menu = Menu(self, menu_items, select_handler=self.select_handler, back_handler=self.back_handler) + self.menu = Menu(self, menu_items, select_handler=self.select_handler, + back_handler=self.back_handler) self.hexpansion_config = None self.button_states = Buttons(self) self.pins = {} @@ -291,12 +299,16 @@ class ExampleApp(app.App): # Drawing a shape as a port indicator. ctx.save() ctx.font_size = 22 - ctx.rgb(*colors["dark_green"]).rectangle(-120,-120, 240, 100).fill() - ctx.rgb(*colors["dark_green"]).rectangle(-120, 20, 240, 100).fill() + ctx.rgb(*colors["dark_green"]).rectangle( + -120, -120, 240, 100).fill() + ctx.rgb(*colors["dark_green"]).rectangle( + -120, 20, 240, 100).fill() rotation_angle = self.menu.position*pi/3 - ctx.rgb(*colors["mid_green"]).rotate(rotation_angle).rectangle(80,-120,40,240).fill() + ctx.rgb(*colors["mid_green"]).rotate(rotation_angle).rectangle( + 80, -120, 40, 240).fill() prompt_message = "Select hexpansion port:" - ctx.rgb(1,1,1).rotate(-rotation_angle).move_to(0,-45).text(prompt_message) + ctx.rgb(1, 1, 1).rotate(-rotation_angle).move_to( + 0, -45).text(prompt_message) ctx.restore() else: @@ -304,19 +316,20 @@ class ExampleApp(app.App): ctx.font_size = 24 msg = "Hexpansion in port " + str(self.hexpansion_config.port) msg_width = ctx.text_width(msg) - ctx.rgb(1,1,1).move_to(-msg_width/2,0).text(msg) + ctx.rgb(1, 1, 1).move_to(-msg_width/2, 0).text(msg) # draw pin values pin_ls_1 = "LS_1: " + str(self.pins["ls_1"].value()) msg_width = ctx.text_width(pin_ls_1) - ctx.rgb(1,1,1).move_to(-msg_width/2,-90).text(pin_ls_1) + ctx.rgb(1, 1, 1).move_to(-msg_width/2, -90).text(pin_ls_1) pin_hs_1 = "HS_1: " + str(self.pins["hs_1"].value()) msg_width = ctx.text_width(pin_hs_1) - ctx.rgb(1,1,1).move_to(-msg_width/2,90).text(pin_hs_1) + ctx.rgb(1, 1, 1).move_to(-msg_width/2, 90).text(pin_hs_1) ctx.restore() + __app_export__ = ExampleApp ``` @@ -328,11 +341,12 @@ GPIO pins support the standard [`machine.Pin` methods](https://docs.micropython. [eGPIO pins](https://github.com/emfcamp/badge-2024-software/blob/main/modules/tildagon/pins.py) support the following methods: -| Method | Description | Arguments | Returns | -| --------- | ---------------------------------------------------------------------------------------------- | --------- | ---------------------------------------------------- | -| `on()` | Drive the pin high. | None | None | -| `off()` | Drive the pin low. | None | None | -| `value()` | If provided with a value, sets the `Pin` value. If called without value, gets the `Pin` value. | None | `value`: The pin value. If called without a `value`. | + +| Method | Description | Arguments | Returns | +| ------ | ----------- | --------- | ------- | +| `on()` | Drive the pin high. | None | None | +| `off()` | Drive the pin low. | None | None | +| `value()` | If provided with a value, sets the `Pin` value. If called without value, gets the `Pin` value. | None | `value`: The pin value. If called without a `value`. | ### Usage @@ -385,13 +399,16 @@ class ExampleApp(app.App): def draw(self, ctx): ctx.save() - ctx.rgb(0.2,0,0).rectangle(-120,-120,240,240).fill() + ctx.rgb(0.2, 0, 0).rectangle(-120, -120, 240, 240).fill() if self.acc_read: - ctx.rgb(1,0,0).move_to(-80,-40).text("accel x,y,z:\n{},\n{},\n{}".format(self.acc_read[0], self.acc_read[1], self.acc_read[2])) + ctx.rgb(1, 0, 0).move_to(-80, -40).text( + "accel x,y,z:\n{},\n{},\n{}".format( + self.acc_read[0], self.acc_read[1], self.acc_read[2])) else: - ctx.rgb(1,0,0).move_to(-80,0).text("no readings yet") + ctx.rgb(1, 0, 0).move_to(-80, 0).text("no readings yet") ctx.restore() + __app_export__ = ExampleApp ``` @@ -399,10 +416,11 @@ __app_export__ = ExampleApp The api currently only allows access to the raw data. -| Method | Description | Arguments | Returns | -| ------------- | --------------------------- | --------- | --------------------------------------------------------------- | -| `acc_read()` | Get the accelerometer data. | None | `(x,y,z)`: The accelerometer data as a tuple of floats (m/s^2). | -| `gyro_read()` | Get the gyro data. | None | `(x,y,z)`: The gyro data as a tuple of floats (d/s). | + +| Method | Description | Arguments | Returns | +| ------ | ----------- | --------- | ------- | +| `acc_read()` | Get the accelerometer data. | None | `(x,y,z)`: The accelerometer data as a tuple of floats (m/s^2). | +| `gyro_read()` | Get the gyro data. | None | `(x,y,z)`: The gyro data as a tuple of floats (d/s). | ### Usage @@ -447,10 +465,11 @@ class ExampleApp(app.App): def draw(self, ctx): ctx.save() - ctx.rgb(0.2,0,0).rectangle(-120,-120,240,240).fill() - ctx.rgb(1,0,0).move_to(-80,0).text("Press up to\npower off") + ctx.rgb(0.2, 0, 0).rectangle(-120, -120, 240, 240).fill() + ctx.rgb(1, 0, 0).move_to(-80, 0).text("Press up to\npower off") ctx.restore() + __app_export__ = ExampleApp ``` @@ -472,18 +491,19 @@ To use the `power` package: ### Methods -| Method | Description | Arguments | Returns | -| ---------------------- | --------------------------------------------------------------------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `Off()` | Turn off the battery. When the usb is disconnected the badge will turn off. | None | None | -| `BatteryChargeState()` | Status of the Battery charing cycle. | None | `status` (`string`): `"Not Charging"`, `"Pre-Charging"`, `"Fast Charging"`, `"Terminated"`. | -| `BatteryLevel()` | Return the battery charge level. | None. | `level` (`float`): Battery charge level as a float representing the charge percentage. | -| `Enable5V()` | Enable the usb out 5V supply. | `enable` (`Boolean`): whether to enable or disable the 5V supply. | None. | -| `Fault()` | Get the PMIC fault status. | None. | - `fault`: The battery fault. Battery: Normal, Over Voltage; Boost: Normal, Overloaded or low battery; Charge: Normal, Input Fault, Safety Timer expired | -| `SupplyCapabilities()` | Read the capabilities of the power supply. | None. | `capabilities` (`List`): List of tuples containing supply type, voltage (V) and current (mA). | -| `Icharge()` | Get the battery charge current | None. | `current` (`float`): The charge current in mA. | -| `Vbat()` | Get the battery voltage. | None. | `voltage` (`float`): The battery voltage in V. | -| `Vin()` | Get the input voltage. | None. | `voltage` (`float`): The input voltage in V. | -| `Vsys()` (`float`) | Get the system voltage. | None. | `voltage` (`float`): Get the system voltage in V. | + +| Method | Description | Arguments | Returns | +| ------ | ----------- | --------- | ------- | +| `Off()`| Turn off the battery. When the usb is disconnected the badge will turn off. | None | None | +| `BatteryChargeState()` | Status of the Battery charing cycle. | None | `status` (`string`): `"Not Charging"`, `"Pre-Charging"`, `"Fast Charging"`, `"Terminated"`. | +| `BatteryLevel()` | Return the battery charge level. | None. | `level` (`float`): Battery charge level as a float representing the charge percentage. | +| `Enable5V()` | Enable the usb out 5V supply. | `enable` (`Boolean`): whether to enable or disable the 5V supply. | None. | +| `Fault()` | Get the PMIC fault status. | None. | - `fault`: The battery fault. Battery: Normal, Over Voltage; Boost: Normal, Overloaded or low battery; Charge: Normal, Input Fault, Safety Timer expired | +| `SupplyCapabilities()` | Read the capabilities of the power supply. | None. | `capabilities` (`List`): List of tuples containing supply type, voltage (V) and current (mA). | +| `Icharge()` | Get the battery charge current | None. | `current` (`float`): The charge current in mA. | +| `Vbat()` | Get the battery voltage. | None. | `voltage` (`float`): The battery voltage in V. | +| `Vin()` | Get the input voltage. | None. | `voltage` (`float`): The input voltage in V. | +| `Vsys()` (`float`) | Get the system voltage. | None. | `voltage` (`float`): Get the system voltage in V. | ### Events @@ -517,7 +537,8 @@ You can also use the following hexpansion-related events To use these events with the `EventBus`, import the following package: ```python -from system.hexpansion.events import HexpansionRemovalEvent, HexpansionInsertionEvent +from system.hexpansion.events import \ + HexpansionRemovalEvent, HexpansionInsertionEvent ``` Then `emit()` the event as following: @@ -536,7 +557,7 @@ The badge supports the [I2C communication protocol](https://www.circuitbasics.co ```python from machine import I2C -bus = I2C(slot) +bus = I2C(1) ``` === "App loaded from EEPROM" @@ -570,7 +591,7 @@ bus = I2C(slot) def draw(self, ctx): ctx.save() clear_background(ctx) - ctx.rgb(0,1,0).move_to(-90,-40).text("Hello from your\nhexpansion!") + ctx.rgb(0, 1, 0).move_to(-90, -40).text("Hello from your\nhexpansion!") ctx.restore() return None @@ -621,7 +642,7 @@ bus = I2C(slot) def draw(self, ctx): ctx.save() clear_background(ctx) - ctx.rgb(0,1,0).move_to(-90,-40).text(self.text) + ctx.rgb(0, 1, 0).move_to(-90, -40).text(self.text) ctx.restore() def scan_for_hexpansion(self): @@ -643,7 +664,7 @@ bus = I2C(slot) self.text = "Hexp. found.\nvid: {}\npid: {}\nat port: {}".format(hex(header.vid), hex(header.pid), port) return HexpansionConfig(port) - self.color = (1,0,0) + self.color = (1, 0, 0) self.text = "No hexpansion found." return None @@ -658,15 +679,21 @@ Example usage from the [MicroPython I2C docs](https://docs.micropython.org/en/la ```python from machine import I2C -i2c.scan() # scan for peripherals, returning a list of 7-bit addresses - -i2c.writeto(42, b'123') # write 3 bytes to peripheral with 7-bit address 42 -i2c.readfrom(42, 4) # read 4 bytes from peripheral with 7-bit address 42 - -i2c.readfrom_mem(42, 8, 3) # read 3 bytes from memory of peripheral 42, - # starting at memory-address 8 in the peripheral -i2c.writeto_mem(42, 2, b'\x10') # write 1 byte to memory of peripheral 42 - # starting at address 2 in the peripheral +i2c = I2C(freq=400000) + +# scan for peripherals, returning a list of 7-bit addresses +i2c.scan() + +# write 3 bytes to peripheral with 7-bit address 42 +i2c.writeto(42, b'123') +# read 4 bytes from peripheral with 7-bit address 42 +i2c.readfrom(42, 4) +# read 3 bytes from memory of peripheral 42, starting at memory-address 8 in +# the peripheral +i2c.readfrom_mem(42, 8, 3) +# write 1 byte to memory of peripheral 42 starting at address 2 in the +# peripheral +i2c.writeto_mem(42, 2, b'\x10') ``` For more information, see [MicroPython I2C docs](https://docs.micropython.org/en/latest/library/machine.I2C.html). diff --git a/docs/tildagon-apps/reference/ctx.md b/docs/tildagon-apps/reference/ctx.md index 8e82a69..6861ad0 100644 --- a/docs/tildagon-apps/reference/ctx.md +++ b/docs/tildagon-apps/reference/ctx.md @@ -30,6 +30,7 @@ class ExampleApp(app.App): def draw(self, ctx): clear_background(ctx) + __app_export__ = ExampleApp ``` @@ -37,7 +38,7 @@ __app_export__ = ExampleApp ### Coordinates of the display area -With `ctx`, you need to provide a place for where to draw objects using coordinates in the form of `(x,y)`. The x-coordinate represents the horizontal coordinate and the y-coordinate the vertical. The middle is at `(0,0)`. +With `ctx`, you need to provide a place for where to draw objects using coordinates in the form of `(x, y)`. The x-coordinate represents the horizontal coordinate and the y-coordinate the vertical. The middle is at `(0, 0)`. ![An app drawing the axes](../../images/ctx-examples/coordinates.png){: style="width:400px;height: auto;margin:auto;display:block;" } @@ -49,7 +50,6 @@ The following is the code for the axes drawing: ```python import app -import math from app_components import clear_background from events.input import Buttons, BUTTON_TYPES @@ -67,12 +67,12 @@ class ExampleApp(app.App): def draw(self, ctx): clear_background(ctx) ctx.rgb(0, 1, 0).begin_path() - ctx.move_to(-120,0) + ctx.move_to(-120, 0) ctx.line_to(120, 0) ctx.move_to(0, 120) ctx.line_to(0, -120) - for i in range(-5,6): + for i in range(-5, 6): # draw indicator lines for x ctx.move_to(i*20, 5) ctx.line_to(i*20, -5) @@ -83,13 +83,14 @@ class ExampleApp(app.App): # text for x and y ctx.stroke() - for i in range(-5,6): + for i in range(-5, 6): ctx.font_size = 12 width = ctx.text_width(str(i*20)) # x axes ctx.rgb(0, 1, 0).move_to(i*20 - width/2, 15).text(i*20) # y axes - ctx.rgb(0, 1, 0).move_to(-15 - width, i*20 + 2*line_height).text(i*20) + ctx.rgb(0, 1, 0).move_to( + -15 - width, i*20 + 2*line_height).text(i*20) width = ctx.text_width("y") ctx.rgb(0, 1, 0).move_to(+15, -100+2*line_height).text("y") @@ -97,6 +98,7 @@ class ExampleApp(app.App): width = ctx.text_width("x") ctx.rgb(0, 1, 0).move_to(100 - width/2, -15).text("x") + __app_export__ = ExampleApp ``` @@ -179,6 +181,7 @@ class ExampleApp(app.App): ctx.rectangle(0, 30, 5, 5).fill() ctx.rectangle(0, 40, 5, 5).fill() + __app_export__ = ExampleApp ``` @@ -216,6 +219,7 @@ class ExampleApp(app.App): ctx.restore() + __app_export__ = ExampleApp ``` @@ -251,6 +255,7 @@ class ExampleApp(app.App): ctx.restore() + __app_export__ = ExampleApp ``` @@ -280,12 +285,13 @@ class ExampleApp(app.App): ctx.save() ctx.radial_gradient(30, 30, 80, -30, -50, 70) - ctx.add_stop(0, (100,0,100), 0.5) - ctx.add_stop(1, (100,0,0), 0.8) + ctx.add_stop(0, (100, 0, 100), 0.5) + ctx.add_stop(1, (100, 0, 0), 0.8) ctx.rectangle(-100, -100, 200, 200).fill() ctx.restore() + __app_export__ = ExampleApp ``` @@ -294,7 +300,6 @@ __app_export__ = ExampleApp This example shows an example of a linear gradient on a rectangle. ```python -import asyncio import app from events.input import Buttons, BUTTON_TYPES @@ -310,7 +315,7 @@ class ExampleApp(app.App): def draw(self, ctx): ctx.save() - ctx.linear_gradient(0.18*120,0.5*120,0.95*120,0.5*120) + ctx.linear_gradient(0.18*120, 0.5*120, 0.95*120, 0.5*120) ctx.add_stop(0.0, (255, 0, 0), 1.0) ctx.add_stop(0.2, (255, 255, 0), 1.0) ctx.add_stop(0.4, (0, 255, 0), 1.0) @@ -323,6 +328,7 @@ class ExampleApp(app.App): ctx.restore() + __app_export__ = ExampleApp ``` @@ -356,6 +362,7 @@ class ExampleApp(app.App): ctx.gray(1.0).arc(30, 0, 40, 0, 2 * math.pi, True).fill() ctx.restore() + __app_export__ = ExampleApp ``` @@ -382,10 +389,11 @@ class ExampleApp(app.App): def draw(self, ctx): ctx.save() ctx.font = ctx.get_font_name(5) - ctx.rgb(100, 0, 0).rectangle(-120,-120,240,240).fill() - ctx.rgb(255, 0, 0).move_to(-80,0).text("Hello world") + ctx.rgb(100, 0, 0).rectangle(-120, -120, 240, 240).fill() + ctx.rgb(255, 0, 0).move_to(-80, 0).text("Hello world") ctx.restore() + __app_export__ = ExampleApp ``` @@ -432,9 +440,10 @@ class ExampleApp(app.App): ctx.rgb(255, 0, 0).move_to(-55, -10).text(line_1) ctx.rgb(255, 0, 0).move_to(-55, 10).text(line_2) else: - ctx.rgb(255, 0, 0).move_to(-55, 0).text(text) + ctx.rgb(255, 0, 0).move_to(-55, 0).text(text) ctx.restore() + __app_export__ = ExampleApp ``` @@ -471,6 +480,7 @@ class ExampleApp(app.App): ctx.rgb(0, 0, 200).arc(0, 0, 10, 0, 2 * math.pi, True).stroke() ctx.restore() + __app_export__ = ExampleApp ``` @@ -511,6 +521,7 @@ class ExampleApp(app.App): ctx.rgb(255, 0, 0).fill() ctx.restore() + __app_export__ = ExampleApp ``` @@ -538,11 +549,12 @@ class ExampleApp(app.App): def draw(self, ctx): clear_background(ctx) ctx.save() - ctx.rgb(100,0,0).rectangle(-40, 0, 40, 20).fill() + ctx.rgb(100, 0, 0).rectangle(-40, 0, 40, 20).fill() ctx.scale(1, 3) - ctx.rgb(0,100,0).rectangle(0, 0, 40, 20).fill() + ctx.rgb(0, 100, 0).rectangle(0, 0, 40, 20).fill() ctx.restore() + __app_export__ = ExampleApp ``` @@ -570,11 +582,12 @@ class ExampleApp(app.App): clear_background(ctx) ctx.save() ctx.rotate(math.pi) - ctx.rgb(100,0,0).rectangle(-40, 0, 40, 20).fill() + ctx.rgb(100, 0, 0).rectangle(-40, 0, 40, 20).fill() ctx.scale(1, 3) - ctx.rgb(0,100,0).rectangle(0, 0, 40, 20).fill() + ctx.rgb(0, 100, 0).rectangle(0, 0, 40, 20).fill() ctx.restore() + __app_export__ = ExampleApp ``` @@ -601,13 +614,14 @@ class ExampleApp(app.App): def draw(self, ctx): clear_background(ctx) ctx.save() - ctx.translate(-50,0) + ctx.translate(-50, 0) ctx.rotate(math.pi) - ctx.rgb(100,0,0).rectangle(-40, 0, 40, 20).fill() + ctx.rgb(100, 0, 0).rectangle(-40, 0, 40, 20).fill() ctx.scale(1, 3) - ctx.rgb(0,100,0).rectangle(0, 0, 40, 20).fill() + ctx.rgb(0, 100, 0).rectangle(0, 0, 40, 20).fill() ctx.restore() + __app_export__ = ExampleApp ``` @@ -623,6 +637,7 @@ import app from app_components import clear_background from events.input import Buttons, BUTTON_TYPES + class ExampleApp(app.App): def __init__(self): self.button_states = Buttons(self) @@ -635,13 +650,14 @@ class ExampleApp(app.App): clear_background(ctx) ctx.save() ctx.rgb(1, 0, 0).begin_path() - ctx.move_to(0,0) + ctx.move_to(0, 0) ctx.line_to(50, 50) ctx.move_to(-30, 50) ctx.line_to(50, 80) ctx.stroke() ctx.restore() + __app_export__ = ExampleApp ``` @@ -657,6 +673,7 @@ import app from app_components import clear_background from events.input import Buttons, BUTTON_TYPES + class ExampleApp(app.App): def __init__(self): self.button_states = Buttons(self) @@ -669,7 +686,7 @@ class ExampleApp(app.App): clear_background(ctx) ctx.save() ctx.rgb(1, 0, 0).begin_path() - ctx.move_to(0,0) + ctx.move_to(0, 0) ctx.line_to(50, 50) ctx.stroke() ctx.rgb(0, 1, 0) @@ -678,6 +695,7 @@ class ExampleApp(app.App): ctx.stroke() ctx.restore() + __app_export__ = ExampleApp ``` @@ -689,11 +707,11 @@ This example draws a triangle. ```python import app -import math from app_components import clear_background from events.input import Buttons, BUTTON_TYPES + class ExampleApp(app.App): def __init__(self): self.button_states = Buttons(self) @@ -713,6 +731,7 @@ class ExampleApp(app.App): ctx.stroke() ctx.restore() + __app_export__ = ExampleApp ``` @@ -729,6 +748,7 @@ import math from app_components import clear_background from events.input import Buttons, BUTTON_TYPES + class ExampleApp(app.App): def __init__(self): self.button_states = Buttons(self) @@ -747,24 +767,25 @@ class ExampleApp(app.App): # end point (e, f): (60, 80) # Add cubic bézier curve - ctx.move_to(-80,-80) + ctx.move_to(-80, -80) ctx.curve_to(-70, 0, 60, 20, 60, 80) ctx.stroke() # Show start and end points in blue ctx.rgb(0, 0, 1).begin_path() - ctx.arc(-80,-80, 5, 0, 2 * math.pi, True) # start point - ctx.arc(60, 80, 5, 0, 2 * math.pi, True) # end point + ctx.arc(-80, -80, 5, 0, 2 * math.pi, True) # start point + ctx.arc(60, 80, 5, 0, 2 * math.pi, True) # end point ctx.fill() # Show control points in green ctx.rgb(0, 1, 0).begin_path() - ctx.arc(-70, 0, 5, 0, 2 * math.pi, True) # first control point - ctx.arc(60, 20, 5, 0, 2 * math.pi, True) # second control point + ctx.arc(-70, 0, 5, 0, 2 * math.pi, True) # first control point + ctx.arc(60, 20, 5, 0, 2 * math.pi, True) # second control point ctx.fill() ctx.restore() + __app_export__ = ExampleApp ``` @@ -780,36 +801,37 @@ __app_export__ = ExampleApp You can use the following methods on a `ctx` canvas object: -| Method | Description | Arguments | Returns | -| ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | -| `move_to(x, y)` | Begin a new sub-path at the point specified by the given `(x, y)` coordinates. | | `self`: The `ctx` object. | -| `curve_to(a, b, c, d, e, f)` | Add a cubic [Bézier curve](https://developer.mozilla.org/en-US/docs/Glossary/Bezier_curve) to the current sub-path. It requires three points: the first two are control points and the third one is the end point. The starting point is the latest point in the current path, which can be changed using `moveTo()` before creating the Bézier curve. | | `self`: The `ctx` object. | -| `quad_to(a, b, c, d)` | Add a quadratic [Bézier curve](https://developer.mozilla.org/en-US/docs/Glossary/Bezier_curve) to the current sub-path. It requires two points: the first one is a control point and the second one is the end point. The starting point is the latest point in the current path, which can be changed using `moveTo()` before creating the quadratic Bézier curve. | | `self`: The `ctx` object. | -| `rel_move_to(x, y)` | Begin a new sub-path at the point specified reached by adding `(x, y)` to the current coordinates. | | `self`: The `ctx` object. | -| `rel_curve_to(a, b, c, d, e, f)` | Add a cubic Bézier curve to the current sub-path relative to the current position instead of relative to `(0, 0, 0)`. It requires three points: the first two are control points and the third one is the end point. The starting point is the latest point in the current path, which can be changed using `moveTo()` before creating the Bézier curve. | | `self`: The `ctx` object. | -| `rel_quad_to(a, b, c, d)` | Add a quadratic Bézier curve to the current sub-path relative to the current position. It requires two points: the first one is a control point and the second one is the end point. The starting point is the latest point in the current path, which can be changed using `moveTo()` before creating the quadratic Bézier curve. | | `self`: The `ctx` object. | -| `close_path()` | Attempt to add a straight line from the current point to the start of the current sub-path. If the shape has already been closed or has only one point, this function does nothing. This method doesn't draw anything to the canvas directly. You can render the path using the stroke() or fill() methods. | None. | `self`: The `ctx` object. | -| `translate(x, y)` | Add a translation transformation to the current matrix by moving the canvas and its origin `x` units horizontally and `y` units vertically on the grid. | | `self`: The `ctx` object. | -| `scale(x, y)` | Add a scaling transformation to the canvas units horizontally and/or vertically. By default, one unit on the canvas is exactly one pixel. A scaling transformation modifies this behavior. For instance, a scaling factor of 0.5 results in a unit size of 0.5 pixels; shapes are thus drawn at half the normal size. Similarly, a scaling factor of 2.0 increases the unit size so that one unit becomes two pixels; shapes are thus drawn at twice the normal size. | | `self`: The `ctx` object. | -| `line_to(x, y)` | Add a straight line to the current sub-path by connecting the sub-path's last point to the specified `(x, y)` coordinates. Like other methods that modify the current path, this method does not directly render anything. To draw the path onto a canvas, you can use the `fill()` or `stroke()` methods. | | `self`: The `ctx` object. | -| `rel_line_to(x, y)` | Add a straight line to the current sub-path by connecting the sub-path's last point to a point moved `x` units horizontally and `y` units vertically on the grid. Like other methods that modify the current path, this method does not directly render anything. To draw the path onto a canvas, you can use the `fill()` or `stroke()` methods. | | `self`: The `ctx` object. | -| `rotate(v)` | Add a rotation to the transformation matrix. The rotation center point is always the canvas origin. To change the center point, you will need to move the canvas by using the `translate()` method. | `v`: The rotation angle, clockwise in radians. You can use degree \* pi / 180 to calculate a radian from a degree. | `self`: The `ctx` object. | -| `gray(v)` | Sets the color of the canvas to a color between `0.0` (black) and `1.0`. | `v`: Intensity of the gray color. `0.0` results in black and `1.0` in white. | `self`: The `ctx` object. | -| `rgba(r, g, b, a)` | Color the canvas in the color `(r, g, b)` with an alpha channel. | | `self`: The `ctx` object. | -| `rgb(r, g, b)` | Color the canvas in the color `(r, g, b)`. | | `self`: The `ctx` object. | -| `text(s)` | Draw the provided text on the canvas. | `s`: The text to draw provided as a string. | `self`: The `ctx` object. | -| `round_rectangle(x, y, width, height, radius)` | Add a rounded rectangle on the canvas at position `(x, y)` with the provided width, height, and corner radius. | | `self`: The `ctx` object. | -| `image(path, x, y, w, h)` | **Currently not functional.** Draw an image to be read from the provided path onto the canvas. | | `self`: The `ctx` object. | -| `rectangle(x, y, width, height)` | Add a rectangle to the current path. Like other methods that modify the current path, this method does not directly render anything. To draw the rectangle onto a canvas, you can use the `fill()` or `stroke()` methods. The `rect()` method creates a rectangular path whose starting point is at `(x, y)` and whose size is specified by width and height. | | `self`: The `ctx` object. | -| `stroke()` | Stroke (outline) the current or given path with the current stroke style. | None. | `self`: The `ctx` object. | -| `save()` | Save the entire state of the canvas by pushing the current state onto a stack. | None. | `self`: The `ctx` object. | -| `restore()` | Restore the most recently saved canvas state by popping the top entry in the drawing state stack. If there is no saved state, this method does nothing. | None. | `self`: The `ctx` object. | -| `fill()` | Fill the current or given path. | None. | `self`: The `ctx` object. | -| `radial_gradient(x0, y0, r0, x1, y1, r1)` | Create a radial gradient using the size and coordinates of two circles. | | `self`: The `ctx` object. | -| `linear_gradient(x0, y0, x1, y1)` | Create a gradient along the line connecting two given coordinates. | | `self`: The `ctx` object. | -| `add_stop(pos, color, alpha)` | Adds a color stop in a gradient with the provided position, color, and alpha values. | | `self`: The `ctx` object. | -| `begin_path()` | Start a new path by emptying the list of sub-paths. Call this method when you want to create a new path. | None. | `self`: The `ctx` object. | -| `arc(x, y, radius, arc_from, arc_to, direction)` | Add a circular arc to the current sub-path. | | `self`: The `ctx` object. | -| `text_width(text)` | Calculate the width of the provided text. | `text`: The text to calculate the width of. | `width`: The width of the provided text. | -| `clip()` | Turn the current or given path into the current clipping region. The previous clipping region, if any, is intersected with the current or given path to create the new clipping region. For a visual example, see the [mdn web docs](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/clip). | None. | `self`: The `ctx` object. | -| `get_font_name(i)` | Get the name of one of the 9 fonts: `"Arimo Regular"`, `"Arimo Bold"`, `"Arimo Italic"`, `"Arimo Bold Italic"`, `"Camp Font 1"`, `"Camp Font 2"`, `"Camp Font 3"`, `"Material Icons"`, and `"Comic Mono"`. | `i`: The number of the font to get the name of. A number between `0` and `8`. | `font_name`: The font. | + +| Method | Description | Arguments | Returns | +| ------ | ----------- | --------- | ------- | +| `move_to(x, y)` | Begin a new sub-path at the point specified by the given `(x, y)` coordinates. | | `self`: The `ctx` object. | +| `curve_to(a, b, c, d, e, f)` | Add a cubic [Bézier curve](https://developer.mozilla.org/en-US/docs/Glossary/Bezier_curve) to the current sub-path. It requires three points: the first two are control points and the third one is the end point. The starting point is the latest point in the current path, which can be changed using `moveTo()` before creating the Bézier curve. | | `self`: The `ctx` object. | +| `quad_to(a, b, c, d)` | Add a quadratic [Bézier curve](https://developer.mozilla.org/en-US/docs/Glossary/Bezier_curve) to the current sub-path. It requires two points: the first one is a control point and the second one is the end point. The starting point is the latest point in the current path, which can be changed using `moveTo()` before creating the quadratic Bézier curve. | | `self`: The `ctx` object. | +| `rel_move_to(x, y)` | Begin a new sub-path at the point specified reached by adding `(x, y)` to the current coordinates. | | `self`: The `ctx` object. | +| `rel_curve_to(a, b, c, d, e, f)` | Add a cubic Bézier curve to the current sub-path relative to the current position instead of relative to `(0, 0, 0)`. It requires three points: the first two are control points and the third one is the end point. The starting point is the latest point in the current path, which can be changed using `moveTo()` before creating the Bézier curve. | | `self`: The `ctx` object. | +| `rel_quad_to(a, b, c, d)` | Add a quadratic Bézier curve to the current sub-path relative to the current position. It requires two points: the first one is a control point and the second one is the end point. The starting point is the latest point in the current path, which can be changed using `moveTo()` before creating the quadratic Bézier curve. | | `self`: The `ctx` object. | +| `close_path()` | Attempt to add a straight line from the current point to the start of the current sub-path. If the shape has already been closed or has only one point, this function does nothing. This method doesn't draw anything to the canvas directly. You can render the path using the stroke() or fill() methods. | None. | `self`: The `ctx` object. | +| `translate(x, y)` | Add a translation transformation to the current matrix by moving the canvas and its origin `x` units horizontally and `y` units vertically on the grid. | | `self`: The `ctx` object. | +| `scale(x, y)` | Add a scaling transformation to the canvas units horizontally and/or vertically. By default, one unit on the canvas is exactly one pixel. A scaling transformation modifies this behavior. For instance, a scaling factor of 0.5 results in a unit size of 0.5 pixels; shapes are thus drawn at half the normal size. Similarly, a scaling factor of 2.0 increases the unit size so that one unit becomes two pixels; shapes are thus drawn at twice the normal size. | | `self`: The `ctx` object. | +| `line_to(x, y)` | Add a straight line to the current sub-path by connecting the sub-path's last point to the specified `(x, y)` coordinates. Like other methods that modify the current path, this method does not directly render anything. To draw the path onto a canvas, you can use the `fill()` or `stroke()` methods. | | `self`: The `ctx` object. | +| `rel_line_to(x, y)` | Add a straight line to the current sub-path by connecting the sub-path's last point to a point moved `x` units horizontally and `y` units vertically on the grid. Like other methods that modify the current path, this method does not directly render anything. To draw the path onto a canvas, you can use the `fill()` or `stroke()` methods. | | `self`: The `ctx` object. | +| `rotate(v)` | Add a rotation to the transformation matrix. The rotation center point is always the canvas origin. To change the center point, you will need to move the canvas by using the `translate()` method. | `v`: The rotation angle, clockwise in radians. You can use degree \* pi / 180 to calculate a radian from a degree. | `self`: The `ctx` object. | +| `gray(v)` | Sets the color of the canvas to a color between `0.0` (black) and `1.0`. | `v`: Intensity of the gray color. `0.0` results in black and `1.0` in white. | `self`: The `ctx` object. | +| `rgba(r, g, b, a)` | Color the canvas in the color `(r, g, b)` with an alpha channel. | | `self`: The `ctx` object. | +| `rgb(r, g, b)` | Color the canvas in the color `(r, g, b)`. | | `self`: The `ctx` object. | +| `text(s)` | Draw the provided text on the canvas. | `s`: The text to draw provided as a string. | `self`: The `ctx` object. | +| `round_rectangle(x, y, width, height, radius)` | Add a rounded rectangle on the canvas at position `(x, y)` with the provided width, height, and corner radius. | | `self`: The `ctx` object. | +| `image(path, x, y, w, h)` | **Currently not functional.** Draw an image to be read from the provided path onto the canvas. | | `self`: The `ctx` object. | +| `rectangle(x, y, width, height)` | Add a rectangle to the current path. Like other methods that modify the current path, this method does not directly render anything. To draw the rectangle onto a canvas, you can use the `fill()` or `stroke()` methods. The `rect()` method creates a rectangular path whose starting point is at `(x, y)` and whose size is specified by width and height. | | `self`: The `ctx` object. | +| `stroke()` | Stroke (outline) the current or given path with the current stroke style. | None. | `self`: The `ctx` object. | +| `save()` | Save the entire state of the canvas by pushing the current state onto a stack. | None. | `self`: The `ctx` object. | +| `restore()` | Restore the most recently saved canvas state by popping the top entry in the drawing state stack. If there is no saved state, this method does nothing. | None. | `self`: The `ctx` object. | +| `fill()` | Fill the current or given path. | None. | `self`: The `ctx` object. | +| `radial_gradient(x0, y0, r0, x1, y1, r1)` | Create a radial gradient using the size and coordinates of two circles. | | `self`: The `ctx` object. | +| `linear_gradient(x0, y0, x1, y1)` | Create a gradient along the line connecting two given coordinates. | | `self`: The `ctx` object. | +| `add_stop(pos, color, alpha)` | Adds a color stop in a gradient with the provided position, color, and alpha values. | | `self`: The `ctx` object. | +| `begin_path()` | Start a new path by emptying the list of sub-paths. Call this method when you want to create a new path. | None. | `self`: The `ctx` object. | +| `arc(x, y, radius, arc_from, arc_to, direction)` | Add a circular arc to the current sub-path. | | `self`: The `ctx` object. | +| `text_width(text)` | Calculate the width of the provided text. | `text`: The text to calculate the width of. | `width`: The width of the provided text. | +| `clip()` | Turn the current or given path into the current clipping region. The previous clipping region, if any, is intersected with the current or given path to create the new clipping region. For a visual example, see the [mdn web docs](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/clip). | None. | `self`: The `ctx` object. | +| `get_font_name(i)` | Get the name of one of the 9 fonts: `"Arimo Regular"`, `"Arimo Bold"`, `"Arimo Italic"`, `"Arimo Bold Italic"`, `"Camp Font 1"`, `"Camp Font 2"`, `"Camp Font 3"`, `"Material Icons"`, and `"Comic Mono"`. | `i`: The number of the font to get the name of. A number between `0` and `8`. | `font_name`: The font. | diff --git a/docs/tildagon-apps/reference/eventbus.md b/docs/tildagon-apps/reference/eventbus.md index c8fd383..4508096 100644 --- a/docs/tildagon-apps/reference/eventbus.md +++ b/docs/tildagon-apps/reference/eventbus.md @@ -77,10 +77,11 @@ You can see a more comprehensive example in [`dialog.py`](https://github.com/emf You can use the following methods on the `eventbus`: -| Method | Description | Arguments | Returns | -| ------------------------------------------ | -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -| `on(event_type, event_handler, app)` | Register an event for an app alongside the synchronous handler to be called when the event fires. | | None | -| `on_async(event_type, event_handler, app)` | Register an event for an app alongside the asynchronous handler to be called when the event fires. | | None | -| `emit(event)` | Emit an event to the eventbus. The handler for the event must be synchronous. | `event` : The event, for example `ButtonDownEvent`. An `event` object must have the methods `__init__()` and `__str__()`. | None | -| `emit_async(event)` | Emit an event to the eventbus. The handler for the event must be asynchronous. | `event` : The event, for example `ButtonDownEvent`. An `event` object must have the methods `__init__()` and `__str__()`. | None | -| `remove(event_type, event_handler, app)` | Remove the event for an app from the eventbus. | | None | + +| Method | Description | Arguments | Returns | +| ------ | ----------- | --------- | ------- | +| `on(event_type, event_handler, app)` | Register an event for an app alongside the synchronous handler to be called when the event fires. | | None | +| `on_async(event_type, event_handler, app)` | Register an event for an app alongside the asynchronous handler to be called when the event fires. | | None | +| `emit(event)` | Emit an event to the eventbus. The handler for the event must be synchronous. | `event` : The event, for example `ButtonDownEvent`. An `event` object must have the methods `__init__()` and `__str__()`. | None | +| `emit_async(event)` | Emit an event to the eventbus. The handler for the event must be asynchronous. | `event` : The event, for example `ButtonDownEvent`. An `event` object must have the methods `__init__()` and `__str__()`. | None | +| `remove(event_type, event_handler, app)` | Remove the event for an app from the eventbus. | | None | diff --git a/docs/tildagon-apps/reference/ui-elements.md b/docs/tildagon-apps/reference/ui-elements.md index 391ab5b..f5380ef 100644 --- a/docs/tildagon-apps/reference/ui-elements.md +++ b/docs/tildagon-apps/reference/ui-elements.md @@ -14,6 +14,7 @@ from app_components import Menu, Notification, clear_background main_menu_items = ["menu_item1", "menu_item2", "menu_item3"] + class MenuDemo(App): def __init__(self): self.menu = Menu( @@ -41,6 +42,7 @@ class MenuDemo(App): if self.notification: self.notification.update(delta) + __app_export__ = MenuDemo ``` @@ -75,18 +77,18 @@ To use a menu: To initialize the Menu use the following parameters: - | Parameter | Type | Description | - | ------------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | `app` | `App` | The app to add the menu to. | - | `menu_items` | `[str]` | A list of strings containing the menu items. | - | `select_handler` | `method` | The method to be called when an item is selected and the confirm button is pressed. | - | `back_handler` | `method` | The method to be called when the cancel button is pressed. | - | `position` | `int` | _Optional_. The menu position to start at. Default: `0`. | - | `speed_ms` | `int` | _Optional_. The speed to redraw the UI at. Default: `300`. | - | `item_font_size` | `float` | _Optional_. The item line height. Default: [`tokens.ten_pt`](https://github.com/emfcamp/badge-2024-software/blob/main/modules/app_components/tokens.py). | - | `item_line_height` | `float` | _Optional_. The item line height. Default: [`tokens.label_font_size * tokens.line_height`](https://github.com/emfcamp/badge-2024-software/blob/main/modules/app_components/tokens.py). | - | `focused_item_font_size` | `float` | _Optional_. The font size of the focused item. Default: [`tokens.heading_font_size`](https://github.com/emfcamp/badge-2024-software/blob/main/modules/app_components/tokens.py). | - | `focused_item_margin` | `float` | _Optional_. The margin on the focused item. Default: `20`. | + | Parameter | Type | Description | + | --------- | ---- | ----------- | + | `app` | `App` | The app to add the menu to. | + | `menu_items` | `[str]` | A list of strings containing the menu items. | + | `select_handler` | `method` | The method to be called when an item is selected and the confirm button is pressed. | + | `back_handler` | `method` | The method to be called when the cancel button is pressed. | + | `position` | `int` | _Optional_. The menu position to start at. Default: `0`. | + | `speed_ms` | `int` | _Optional_. The speed to redraw the UI at. Default: `300`. | + | `item_font_size` | `float` | _Optional_. The item line height. Default: [`tokens.ten_pt`](https://github.com/emfcamp/badge-2024-software/blob/main/modules/app_components/tokens.py). | + | `item_line_height` | `float` | _Optional_. The item line height. Default: [`tokens.label_font_size * tokens.line_height`](https://github.com/emfcamp/badge-2024-software/blob/main/modules/app_components/tokens.py). | + | `focused_item_font_size` | `float` | _Optional_. The font size of the focused item. Default: [`tokens.heading_font_size`](https://github.com/emfcamp/badge-2024-software/blob/main/modules/app_components/tokens.py). | + | `focused_item_margin` | `float` | _Optional_. The margin on the focused item. Default: `20`. | 4. If you are creating a multi-layered menu, also create a variable like `current_menu` in the `__init__` method of your app to store the users menu state: @@ -126,12 +128,13 @@ To use a menu: You can use the following methods on a `Menu` object: -| Method | Description | Parameter | Returns | -| ---------------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ------- | -| `up_handler()` | Manually moves you up one position in the menu. | None | None | -| `down_handler()` | Manually moves you down one position in the menu. | None | None | -| `update(delta)` | Update the menu as animations are happening. You need to call this method in your app's `update()` method. | `delta`: Time difference between the last update call and the current update call. | None | -| `draw(ctx)` | Add the menu to the screen. You need to call this method in your app's `draw()` method. | `ctx`: The canvas that let's you add graphics or texts. See [`ctx` library](../reference/ctx.md). | None | + +| Method | Description | Arguments | Returns | +| ------ | ----------- | --------- | ------- | +| `up_handler()` | Manually moves you up one position in the menu. | None | None | +| `down_handler()` | Manually moves you down one position in the menu. | None | None | +| `update(delta)` | Update the menu as animations are happening. You need to call this method in your app's `update()` method. | `delta`: Time difference between the last update call and the current update call. | None | +| `draw(ctx)` | Add the menu to the screen. You need to call this method in your app's `draw()` method. | `ctx`: The canvas that let's you add graphics or texts. See [`ctx` library](../reference/ctx.md). | None | ## Notification @@ -173,6 +176,7 @@ class NotificationDemo(App): if self.notification: self.notification.draw(ctx) + __app_export__ = NotificationDemo ``` @@ -200,11 +204,11 @@ To use a notification: `Notification()` supports the following parameters: - | Parameter | Type | Description | - | --------- | --------- | -------------------------------------------------------------------------- | - | `message` | `str` | The notification message. | - | `port` | `int` | _Optional_. The port from which the notification was issued. Default: `0`. | - | `open` | `boolean` | _Optional_. Whether to open the notification. Default: `True`. | + | Parameter | Type | Description | + | --------- | ---- | ----------- | + | `message` | `str` | The notification message. | + | `port` | `int` | _Optional_. The port from which the notification was issued. Default: `0`. | + | `open` | `boolean` | _Optional_. Whether to open the notification. Default: `True`. | 4. Add the following lines in your `draw()` method to draw the notification when `self.notification` contains a notification: @@ -226,12 +230,13 @@ To use a notification: You can use the following methods on a `Notification` object: -| Method | Description | Arguments | Returns | -| --------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ------- | -| `open()` | Manually open the notification. | None | None | -| `close()` | Manually close the notification. | None | None | -| `update(delta)` | Automatically display the notification for a period of 5 seconds. You need to call this method in your app's `update()` method. | `delta`: Time difference between the last update call and the current update call. | None | -| `draw(ctx)` | Add the notification to the screen. You need to call this method in your app's `draw()` method. | `ctx`: The canvas that let's you add graphics or texts. See [`ctx` library](../reference/ctx.md). | None | + +| Method | Description | Arguments | Returns | +| ------ | ----------- | --------- | ------- | +| `open()` | Manually open the notification. | None | None | +| `close()` | Manually close the notification. | None | None | +| `update(delta)` | Automatically display the notification for a period of 5 seconds. You need to call this method in your app's `update()` method. | `delta`: Time difference between the last update call and the current update call. | None | +| `draw(ctx)` | Add the notification to the screen. You need to call this method in your app's `draw()` method. | `ctx`: The canvas that let's you add graphics or texts. See [`ctx` library](../reference/ctx.md). | None | ## Yes/No Dialog @@ -282,8 +287,8 @@ The [`YesNoDialog`](https://github.com/emfcamp/badge-2024-software/blob/main/mod ctx.save() if self.answer: - ctx.rgb(0,0,0.2).rectangle(-120,-120,240,240).fill() - ctx.rgb(0,0,1).move_to(-80,0).text(self.answer) + ctx.rgb(0, 0, 0.2).rectangle(-120, -120, 240, 240).fill() + ctx.rgb(0, 0, 1).move_to(-80, 0).text(self.answer) ctx.restore() if self.dialog: @@ -331,8 +336,8 @@ The [`YesNoDialog`](https://github.com/emfcamp/badge-2024-software/blob/main/mod clear_background(ctx) if self.answer: ctx.save() - ctx.rgb(0,0,0.2).rectangle(-120,-120,240,240).fill() - ctx.rgb(0,0,1).move_to(-80,0).text(self.answer) + ctx.rgb(0, 0, 0.2).rectangle(-120, -120, 240, 240).fill() + ctx.rgb(0, 0, 1).move_to(-80, 0).text(self.answer) ctx.restore() self.draw_overlays(ctx) @@ -380,7 +385,8 @@ To use the Yes/No dialog: # Create a yes/no dialogue, add it to the overlays dialog = YesNoDialog("Is it a happy day?", self) self.overlays = [dialog] - # Wait for an answer from the dialogue, and if it was yes, do something + # Wait for an answer from the dialogue, and if it was yes, do + # something if await dialog.run(render_update): # this sets a variable that can be used in the draw method self.answer = "I'm sorry" @@ -394,12 +400,12 @@ To use the Yes/No dialog: `YesNoDialog()` supports the following parameters: -| Parameter | Type | Description | -| --------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- | -| `message` | `str` | The dialog message. | -| `app` | `App` | The app opening the dialog. | -| `on_yes` | `method` | _Optional_. Call the provided handler method or return `True` if answer is yes and no handler is provided. Default: `None`. | -| `on_no` | `method` | _Optional_. Call the provided handler method or return `False` if answer is yes and no handler is provided. Default: `None`. | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `message` | `str` | The dialog message. | +| `app` | `App` | The app opening the dialog. | +| `on_yes` | `method` | _Optional_. Call the provided handler method or return `True` if answer is yes and no handler is provided. Default: `None`. | +| `on_no` | `method` | _Optional_. Call the provided handler method or return `False` if answer is yes and no handler is provided. Default: `None`. | === "Synchronous" @@ -427,11 +433,12 @@ To make the dialog's answers have an effect you need to do something based on th You can use the following methods on a `YesNoDialog` object: -| Method | Description | Arguments | Returns | -| -------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------- | -| `run(render_update)` | Asynchronous. Open the dialog. You need to call this method to display the dialog. | `render_update`: The method that triggers a `draw()` call when updates are complete. | `True` or `False` | -| `draw_message(ctx)` | Helper method to add your message to the screen. This method is called by the `draw()` method. | `ctx`: The canvas that let's you add graphics or texts. See [`ctx` library](../reference/ctx.md). | None | -| `draw(ctx)` | Add the dialog to the screen. You need to call this method in your app's `draw()` method. | `ctx`: The canvas that let's you add graphics or texts. See [`ctx` library](../reference/ctx.md). | None | + +| Method | Description | Arguments | Returns | +| ------ | ----------- | --------- | ------- | +| `run(render_update)` | Asynchronous. Open the dialog. You need to call this method to display the dialog. | `render_update`: The method that triggers a `draw()` call when updates are complete. | `True` or `False` | +| `draw_message(ctx)` | Helper method to add your message to the screen. This method is called by the `draw()` method. | `ctx`: The canvas that let's you add graphics or texts. See [`ctx` library](../reference/ctx.md). | None | +| `draw(ctx)` | Add the dialog to the screen. You need to call this method in your app's `draw()` method. | `ctx`: The canvas that let's you add graphics or texts. See [`ctx` library](../reference/ctx.md). | None | ## Text Dialog @@ -585,13 +592,13 @@ To use the text dialog: `TextDialog()` supports the following parameters: -| Parameter | Type | Description | -| ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -| `message` | `str` | The dialog message. | -| `app` | `App` | The app opening the dialog. | -| `masked` | `bool` | _Optional_. Whether to obscure the text buffer with asterisks (for example, for passwords). Default: `False`. | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `message` | `str` | The dialog message. | +| `app` | `App` | The app opening the dialog. | +| `masked` | `bool` | _Optional_. Whether to obscure the text buffer with asterisks (for example, for passwords). Default: `False`. | | `on_complete` | `method` | _Optional_. Call the provided handler method or return the text entry if the text entry is confirmed and no handler is provided. Default: `None`. | -| `on_cancel` | `method` | _Optional_. Call the provided handler method or return `False` if answer is cancelled and no handler is provided. Default: `None`. | +| `on_cancel` | `method` | _Optional_. Call the provided handler method or return `False` if answer is cancelled and no handler is provided. Default: `None`. | === "Synchronous" @@ -619,11 +626,12 @@ To make the dialog's answer have an effect you need to do something with the ans You can use the following methods on a `TextDialog` object: -| Method | Description | Arguments | Returns | -| -------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------- | -| `run(render_update)` | Asynchronous. Open the dialog. You need to call this method to display the dialog. | `render_update`: The method that triggers a `draw()` call when updates are complete. | `True` or `False` | -| `draw_message(ctx)` | Helper method to add your message to the screen. This method is called by the `draw()` method. | `ctx`: The canvas that let's you add graphics or texts. See [`ctx` library](../reference/ctx.md). | None | -| `draw(ctx)` | Add the dialog to the screen. You need to call this method in your app's `draw()` method. | `ctx`: The canvas that let's you add graphics or texts. See [`ctx` library](../reference/ctx.md). | None | + +| Method | Description | Arguments | Returns | +| ------ | ----------- | --------- | ------- | +| `run(render_update)` | Asynchronous. Open the dialog. You need to call this method to display the dialog. | `render_update`: The method that triggers a `draw()` call when updates are complete. | `True` or `False` | +| `draw_message(ctx)` | Helper method to add your message to the screen. This method is called by the `draw()` method. | `ctx`: The canvas that let's you add graphics or texts. See [`ctx` library](../reference/ctx.md). | None | +| `draw(ctx)` | Add the dialog to the screen. You need to call this method in your app's `draw()` method. | `ctx`: The canvas that let's you add graphics or texts. See [`ctx` library](../reference/ctx.md). | None | ## Layouts @@ -645,11 +653,20 @@ This example creates what could be a small game menu with three parts: import app from app_components import clear_background, TextDialog -from app_components.layout import TextDisplay, ButtonDisplay, DefinitionDisplay, LinearLayout +from app_components.layout import \ + TextDisplay, ButtonDisplay, DefinitionDisplay, LinearLayout from events.input import BUTTON_TYPES, ButtonDownEvent from system.eventbus import eventbus DIFFICULTY_VALUES = ["easy", "normal", "hard"] +LOREM = """Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do\ + eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim\ + veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea\ + commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit\ + esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat \ + cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est\ + laborum.""" + def string_formatter(value): if value is None: @@ -657,19 +674,25 @@ def string_formatter(value): else: return str(value) + class LayoutMenuDemo(app.App): def __init__(self): self.layout = LinearLayout(items=[DefinitionDisplay("", "")]) self.dialog = None self.options = { - ("text_setting", "Player Name", string_formatter, self.string_editor), + ( + "text_setting", + "Player Name", + string_formatter, + self.string_editor + ), ("button_selector", "Difficulty selector", string_formatter, None), ("text", "Game Instructions", string_formatter, None), } self.app_settings = { "text_setting": "Naomi", "button_selector": "easy", - "instructions": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." + "instructions": LOREM } eventbus.on_async(ButtonDownEvent, self._button_handler, self) @@ -699,12 +722,13 @@ class LayoutMenuDemo(app.App): self.layout.items = [] for id, label, formatter, editor in self.options: if id in self.app_settings.keys(): - value = self.app_settings[id - ] + value = self.app_settings[id] else: - value = "" + value = "" + if editor: - async def _button_event(event, label=label, id=id, editor=editor): + async def _button_event( + event, label=label, id=id, editor=editor): if BUTTON_TYPES["CONFIRM"] in event.button: await editor(label, id, render_update) return True @@ -751,13 +775,12 @@ class LayoutMenuDemo(app.App): result = await self.dialog.run(render_update) if ( result is not False - ): #!= because we want to allow entering empty strings + ): # != because we want to allow entering empty strings self.app_settings[self.dialog._settings_id] = result self.dialog = None if result: break - def update(self, delta): return True @@ -765,6 +788,7 @@ class LayoutMenuDemo(app.App): clear_background(ctx) self.layout.draw(ctx) + __app_export__ = LayoutMenuDemo ``` @@ -801,12 +825,12 @@ To use layouts: To initialize the definition display use the following parameters: - | Parameter | Type | Description | - | --------- | ---- | ----------- | - | `label` | `str` | The text to display for the label. | - | `value` | `str` | The text to display for the definition. | - | `height` | `int` | The height at which to display the definition display. | - | `button_handler` | `method` | The handler for button events. | + | Parameter | Type | Description | + | --------- | ---- | ----------- | + | `label` | `str` | The text to display for the label. | + | `value` | `str` | The text to display for the definition. | + | `height` | `int` | The height at which to display the definition display. | + | `button_handler` | `method` | The handler for button events. | === "TextDisplay" @@ -815,17 +839,17 @@ To use layouts: Initialize the text_display in your `__init__` or in your `update` method and add it to the `self.layout.items` variable: ```python - text_display = TextDisplay("My long text", font_size=8, rgb=(0,0,50)) + text_display = TextDisplay("My long text", font_size=8, rgb=(0, 0, 50)) self.layout.items.append(text_display) ``` To initialize the `TextDisplay` use the ?following parameters: - | Parameter | Type | Description | - | --------- | ---- | ----------- | - | `text` | `str` | The long text to display. | - | `font_size` | `int` | The font size to display the text in. | - | `rgb` | `tuple` | The color to display the text in. | + | Parameter | Type | Description | + | --------- | ---- | ----------- | + | `text` | `str` | The long text to display. | + | `font_size` | `int` | The font size to display the text in. | + | `rgb` | `tuple` | The color to display the text in. | === "ButtonDisplay" @@ -838,20 +862,20 @@ To use layouts: text="Select me", app=self, font_size=8, - rgb=(50,0,0), + rgb=(50, 0, 0), button_handler=self.select_handler) self.layout.items.append(button_display) ``` To initialize the button display use the following parameters: - | Parameter | Type | Description | - | --------- | ---- | ----------- | - | `text` | `str` | The long text to display. | - | `app` | `App` | The app to add the button display to. | - | `font_size` | `int` | The font size to display the text in. | - | `rgb` | `tuple` | The color to display the text in. | - | `button_handler` | `method` | The handler for button events. | + | Parameter | Type | Description | + | --------- | ---- | ----------- | + | `text` | `str` | The long text to display. | + | `app` | `App` | The app to add the button display to. | + | `font_size` | `int` | The font size to display the text in. | + | `rgb` | `tuple` | The color to display the text in. | + | `button_handler` | `method` | The handler for button events. | Create an asynchronous `select_handler` that does something when a button is pressed: @@ -875,10 +899,11 @@ The [`Tokens`](https://github.com/emfcamp/badge-2024-software/blob/main/modules/ ### Functions -| Method | Description | Arguments | Returns | -| ----------------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -| `clear_background(ctx)` | Clear the badge background. | `ctx`: The canvas that let's you add graphics or texts. See [`ctx` library](../reference/ctx.md). | None | -| `set_color(ctx, color)` | Set the color for the canvas. | | None | + +| Method | Description | Arguments | Returns | +| ------ | ----------- | --------- | ------- | +| `clear_background(ctx)` | Clear the badge background. | `ctx`: The canvas that let's you add graphics or texts. See [`ctx` library](../reference/ctx.md). | None | +| `set_color(ctx, color)` | Set the color for the canvas. | | None | #### Usage diff --git a/docs/tildagon-apps/simple_tildagon.md b/docs/tildagon-apps/simple_tildagon.md index d700f24..93dc6fc 100644 --- a/docs/tildagon-apps/simple_tildagon.md +++ b/docs/tildagon-apps/simple_tildagon.md @@ -104,7 +104,7 @@ while True: # is_tilted_foward, is_tilted_back if st.imu.is_tilted_left(): print("Left!") - time.sleep(1) # Only checking every second + time.sleep(1) # Only checking every second ``` There is also a check for shaking using `st.imu.is_shaken()`. @@ -131,10 +131,11 @@ import time while True: - # Options include is_tilted_left, is_tilted_right, is_tilted_foward, is_tilted_back + # Options include is_tilted_left, is_tilted_right, is_tilted_foward, + # is_tilted_back if st.button.get("A"): print("Button A is being pressed!") - time.sleep(1) # Only checking every second + time.sleep(1) # Only checking every second ``` !!! warning "Multiple loops" diff --git a/docs/tildagon-apps/simulate.md b/docs/tildagon-apps/simulate.md index 6ba24d6..eb34164 100644 --- a/docs/tildagon-apps/simulate.md +++ b/docs/tildagon-apps/simulate.md @@ -41,8 +41,8 @@ The badge simulator simulates all apps in the [`sim/apps/`](https://github.com/e def draw(self, ctx): ctx.save() - ctx.rgb(0.2,0,0).rectangle(-120,-120,240,240).fill() - ctx.rgb(1,0,0).move_to(-80,0).text("Hello world") + ctx.rgb(0.2, 0, 0).rectangle(-120, -120, 240, 240).fill() + ctx.rgb(1, 0, 0).move_to(-80, 0).text("Hello world") ctx.restore() __app_export__ = ExampleApp