Skip to content
Snippets Groups Projects
Verified Commit 7d32047b authored by rahix's avatar rahix
Browse files

refactor(menu.py): Use simple_menu module


Signed-off-by: default avatarRahix <rahix@rahix.de>
parent b5dfc265
Branches
Tags
No related merge requests found
...@@ -5,286 +5,91 @@ You can customize this script however you want :) If you want to go back to ...@@ -5,286 +5,91 @@ You can customize this script however you want :) If you want to go back to
the default version, just delete this file; the firmware will recreate it on the default version, just delete this file; the firmware will recreate it on
next run. next run.
""" """
import buttons import collections
import color import color
import display import display
import os import os
import utime import simple_menu
import ujson
import sys import sys
import ujson
import utime
BUTTON_TIMER_POPPED = -1 App = collections.namedtuple("App", ["name", "path"])
COLOR_BG = color.CHAOSBLUE_DARK
COLOR_BG_SEL = color.CHAOSBLUE
COLOR_ARROW = color.COMMYELLOW
COLOR_TEXT = color.COMMYELLOW
MAXCHARS = 11
HOMEAPP = "main.py"
def create_folders():
try:
os.mkdir("/apps")
except:
pass
def read_metadata(app_folder):
try:
info_file = "/apps/%s/metadata.json" % (app_folder)
with open(info_file) as f:
information = f.read()
return ujson.loads(information)
except Exception as e:
print("Failed to read metadata for %s" % (app_folder))
sys.print_exception(e)
return {
"author": "",
"name": app_folder,
"description": "",
"category": "",
"revision": 0,
}
def list_apps():
"""Create a list of available apps."""
apps = []
# add main application def enumerate_apps():
for mainFile in os.listdir("/"): """List all installed apps."""
if mainFile == HOMEAPP: for f in os.listdir("/"):
apps.append( if f == "main.py":
[ yield App("Home", f)
"/%s" % HOMEAPP,
{
"author": "card10badge Team",
"name": "Home",
"description": "",
"category": "",
"revision": 0,
},
]
)
dirlist = [ for app in sorted(os.listdir("/apps")):
entry for entry in sorted(os.listdir("/apps")) if not entry.startswith(".") if app.startswith("."):
] continue
# list all hatchary style apps (not .elf and not .py) if app.endswith(".py") or app.endswith(".elf"):
# with or without metadata.json yield App(app, "/apps/" + app)
for appFolder in dirlist: continue
if not (appFolder.endswith(".py") or appFolder.endswith(".elf")):
metadata = read_metadata(appFolder)
if not metadata.get("bin", None):
fileName = "/apps/%s/__init__.py" % appFolder
else:
fileName = "/apps/%s/%s" % (appFolder, metadata["bin"])
apps.append([fileName, metadata])
# list simple python scripts try:
for pyFile in dirlist: with open("/apps/" + app + "/metadata.json") as f:
if pyFile.endswith(".py"): info = ujson.load(f)
apps.append(
[
"/apps/%s" % pyFile,
{
"author": "",
"name": pyFile,
"description": "",
"category": "",
"revision": 0,
},
]
)
# list simple elf binaries yield App(
for elfFile in dirlist: info["name"], "/apps/{}/{}".format(app, info.get("bin", "__init__.py"))
if elfFile.endswith(".elf"):
apps.append(
[
"/apps/%s" % elfFile,
{
"author": "",
"name": elfFile,
"description": "",
"category": "",
"revision": 0,
},
]
) )
except Exception as e:
print("Could not load /apps/{}/metadata.json!".format(app))
sys.print_exception(e)
return apps
def button_events(timeout=0):
"""Iterate over button presses (event-loop)."""
yield 0
button_pressed = False
count = 0
while True:
v = buttons.read(buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT | buttons.TOP_RIGHT)
if timeout > 0 and count > 0 and count % timeout == 0:
yield BUTTON_TIMER_POPPED
if timeout > 0:
count += 1
if v == 0:
button_pressed = False
if not button_pressed and v & buttons.BOTTOM_LEFT != 0:
button_pressed = True
yield buttons.BOTTOM_LEFT
if not button_pressed and v & buttons.BOTTOM_RIGHT != 0:
button_pressed = True
yield buttons.BOTTOM_RIGHT
if not button_pressed and v & buttons.TOP_RIGHT != 0:
button_pressed = True
yield buttons.TOP_RIGHT
utime.sleep_ms(10)
def triangle(disp, x, y, left, scale=6, color=[255, 0, 0]):
"""Draw a triangle to show there's more text in this line"""
yf = 1 if left else -1
disp.line(x - scale * yf, int(y + scale / 2), x, y, col=color)
disp.line(x, y, x, y + scale, col=color)
disp.line(x, y + scale, x - scale * yf, y + int(scale / 2), col=color)
class MainMenu(simple_menu.Menu):
timeout = 30.0
def draw_menu(disp, applist, pos, appcount, lineoffset): def entry2name(self, app):
disp.clear() return app.name
start = 0 def on_select(self, app, index):
if pos > 0: self.disp.clear().update()
start = pos - 1 try:
if start + 4 > appcount: print("Trying to load " + app.path)
start = appcount - 4 os.exec(app.path)
if start < 0: except OSError as e:
start = 0 print("Loading failed: ")
sys.print_exception(e)
self.error("Loading", "failed")
utime.sleep(1.0)
os.exit(1)
for i, app in enumerate(applist): def on_timeout(self):
if i >= start + 4 or i >= appcount: try:
break f = open("main.py")
if i >= start: f.close()
disp.rect( os.exec("main.py")
0, except OSError:
(i - start) * 20, pass
159,
(i - start) * 20 + 20,
col=COLOR_BG_SEL if i == pos else COLOR_BG,
)
line = app[1]["name"]
linelength = len(line)
off = 0
# calc line offset for scrolling def no_apps_message():
if i == pos and linelength > (MAXCHARS - 1) and lineoffset > 0: """Display a warning if no apps are installed."""
off = ( with display.open() as disp:
lineoffset disp.clear(color.COMMYELLOW)
if lineoffset + (MAXCHARS - 1) < linelength disp.print(
else linelength - (MAXCHARS - 1) " No apps ", posx=17, posy=20, fg=color.COMMYELLOW_DARK, bg=color.COMMYELLOW
) )
if lineoffset > linelength:
off = 0
disp.print( disp.print(
" " + line[off : (off + (MAXCHARS - 1))], "available", posx=17, posy=40, fg=color.COMMYELLOW_DARK, bg=color.COMMYELLOW
posy=(i - start) * 20,
fg=COLOR_TEXT,
bg=COLOR_BG_SEL if i == pos else COLOR_BG,
) )
if i == pos:
disp.print(">", posy=(i - start) * 20, fg=COLOR_ARROW, bg=COLOR_BG_SEL)
if linelength > (MAXCHARS - 1) and off < linelength - (MAXCHARS - 1):
triangle(disp, 153, (i - start) * 20 + 6, False, 6)
triangle(disp, 154, (i - start) * 20 + 7, False, 4)
triangle(disp, 155, (i - start) * 20 + 8, False, 2)
if off > 0:
triangle(disp, 24, (i - start) * 20 + 6, True, 6)
triangle(disp, 23, (i - start) * 20 + 7, True, 4)
triangle(disp, 22, (i - start) * 20 + 8, True, 2)
disp.update()
def main():
create_folders()
disp = display.open()
applist = list_apps()
numapps = len(applist)
current = 0
lineoffset = 0
timerscrollspeed = 1
timerstartscroll = 5
timercountpopped = 0
timerinactivity = 100
for ev in button_events(10):
if numapps == 0:
disp.clear(COLOR_BG)
disp.print(" No apps ", posx=17, posy=20, fg=COLOR_TEXT, bg=COLOR_BG)
disp.print("available", posx=17, posy=40, fg=COLOR_TEXT, bg=COLOR_BG)
disp.update() disp.update()
continue
if ev == buttons.BOTTOM_RIGHT:
# Scroll down
current = (current + 1) % numapps
lineoffset = 0
timercountpopped = 0
elif ev == buttons.BOTTOM_LEFT: while True:
# Scroll up utime.sleep(0.5)
current = (current + numapps - 1) % numapps
lineoffset = 0
timercountpopped = 0
elif ev == BUTTON_TIMER_POPPED:
timercountpopped += 1
if (
timercountpopped >= timerstartscroll
and (timercountpopped - timerstartscroll) % timerscrollspeed == 0
):
lineoffset += 1
if applist[0][0] == "/%s" % HOMEAPP and timercountpopped >= timerinactivity:
print("Inactivity timer popped")
disp.clear().update()
disp.close()
try:
os.exec("/%s" % HOMEAPP)
except OSError as e:
print("Loading failed: ", e)
os.exit(1)
elif ev == buttons.TOP_RIGHT: if __name__ == "__main__":
# Select & start apps = list(enumerate_apps())
disp.clear().update()
disp.close()
try:
os.exec(applist[current][0])
except OSError as e:
print("Loading failed: ", e)
os.exit(1)
draw_menu(disp, applist, current, numapps, lineoffset)
if apps == []:
no_apps_message()
if __name__ == "__main__": MainMenu(apps).run()
try:
main()
except Exception as e:
sys.print_exception(e)
with display.open() as d:
d.clear(color.COMMYELLOW)
d.print("Menu", posx=52, posy=20, fg=color.COMMYELLOW_DARK, bg=color.COMMYELLOW)
d.print("crashed", posx=31, posy=40, fg=color.COMMYELLOW_DARK, bg=color.COMMYELLOW)
d.update()
utime.sleep(2)
os.exit(1)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment