Somewhat related to #13 and web_components.py, I want to improve shared logic to write to the LED. This is one of the most cumbersome parts when you write an app but in theory you should only need to implement this once. We do have pprint function but it's a bit tricky to use and have a lot of quirks.
A lot of companion functions live in odd namespaces (e.g. pprint in load_screen) and are marked as private (prefixed with _).
I though a bit about this when I rewrote wlan.py and created show_setup_on_led.
|
def show_setup_on_led(): |
|
# Show wifi name and device IP for setup instructions. |
|
# One space per pixel so name and IP align. |
|
pprint("1. Connect to WiFi", line=1) |
|
pprint(f" {macid}", line=2, color="yellow") |
|
pprint("2. Go to", line=3) |
|
pprint(f" http://{wifi.radio.ipv4_address_ap!s}", line=4) |
The idea here being that no one should ever have to figure out how to print this to the LED, it should just be a call from any view or any app at any time.
We recently added _wrap_text which is more or less a copy from the app "dictaphone" but I think we did the same thing here. It's in an odd namespace (cmd, makes no sense since it's not related to cmd and would be odd to re-use e.g. from wlan) and it's prefixed with _ indicating it's private and shouldn't be used outside of the namespace.
Since this is private and a bit tricky to use, it's now forced and monkey patched when a user uses the /system/cmd endpoint. I think it would be better to leave that to the end user to decide, and with a good enough library it would be super easy to wrap the text when desired.
I suggest we build a good library for the LED display in a clear namespace (e.g. led.py) which holds all the logic on how to draw to the screen so anyone can use them from any app. Most of the time it's text we want to render so we should start with that.
I want to build a system that looks something like this (pseudo code). I don't know if it will be too size consuming, but I'm opening this as a tracking ticket to get closer to this goal. Hopefully we can compensate by remove a lot of duplicated code. It would work a lot like pprint but be extendable with a state to also support animations. In the future we can also improve shapes etc like the "paint" app.
from enum import Enum
class Scroll(Enum):
NONE = 1
HORIZONTAL = 2
VERTICAL = 3
class ScrollStop(Enum):
LOOP = 1 # keeps scrolling in a loop
RESET = 2 # stops at the end and snaps back to start
class Font(Enum):
MINI = 1
SMALL = 2
LARGE = 3
class Color(Enum):
WHITE = "white"
BRIGHT_WHITE = "brightwhite"
RED = "red"
GREEN = "green"
BLUE = "blue"
LIGHT_BLUE = "light_blue"
YELLOW = "yellow"
ORANGE = "orange"
PINK = "pink"
GREY = "grey"
BLACK = "black"
class TextAlign(Enum):
LEFT = 1
CENTER = 2
RIGHT = 3
class Text:
def __init__(self, text, color=Color.WHITE, shadow_color=None):
self.text = text
self.color = color
self.shadow_color = shadow_color
class Printer:
PHASE_START = "start"
PHASE_SCROLL = "scroll"
PHASE_END = "end"
def __init__(self, window=None):
self.width = get_width()
self.height = get_height()
self.window=window
self.x = 0
self.y = 0
self._rows = []
def add_row(
self,
*content,
font_size=Font.MINI,
align=TextAlign.LEFT,
wrap=False,
scroll=Scroll.NONE,
scroll_stop=ScrollStop.LOOP,
scroll_speed=1,
top_offset=0,
bottom_offset=0,
start_x=None, # Override self.x state
start_y=None, # Override self.y state
clear_after=True,
):
"""
Add rows and has a shared state on where they should apply in the
given window (which might be full display).
"""
def print(self):
"""
Print rich text, keeps track of position and screen.
This means that if scroll is enabled, calling `print` multiple
times will simply update the position and simulate a scrolling
behavior for the caller without ever tracking previous position.
Will need to figure out `line` and those bits but want a system
that works for all font sizes and types.
"""
def clear():
"""Clears the whole screen making it all black"""
def draw_rect(x0, y0, x1, y1, color=Colro.WHITE, fill=None):
"""Draw a rectangle with green border and no fill"""
def draw_line(x0, y0, x1, y1, color=Colro.WHITE):
"""Draw a line from (x0,y0) to (x1,y1)"""
def draw_image(x, y, image_path):
"""Draw image `image_path` at (x, y)"""
def draw_logo():
"""Draw the MatrixBOX logo (with yellow BOX) at the first row"""
Then I'd just want to have an app which can combine a bunch of these calls to render on the LED without having to calculate offsets, x, y, spacing, scrolling and whatever, just handle it automatically with the state of the Printer.
import led
led.clear()
p = led.Printer()
p.add_row(
["Hello from ", Text("Another", color=Color.RED), " color"],
font_size=Font.LARGE,
align=TextAlign.CENTER,
scroll=Scroll.HORIZONTAL,
)
p.add_row(["This is a row ", Text("after...", shadow_color=Color.GREEN)])
p.print() # Render all rows
I know this is a very open ended ticket but I want it tracked to get the initial plan rolling and have good building bricks for future changes. The main focus should be on printing to the display because if we can sort that out it would be super easy to hook up whatever command or external requet to just fill the LED with text scrolling in any direction and any speed, potentially without even building apps for it if it's triggered externally.
Somewhat related to #13 and
web_components.py, I want to improve shared logic to write to the LED. This is one of the most cumbersome parts when you write an app but in theory you should only need to implement this once. We do havepprintfunction but it's a bit tricky to use and have a lot of quirks.A lot of companion functions live in odd namespaces (e.g. pprint in
load_screen) and are marked as private (prefixed with_).I though a bit about this when I rewrote
wlan.pyand createdshow_setup_on_led.matrixbox/lib/wlan.py
Lines 114 to 120 in d48958b
The idea here being that no one should ever have to figure out how to print this to the LED, it should just be a call from any view or any app at any time.
We recently added
_wrap_textwhich is more or less a copy from the app "dictaphone" but I think we did the same thing here. It's in an odd namespace (cmd, makes no sense since it's not related tocmdand would be odd to re-use e.g. fromwlan) and it's prefixed with_indicating it's private and shouldn't be used outside of the namespace.Since this is private and a bit tricky to use, it's now forced and monkey patched when a user uses the
/system/cmdendpoint. I think it would be better to leave that to the end user to decide, and with a good enough library it would be super easy to wrap the text when desired.I suggest we build a good library for the LED display in a clear namespace (e.g.
led.py) which holds all the logic on how to draw to the screen so anyone can use them from any app. Most of the time it's text we want to render so we should start with that.I want to build a system that looks something like this (pseudo code). I don't know if it will be too size consuming, but I'm opening this as a tracking ticket to get closer to this goal. Hopefully we can compensate by remove a lot of duplicated code. It would work a lot like
pprintbut be extendable with a state to also support animations. In the future we can also improve shapes etc like the "paint" app.Then I'd just want to have an app which can combine a bunch of these calls to render on the LED without having to calculate offsets, x, y, spacing, scrolling and whatever, just handle it automatically with the state of the
Printer.I know this is a very open ended ticket but I want it tracked to get the initial plan rolling and have good building bricks for future changes. The main focus should be on printing to the display because if we can sort that out it would be super easy to hook up whatever command or external requet to just fill the LED with text scrolling in any direction and any speed, potentially without even building apps for it if it's triggered externally.