Toggle Navigation
Hatchery
Eggs
Menu++
menu.py
Users
Badges
Login
Register
MCH2022 badge?
go to mch2022.badge.team
menu.py
raw
Content
""" Menu Script =========== This is a modified menu script by Pixtxa. You can customize this script however you want :) If you want to go back to card10s default version, just delete this file; the firmware will recreate it on next run. """ import buttons import collections import color import display import os import simple_menu import sys import ujson import utime import leds App = collections.namedtuple("App", ["name", "path"]) # Favorite apps which are shown at the very top of the app list FAVORITE_APPS = ["personal_state", "ecg"] # Scroll delay for advanced info texts SCROLLDELAY = 4 leds.set_flashlight(0) def enumerate_entries(): if 'main.py' in os.listdir("/"): yield App("Home", "main.py") yield App("USB Storage", "USB_STORAGE_FLAG") yield from enumerate_apps(FAVORITE_APPS) yield App("*Refresh this List", "REFRESH_LIST_FLAG") yield from sorted(enumerate_apps(), key=lambda b: b.name.lower()) def enumerate_apps(apps=None): """List all installed apps.""" for app in apps or os.listdir("/apps"): if app.startswith("."): continue # Skip special apps when enumerating from filesystem if apps is None and app in FAVORITE_APPS: continue if app.endswith(".py") or app.endswith(".elf"): yield App(app, "/apps/" + app) continue try: with open("/apps/" + app + "/metadata.json") as f: info = ujson.load(f) yield App( info["name"], "/apps/{}/{}".format(app, info.get("bin", "__init__.py")) ) except Exception as e: print("'{}': metadata.json is invalid ... hoping for the best.".format(app)) sys.print_exception(e) pyfile = "/apps/{}/__init__.py".format(app) try: open(pyfile).close() yield App(app, pyfile) except OSError: print(pyfile, "does not even exist :(") pass def usb_mode(disp): os.usbconfig(os.USB_FLASH) disp.clear(color.CHAOSBLUE) disp.print("USB Storage", posx=3, posy=20, fg=color.CHAOSBLUE_DARK) disp.print("open", posx=52, posy=40, fg=color.CHAOSBLUE_DARK) disp.update() utime.sleep_ms(200) # Wait for select button to be released while buttons.read() == buttons.TOP_RIGHT: pass # Wait for any button to be pressed and disable USB storage again while buttons.read() == 0: pass os.usbconfig(os.USB_SERIAL) def draw_info(disp, offset, lines): disp.clear() for line in range(4): if lines[line]: if line%2: bgcol = color.COMMYELLOW else: bgcol = color.COMMYELLOW_DARK linelen = len(lines[line]) useoffset = offset%linelen linerep = "" if (useoffset+11) > linelen: linerep = lines[line][:(useoffset+11 - linelen)] disp.rect(0, line*20, 159, (line+1)*20, col=bgcol) disp.print(lines[line][useoffset:(useoffset+11)] + linerep, posx=3, posy=line*20, fg=color.BLACK, bg=bgcol) disp.update() def advanced_info(disp, app): folder = '/'.join(app.path.split('/')[:-1]) # delete last entry of path info = {} if folder: try: with open(folder+'/metadata.json') as data: info = ujson.loads(data.read()) {"category":"hardware","author":"card10 contributors","revision":-1,"source":"preload"} {"name":"Essensangebote","description":"Essensangebot der Kantine","category":"system","author":"card10 contributors","revision":1} except: pass info['path'] = folder else: folder = "/" if app.path == 'main.py': info = {"description":"This is the default app. It's loaded at system start and when you don't press any button in the menu","path":"/main.py","category":"System"} elif app.path == 'USB_STORAGE_FLAG': info = {"description":"Puts card10 into USB Storage mode.","path":"/menu.py","category":"System","author":"card10 contributors"} elif app.path == 'REFRESH_LIST_FLAG': info = {"description":"Refreshes the app list of the menu and saves it. Instead of the original menu.py script, this version doesn't create the list on every start, which is faster, but needs manual refreshing.","path":"/menu.py","category":"System","author":"Pixtxa"} if folder[0] != '/': folder = '/' + folder if 'name' not in info: info['name'] = app.name lines = ['','','',''] line = 0 lines[line] = str(info.pop('name')) + ' -' if 'revision' in info: lines[line] += ' Rev: ' + str(info.pop('revision')) + ' -' if 'path' in info: lines[line] += ' Path: ' + str(info.pop('path')) + ' -' if 'path' in info: lines[line] += ' Path: ' + str(info.pop('path')) + ' -' lines[line] += "-- Name: " line +=1 if 'description' in info: lines[line] = str(info.pop('description')) + ' --- Description: ' line += 1 else: try: with open(folder+'/README.md') as data: lines[line] = data.read() + ' --- Readme: ' line += 1 except: lines[line] = 'No description avialable. ---' if 'author' in info: lines[line] += 'Author: ' + str(info.pop('author')) + ' - ' if 'category' in info: lines[line] += 'Category: ' + str(info.pop('category')) + ' - ' if lines[line]: lines[line] = lines[line][:-1] + '-- ' line += 1 for entry in info: field = str(entry).replace('_',' ') if field.islower() or field.isupper(): field = ''.join([s[0].upper() + s.split(s[0],1)[1].lower() for s in field.split(' ')]) lines[line] += str(entry) + ': ' + str(info[entry]) + ' - ' if lines[line]: lines[line] = lines[line][:-1] + '-- ' line += 1 scroll = 0 offset = 0 draw_info(disp, offset, lines) # Wait for select button to be released while buttons.read() == buttons.TOP_RIGHT: pass #Debounce utime.sleep_ms(20) buttons.read() # Wait for select button to be pressed again btn = 0 while not (btn & buttons.TOP_RIGHT): btn = buttons.read() #Reset view when bottom left button is pressed if btn & buttons.BOTTOM_LEFT: scroll = 0 offset = 0 draw_info(disp, offset, lines) #Pause scrolling when bottom right button is pressed if not (btn & buttons.BOTTOM_RIGHT): scroll += 1 if scroll >= SCROLLDELAY: offset += 1 scroll = 0 draw_info(disp, offset, lines) utime.sleep_ms(10) class MainMenu(simple_menu.Menu): timeout = 30.0 color_1 = color.CAMPGREEN color_2 = color.CAMPGREEN_DARK def entry2name(self, app): return app.name def on_select(self, app, index): self.disp.clear().update() try: if app.path == "USB_STORAGE_FLAG": usb_mode(self.disp) # self.exit() #Refresh menu list return if app.path == "REFRESH_LIST_FLAG": self.exit() print("Trying to load " + app.path) os.exec(app.path) except OSError as e: print("Loading failed: ") sys.print_exception(e) self.error("Loading", "failed") utime.sleep(1.0) os.exit(1) def on_long_select(self, app, index): advanced_info(self.disp, app) def on_timeout(self): try: f = open("main.py") f.close() os.exec("main.py") except OSError: pass def loading_message(): with display.open() as disp: disp.clear(color.COMMYELLOW) disp.print("Loading", posx=31, posy=20, fg=color.COMMYELLOW_DARK, bg=color.COMMYELLOW) disp.print("menu ...", posx=24, posy=40, fg=color.COMMYELLOW_DARK, bg=color.COMMYELLOW) disp.update() def no_apps_message(): """Display a warning if no apps are installed.""" with display.open() as disp: disp.clear(color.COMMYELLOW) disp.print( " No apps ", posx=17, posy=20, fg=color.COMMYELLOW_DARK, bg=color.COMMYELLOW ) disp.print( "available", posx=17, posy=40, fg=color.COMMYELLOW_DARK, bg=color.COMMYELLOW ) disp.update() while True: utime.sleep(0.5) def refresh_app_list(): global apps with display.open() as disp: disp.clear(color.COMMYELLOW) disp.print("Refreshing", posx=10, posy=20, fg=color.COMMYELLOW_DARK, bg=color.COMMYELLOW) disp.print("list ...", posx=24, posy=40, fg=color.COMMYELLOW_DARK, bg=color.COMMYELLOW) disp.update() try: apps = list(enumerate_entries()) except OSError: apps = [] if not apps: no_apps_message() # Write apps list to filesystem, so it can be reused on next run # Format for each entry: app.name[tab]app.path[newline] with open('menu.apps', 'w') as applist: for app in apps: applist.write('\t'.join(app)+'\n') if __name__ == "__main__": global apps loading_message() try: apps = [] # Try to read the apps from filesystem # If the file is corrupted or missing, this fails and the list will be refreshed with open('menu.apps') as applist: for app in applist: apps.append(App(*app[:-1].split('\t'))) except: apps = [] while True: if "REFRESH_LIST_FLAG" not in [app.path for app in apps]: # App list is empty or corrupted => refresh it refresh_app_list() MainMenu(apps).run() apps = [] #Reset list so it will be refreshed