feat: menu demo

This commit is contained in:
xGlc
2026-05-10 13:03:24 +02:00
parent f852c1a2b5
commit 2bbe5ae0e8
4 changed files with 158 additions and 55 deletions

View File

@@ -5,6 +5,7 @@ from time import sleep, time_ns
from machine import SPI, Pin
from encoder import Encoder
from ili9341 import Display, color565
colors = {
@@ -18,6 +19,12 @@ colors = {
"NAVY": (0, 0, 128),
}
menu = [
"Option 1",
"Option 2",
"Option 3",
]
def display():
bg = Pin(33, Pin.OUT)
@@ -26,70 +33,80 @@ def display():
bg.on()
while True:
encoder_a = Pin(2, Pin.IN, Pin.PULL_UP)
encoder_b = Pin(4, Pin.IN, Pin.PULL_UP)
def draw_debug_text(text: str, y: int):
display.draw_text8x8(
10,
10,
"ILI9341 Display Test",
y,
text,
color565(255, 255, 255),
color565(0, 0, 0),
270,
)
display.draw_text8x8(
10,
280,
"PPAP",
color565(255, 255, 255),
color565(0, 0, 0),
270,
)
sleep(1)
# Calculate valid hlines parameters for display clear method
valid_hlines = []
for i in range(1, display.height):
if display.height % i == 0:
valid_hlines.append(i)
# Ensure only 13 entries, truncate or repeat the last one
valid_hlines = valid_hlines[:8]
if len(valid_hlines) < 8:
valid_hlines += [valid_hlines[-1]] * (8 - len(valid_hlines))
# Ensure only 13 entries, truncate or repeat the last one
valid_hlines = valid_hlines[:8]
if len(valid_hlines) < 8:
valid_hlines += [valid_hlines[-1]] * (8 - len(valid_hlines))
print("Clearing to black...")
start = time_ns()
display.clear()
end = time_ns()
print(f"Display cleared in {end - start} ms.")
sleep(2)
last_registered: int = time_ns()
print("Clearing to white...")
start = time_ns()
display.clear(color565(255, 255, 255))
end = time_ns()
print(f"Display cleared in {end - start} ms.")
sleep(2)
menu_index = 0
for hlines, (color, rgb) in zip(valid_hlines, colors.items()):
def handle_button_pres(dir: str):
nonlocal last_registered
nonlocal menu_index
if (time_ns() - last_registered) < 100000000:
return
last_registered = time_ns()
if dir == "Left":
menu_index = (menu_index - 1) % 3
elif dir == "Right":
menu_index = (menu_index + 1) % 3
else:
return
refresh_display()
encoder = Encoder(
encoder_a,
encoder_b,
on_left=lambda x: handle_button_pres("Left"),
on_right=lambda x: handle_button_pres("Right"),
)
display.clear(color=color565(*colors["BLUE"]))
display.draw_text8x8(
10,
10,
"Menu:",
color565(255, 255, 255),
color565(0, 0, 0),
)
display.draw_text8x8(
230 - (8 * 4),
10,
"PPAP",
color565(255, 255, 255),
color565(0, 0, 0),
)
def get_cursor(line: int):
nonlocal menu_index
if line == menu_index:
return "> "
else:
return " "
def refresh_display():
for i, line in enumerate(menu):
display.draw_text8x8(
10,
10,
f"Next color: {color}",
18 + (i + 1) * 8,
get_cursor(i) + line,
color565(255, 255, 255),
color565(0, 0, 0),
270,
)
sleep(1)
print(f"Clearing display to {color}, hlines={hlines}...")
try:
start = time_ns()
display.clear(hlines=hlines, color=color565(*rgb))
end = time_ns()
print(f"Display cleared in {end - start} ms.")
except Exception as e:
print(e)
sleep(1)
sleep(5)
refresh_display()

10
encoder.py Normal file
View File

@@ -0,0 +1,10 @@
from machine import Pin
class Encoder:
def __init__(self, pin_a: Pin, pin_b: Pin, on_left, on_right) -> None:
self.pin_a = pin_a
self.pin_b = pin_b
pin_a.irq(trigger=Pin.IRQ_RISING, handler=on_left)
pin_b.irq(trigger=Pin.IRQ_RISING, handler=on_right)
pass

View File

@@ -13,5 +13,5 @@ import led_program
import motor
# _thread.start_new_thread(led_program.boostrap, ())
# _thread.start_new_thread(display_program.display, ())
_thread.start_new_thread(motor.motor_run, ())
_thread.start_new_thread(display_program.display, ())
# _thread.start_new_thread(motor.motor_run, ())

76
menu.py Normal file
View File

@@ -0,0 +1,76 @@
from this import d
from ili9341 import Display, color565
class MenuItem:
def __init__(self, name: str, navigate: str):
self.name = name
self.navigate = navigate
class MenuPage:
UP = 0
DOWN = 1
def __init__(self, title: str, item: list[MenuItem]):
self.title = title
self.items = item
self.items.append(MenuItem("Back", "Root"))
self.cursor = 0
def size(self) -> int:
return len(self.items)
def move(self, direction: int):
match direction:
case self.UP:
self.cursor = (self.cursor + 1) % self.size()
case self.DOWN:
self.cursor = (self.cursor - 1) % self.size()
def draw(self) -> str:
lines: list[str] = []
for i, item in enumerate(self.items):
if i == self.cursor:
lines.append("> {item.name}\n")
else:
lines.append(" {item.name}\n")
return "\n".join(lines)
def current_at_cursor(self, cursor: int) -> MenuItem:
return self.items[cursor]
class DisplayMenu:
def __init__(self, display: Display, pages: list[MenuPage]):
self.display = display
self.pages = pages
self.cursor = 0
self.current_page = self.pages[0]
self.update()
def __draw_display_line(self, line: str, offset: int):
self.display.draw_text8x8(
10,
10 + (offset * 8),
line,
color565(255, 255, 255),
color565(0, 0, 0),
)
def move_up(self):
self.current_page.move(MenuPage.UP)
def move_down(self):
self.current_page.move(MenuPage.DOWN)
def select(self):
pass
def update(self):
self.__draw_display_line(self.current_page.title, 0)
self.__draw_display_line(self.current_page.draw(), 2)