From 0f3c2c0e7cc4f8911ab35443a6dff5160dc276b6 Mon Sep 17 00:00:00 2001 From: Marek Ventur Date: Mon, 27 Aug 2018 22:20:51 +0100 Subject: [PATCH] Settings app --- badge_store/main.py | 12 +++--- lib/dialogs.py | 19 ++++---- lib/sleep.py | 2 + lib/stack_nav.py | 63 +++++++++++++++++++++++++++ settings/badge_store_settings.py | 9 ++++ settings/main.py | 74 ++++++++++---------------------- 6 files changed, 113 insertions(+), 66 deletions(-) create mode 100644 lib/stack_nav.py create mode 100644 settings/badge_store_settings.py diff --git a/badge_store/main.py b/badge_store/main.py index c45e147..8fb5f42 100644 --- a/badge_store/main.py +++ b/badge_store/main.py @@ -6,22 +6,22 @@ To publish apps use https://badge.emfcamp.org""" ___license___ = "MIT" ___title___ = "Badge Store" -___dependencies___ = ["badge_store", "dialogs", "ugfx_helper", "app"] +___dependencies___ = ["badge_store", "dialogs", "ugfx_helper", "app", "database"] ___categories___ = ["System"] ___bootstrapped___ = True -import ugfx_helper -import os -import wifi +import ugfx_helper, os, database, wifi, app from dialogs import * -import app from lib.badge_store import BadgeStore ### VIEWS ### ugfx_helper.init() -store = BadgeStore() +url = database.get("badge_store.url", "http://badgeserver.emfcamp.org/2018") +repo = database.get("badge_store.repo", "emfcamp/Mk4-Apps") +ref = database.get("badge_store.ref", "master") +store = BadgeStore(url=url, repo=repo, ref=ref) title = "TiLDA Badge Store" def clear(): diff --git a/lib/dialogs.py b/lib/dialogs.py index f977627..ec01c61 100644 --- a/lib/dialogs.py +++ b/lib/dialogs.py @@ -66,7 +66,7 @@ def prompt_boolean(text, title="TiLDA", true_text="Yes", false_text="No", width if button_no: button_no.destroy() label.destroy() -def prompt_text(description, init_text = "", true_text="OK", false_text="Back", font=FONT_MEDIUM_BOLD, style=default_style_badge): +def prompt_text(description, init_text="", true_text="OK", false_text="Back", font=FONT_MEDIUM_BOLD, style=default_style_badge): """Shows a dialog and keyboard that allows the user to input/change a string Returns None if user aborts with button B @@ -119,7 +119,7 @@ def prompt_text(description, init_text = "", true_text="OK", false_text="Back", edit.destroy(); return -def prompt_option(options, index=0, text = "Please select one of the following:", title=None, select_text="OK", none_text=None): +def prompt_option(options, index=0, text = None, title=None, select_text="OK", none_text=None): """Shows a dialog prompting for one of multiple options If none_text is specified the user can use the B or Menu button to skip the selection @@ -133,12 +133,15 @@ def prompt_option(options, index=0, text = "Please select one of the following:" if title: window.text(5, 10, title, TILDA_COLOR) window.line(0, 25, ugfx.width() - 10, 25, ugfx.BLACK) - window.text(5, 30, text, ugfx.BLACK) - list_y = 50 + list_y = 30 + if text: + list_y += 20 + window.text(5, 30, text, ugfx.BLACK) + else: window.text(5, 10, text, ugfx.BLACK) - options_list = ugfx.List(5, list_y, ugfx.width() - 25, 180 - list_y, parent = window) + options_list = ugfx.List(5, list_y, ugfx.width() - 25, 260 - list_y, parent = window) for option in options: if isinstance(option, dict) and option["title"]: @@ -151,8 +154,8 @@ def prompt_option(options, index=0, text = "Please select one of the following:" if none_text: none_text = "B: " + none_text - button_select = ugfx.Button(5, ugfx.height() - 50, 140 if none_text else ugfx.width() - 25, 30 , select_text, parent=window) - button_none = ugfx.Button(ugfx.width() - 160, ugfx.height() - 50, 140, 30 , none_text, parent=window) if none_text else None + button_select = ugfx.Button(5, ugfx.height() - 50, 105 if none_text else 200, 30 , select_text, parent=window) + button_none = ugfx.Button(117, ugfx.height() - 50, 105, 30 , none_text, parent=window) if none_text else None try: while True: @@ -180,7 +183,7 @@ def prompt_option(options, index=0, text = "Please select one of the following:" class WaitingMessage: """Shows a dialog with a certain message that can not be dismissed by the user""" - def __init__(self, text = "Please Wait...", title="TiLDA"): + def __init__(self, text="Please Wait...", title="TiLDA"): self.window = ugfx.Container(30, 30, ugfx.width() - 60, ugfx.height() - 60) self.window.show() self.window.text(5, 10, title, TILDA_COLOR) diff --git a/lib/sleep.py b/lib/sleep.py index 53b637a..4da57f4 100644 --- a/lib/sleep.py +++ b/lib/sleep.py @@ -1,5 +1,7 @@ """Library for sleep related functions""" +___license___ = "MIT" + import time def sleep_ms(duration): diff --git a/lib/stack_nav.py b/lib/stack_nav.py new file mode 100644 index 0000000..9dedae2 --- /dev/null +++ b/lib/stack_nav.py @@ -0,0 +1,63 @@ +"""A stack-based naviation system. + +Maintains a stack of states through which a user can navigate. +Think Android's back button model. + +Every screen is modeled as a function that takes one argument "state". +It can modify state and should either return +* None (which means go back one level) +* A new function (which adds to the stack) +* A keyword like NEXT_EXIT (exit nav) or NEXT_TOP (go to top of stack) + +Children get a a shallow clone of the current state, going back will +lead to a screen with the original state. +""" + +___dependencies___ = ["dialogs", "database"] +___license___ = "MIT" + +import dialogs, database + +NEXT_EXIT = "exit" # Leave navigation +NEXT_TOP = "init" # Go to top of stack +def nav(init_fn, init_state={}): + stack = [(init_fn, init_state)] + while stack: + (fn, state) = stack[-1] # peek + next_state = state.copy() + result = fn(next_state) + if callable(result): + stack.append((result, next_state)) + elif result == NEXT_TOP: + stack = [(init_fn, init_state)] + elif result == NEXT_EXIT: + break + else: + stack.pop() + +# A simple menu. Format of items is [{"foo":fn}, ...] +def selection(items, title="Settings", none_text="Back"): + items = [{"title": t, "function": fn} for (t, fn) in items.items()] + selection = dialogs.prompt_option(items, none_text=none_text, title=title) + if selection: + return selection["function"] + else: + return None + +# A wrapper for a simple string menu +def change_string(title, getter_setter_function): + def inner(state): + value = getter_setter_function() + value = dialogs.prompt_text(title, init_text=value, true_text="Change", false_text="Back") + if value: + getter_setter_function(value) + return inner + +# A wrapper for a database key +def change_database_string(title, key, default=""): + def inner(state): + value = database.get(key, default) + value = dialogs.prompt_text(title, init_text=value, true_text="Change", false_text="Back") + if value: + database.set(key, value) + return inner diff --git a/settings/badge_store_settings.py b/settings/badge_store_settings.py new file mode 100644 index 0000000..f4f0a68 --- /dev/null +++ b/settings/badge_store_settings.py @@ -0,0 +1,9 @@ +from stack_nav import * +from database import * + +def settings_badge_store(state): + return selection({ + ("API: %s" % get("badge_store.url", "http://badgeserver.emfcamp.org/2018")): change_database_string("Set API", "badge_store.url", "http://badgeserver.emfcamp.org/2018"), + ("Repo: %s" % get("badge_store.repo", "emfcamp/Mk4-Apps")): change_database_string("Git repository", "badge_store.repo", "emfcamp/Mk4-Apps"), + ("Ref: %s" % get("badge_store.ref", "master")): change_database_string("Set branch, tag or commit", "badge_store.ref", "master") + }, "Badge store settings") diff --git a/settings/main.py b/settings/main.py index e5e4b45..8df4d20 100644 --- a/settings/main.py +++ b/settings/main.py @@ -6,71 +6,41 @@ Currently supports * Pick default app * Change badgestore repo/branch +Todo: +* timezone + """ ___name___ = "Settings" ___license___ = "MIT" -___dependencies___ = ["dialogs", "ugfx_helper", "database"] +___dependencies___ = ["dialogs", "ugfx_helper", "database", "app", "stack_nav", "wifi"] ___categories___ = ["System"] ___bootstrapped___ = True import ugfx_helper, os, wifi, app, database +from settings.badge_store_settings import settings_badge_store +from dialogs import * +from stack_nav import * -### VIEWS ### - -ugfx_helper.init() - -title = "Settings" - -def clear(): - ugfx.clear(ugfx.html_color(ugfx.WHITE)) - -def settings_name(state): - pass - +### SCREENS ### def settings_startup_app(state): - pass + apps = app.get_apps() + print(apps) + selection = prompt_option([{"title": a.title, "app": a} for a in apps], text="Select App:", none_text="Back", title="Set startup app") + if selection: + app.write_launch_file(app.name, "default_app.txt") def settings_wifi(state): - pass - -def settings_badge_store(state): - pass + wifi.choose_wifi() def settings_main(state): - menu_items = [ - {"title": "Change Name", "function": settings_name}, - {"title": "Wifi", "function": settings_wifi}, - {"title": "Set startup app", "function": settings_startup_app}, - {"title": "Change Badge Store", "function": settings_badge_store} - ] + return selection({ + "Homescreeen Name": change_database_string("Set your name", "name"), + "Wifi": settings_wifi, + "Startup app": settings_startup_app, + "Badge Store": settings_badge_store + }, none_text="Exit") - return prompt_option(menu_items, none_text="Exit", text="What do you want to do?", title=title) - -# Todo: this might be useful in a lib - -# A stack-based naviation system. -NEXT_EXIT = "exit" # Leave navigation -NEXT_INIT = "init" # Go to top of stack -def nav(init_fn, init_state={}): - stack = [(init_fn, init_state)] - - while len(stack): - (fn, state) = stack[-1] # peek - next_state = state.clone() - print(next_state) - result = fn(next_state) - if callable(result): - stack.append((result, next_state)) - elif result == NEXT_INIT: - stack = [(init_fn, init_state)] - elif result == NEXT_EXIT: - break - else: - stack.pop() - - print("bye") - -# Entry point +### ENTRY POINT ### +ugfx_helper.init() nav(settings_main) -#show_app("launcher")