Skip to content

Commit 0331575

Browse files
committed
Rewrite the Tufty2040 main menu somewhat
Primarily motivated to get almost everything out of globals and fix issue #631. This is a stripped-down version of the gist there without the memory debugging and reboot-into-direct-boot mode. Mostly move things to functions. Clean up some dead code and comments. I think the biggest win is not actually the PicoGraphics framebuffer, which is well-behaved, but random temporary floats that were leaking previously. Also type-annotated and lints clean. Fixes #631. (As much as I think is possible.)
1 parent de8cb95 commit 0331575

File tree

1 file changed

+76
-72
lines changed
  • micropython/examples/tufty2040

1 file changed

+76
-72
lines changed
Lines changed: 76 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
from picographics import PicoGraphics, DISPLAY_TUFTY_2040, PEN_RGB332
2-
from os import listdir
3-
import time
1+
# Tufty2040 boot menu/loader.
2+
43
import gc
4+
import time
5+
from os import listdir
6+
from picographics import PicoGraphics, DISPLAY_TUFTY_2040, PEN_RGB332
57
from pimoroni import Button
68

7-
display = PicoGraphics(display=DISPLAY_TUFTY_2040, pen_type=PEN_RGB332, rotate=180)
8-
99

10-
def hsv_to_rgb(h, s, v):
10+
def hsv_to_rgb(h: float, s: float, v: float) -> tuple[float, float, float]:
1111
if s == 0.0:
1212
return v, v, v
1313
i = int(h * 6.0)
@@ -34,7 +34,7 @@ def hsv_to_rgb(h, s, v):
3434
return v, p, q
3535

3636

37-
def get_applications():
37+
def get_applications() -> list[dict[str, str]]:
3838
# fetch a list of the applications that are stored in the filesystem
3939
applications = []
4040
for file in listdir():
@@ -54,98 +54,102 @@ def get_applications():
5454
return sorted(applications, key=lambda x: x["title"])
5555

5656

57-
def launch_application(application):
57+
def prepare_for_launch() -> None:
5858
for k in locals().keys():
59-
if k not in ("gc", "file", "badger_os"):
59+
if k not in ("__name__",
60+
"application_file_to_launch",
61+
"gc"):
6062
del locals()[k]
61-
6263
gc.collect()
6364

64-
__import__(application["file"])
65-
66-
67-
applications = get_applications()
68-
69-
button_up = Button(22, invert=False)
70-
button_down = Button(6, invert=False)
71-
button_a = Button(7, invert=False)
7265

73-
display.set_backlight(1.0)
66+
def menu() -> str:
67+
applications = get_applications()
7468

75-
WHITE = display.create_pen(255, 255, 255)
76-
BLACK = display.create_pen(0, 0, 0)
77-
RED = display.create_pen(200, 0, 0)
69+
button_up = Button(22, invert=False)
70+
button_down = Button(6, invert=False)
71+
button_a = Button(7, invert=False)
7872

73+
display = PicoGraphics(display=DISPLAY_TUFTY_2040, pen_type=PEN_RGB332)
74+
display.set_backlight(1.0)
7975

80-
def text(text, x, y, pen, s):
81-
display.set_pen(pen)
82-
display.text(text, x, y, -1, s)
76+
selected_item = 2
77+
scroll_position = 2
78+
target_scroll_position = 2
8379

80+
selected_pen = display.create_pen(255, 255, 255)
81+
unselected_pen = display.create_pen(80, 80, 100)
82+
background_pen = display.create_pen(50, 50, 70)
83+
shadow_pen = display.create_pen(0, 0, 0)
8484

85-
selected_item = 2
86-
scroll_position = 2
87-
target_scroll_position = 2
85+
while True:
86+
t = time.ticks_ms() / 1000.0
8887

89-
selected_pen = display.create_pen(255, 255, 255)
90-
unselected_pen = display.create_pen(80, 80, 100)
91-
background_pen = display.create_pen(50, 50, 70)
92-
shadow_pen = display.create_pen(0, 0, 0)
88+
if button_up.read():
89+
target_scroll_position -= 1
90+
target_scroll_position = target_scroll_position if target_scroll_position >= 0 else len(applications) - 1
9391

94-
while True:
95-
t = time.ticks_ms() / 1000.0
92+
if button_down.read():
93+
target_scroll_position += 1
94+
target_scroll_position = target_scroll_position if target_scroll_position < len(applications) else 0
9695

97-
if button_up.read():
98-
target_scroll_position -= 1
99-
target_scroll_position = target_scroll_position if target_scroll_position >= 0 else len(applications) - 1
96+
if button_a.read():
97+
# Wait for the button to be released.
98+
while button_a.is_pressed:
99+
time.sleep(0.01)
100100

101-
if button_down.read():
102-
target_scroll_position += 1
103-
target_scroll_position = target_scroll_position if target_scroll_position < len(applications) else 0
101+
return applications[selected_item]["file"]
104102

105-
if button_a.read():
106-
launch_application(applications[selected_item])
103+
display.set_pen(background_pen)
104+
display.clear()
107105

108-
display.set_pen(background_pen)
109-
display.clear()
106+
scroll_position += (target_scroll_position - scroll_position) / 5
110107

111-
scroll_position += (target_scroll_position - scroll_position) / 5
108+
grid_size = 40
109+
for y in range(0, 240 // grid_size):
110+
for x in range(0, 320 // grid_size):
111+
h = x + y + int(t * 5)
112+
h = h / 50.0
113+
r, g, b = hsv_to_rgb(h, 0.5, 1)
112114

113-
grid_size = 40
114-
for y in range(0, 240 / grid_size):
115-
for x in range(0, 320 / grid_size):
116-
h = x + y + int(t * 5)
117-
h = h / 50.0
118-
r, g, b = hsv_to_rgb(h, .5, 1)
115+
display.set_pen(display.create_pen(r, g, b))
116+
display.rectangle(x * grid_size, y * grid_size, grid_size, grid_size)
119117

120-
display.set_pen(display.create_pen(r, g, b))
121-
display.rectangle(x * grid_size, y * grid_size, grid_size, grid_size)
118+
# work out which item is selected (closest to the current scroll position)
119+
selected_item = round(target_scroll_position)
122120

123-
# work out which item is selected (closest to the current scroll position)
124-
selected_item = round(target_scroll_position)
121+
for list_index, application in enumerate(applications):
122+
distance = list_index - scroll_position
125123

126-
start = time.ticks_ms()
124+
text_size = 4 if selected_item == list_index else 3
127125

128-
for list_index, application in enumerate(applications):
129-
distance = list_index - scroll_position
126+
# center text horixontally
127+
title_width = display.measure_text(application["title"], text_size)
128+
text_x = int(160 - title_width / 2)
130129

131-
text_size = 4 if selected_item == list_index else 3
130+
row_height = text_size * 5 + 20
132131

133-
# center text horixontally
134-
title_width = display.measure_text(application["title"], text_size)
135-
text_x = int(160 - title_width / 2)
132+
# center list items vertically
133+
text_y = int(120 + distance * row_height - (row_height / 2))
136134

137-
row_height = text_size * 5 + 20
135+
# draw the text, selected item brightest and with shadow
136+
if selected_item == list_index:
137+
display.set_pen(shadow_pen)
138+
display.text(application["title"], text_x + 1, text_y + 1, -1, text_size)
138139

139-
# center list items vertically
140-
text_y = int(120 + distance * row_height - (row_height / 2))
140+
text_pen = selected_pen if selected_item == list_index else unselected_pen
141+
display.set_pen(text_pen)
142+
display.text(application["title"], text_x, text_y, -1, text_size)
141143

142-
# draw the text, selected item brightest and with shadow
143-
if selected_item == list_index:
144-
text(application["title"], text_x + 1, text_y + 1, shadow_pen, text_size)
144+
display.update()
145145

146-
text_pen = selected_pen if selected_item == list_index else unselected_pen
147-
text(application["title"], text_x, text_y, text_pen, text_size)
148146

149-
start = time.ticks_ms()
147+
# The application we will be launching. This should be ouronly global, so we can
148+
# drop everything else.
149+
application_file_to_launch = menu()
150150

151-
display.update()
151+
# Run whatever we've set up to.
152+
# If this fails, we'll exit the script and drop to the REPL, which is
153+
# fairly reasonable.
154+
prepare_for_launch()
155+
__import__(application_file_to_launch)

0 commit comments

Comments
 (0)