You've already forked MicroPythonOS
mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
Allow launcher to have a non-hardcoded assets/main.py
This commit is contained in:
@@ -1,28 +1,4 @@
|
||||
import uos
|
||||
import uio
|
||||
import machine
|
||||
|
||||
def parse_manifest(manifest_path):
|
||||
name = "Unknown"
|
||||
main_script = None # Default to None if Main-Script is not found
|
||||
try:
|
||||
with uio.open(manifest_path, 'r') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line.startswith("Name:"):
|
||||
name = line.split(":", 1)[1].strip()
|
||||
elif line.startswith("Main-Script:"):
|
||||
main_script = line.split(":", 1)[1].strip()
|
||||
except OSError:
|
||||
print(f"Error reading {manifest_path}")
|
||||
return name, main_script
|
||||
|
||||
|
||||
def on_app_click(event, app_name, main_script, app_dir):
|
||||
if event.get_code() == lv.EVENT.CLICKED:
|
||||
print(f"Launching app: {app_name} by starting {main_script} in {app_dir})")
|
||||
run_app(main_script, True)
|
||||
|
||||
|
||||
# Create a container for the grid
|
||||
cont = lv.obj(subwindow)
|
||||
@@ -38,26 +14,20 @@ iconcont_width = icon_size + 24
|
||||
iconcont_height = icon_size + label_height
|
||||
|
||||
# Get list of app directories
|
||||
# Should we skip 'Launcher' apps from the list here?
|
||||
apps_dir = "/apps"
|
||||
for app_dir in [d for d in uos.listdir(apps_dir) if uos.stat(f"{apps_dir}/{d}")[0] & 0x4000]: # TODO: skip 'Launcher' apps from the list here
|
||||
for app_dir in [d for d in uos.listdir(apps_dir) if uos.stat(f"{apps_dir}/{d}")[0] & 0x4000]:
|
||||
# Paths
|
||||
base_path = f"{apps_dir}/{app_dir}"
|
||||
#icon_path = f"{base_path}/res/mipmap-mdpi/launcher_icon.png"
|
||||
icon_path = "/resources/icon_64x64.bin"
|
||||
manifest_path = f"{base_path}/META-INF/MANIFEST.MF"
|
||||
# Get app name from MANIFEST.MF
|
||||
app_name, main_script = parse_manifest(manifest_path)
|
||||
if main_script:
|
||||
main_script = f"{base_path}/{main_script}"
|
||||
else:
|
||||
main_script = f"{base_path}/assets/main.py" # default
|
||||
app_dir_fullpath = f"{apps_dir}/{app_dir}"
|
||||
app_name, main_script = parse_manifest(f"{app_dir_fullpath}/META-INF/MANIFEST.MF")
|
||||
# Create a container for each app (icon + label)
|
||||
app_cont = lv.obj(cont)
|
||||
app_cont.set_size(iconcont_width, iconcont_height)
|
||||
app_cont.set_style_border_width(0, 0)
|
||||
app_cont.set_style_pad_all(0, 0)
|
||||
#app_cont.set_style_bg_color(lv.color_hex(0x00FF00), 0)
|
||||
# Load and display icon
|
||||
#icon_path = f"{base_path}/res/mipmap-mdpi/launcher_icon.png"
|
||||
icon_path = "/resources/icon_64x64.bin"
|
||||
image = lv.image(app_cont)
|
||||
try:
|
||||
with open(icon_path, 'rb') as f:
|
||||
@@ -77,7 +47,6 @@ for app_dir in [d for d in uos.listdir(apps_dir) if uos.stat(f"{apps_dir}/{d}")[
|
||||
image.set_src(image_dsc)
|
||||
except Exception as e:
|
||||
print(f"Error loading icon {icon_path}: {e}")
|
||||
# Fallback: create a placeholder image
|
||||
image.set_src(lv.SYMBOL.DUMMY) # Or use a default image
|
||||
image.align(lv.ALIGN.TOP_MID, 0, 0)
|
||||
image.set_size(icon_size, icon_size)
|
||||
@@ -88,6 +57,6 @@ for app_dir in [d for d in uos.listdir(apps_dir) if uos.stat(f"{apps_dir}/{d}")[
|
||||
label.set_width(iconcont_width)
|
||||
label.align(lv.ALIGN.BOTTOM_MID, 0, 0)
|
||||
label.set_style_text_align(lv.TEXT_ALIGN.CENTER, 0)
|
||||
app_cont.add_event_cb(lambda e, name=app_name, main_script=main_script, dir=app_dir: on_app_click(e, name, main_script, dir), lv.EVENT.CLICKED, None)
|
||||
app_cont.add_event_cb(lambda e, app_dir=app_dir: start_app(app_dir_fullpath), lv.EVENT.CLICKED, None)
|
||||
|
||||
|
||||
|
||||
@@ -235,8 +235,24 @@ restart_btn.add_event_cb(lambda event: machine.reset(),lv.EVENT.CLICKED,None)
|
||||
|
||||
import _thread
|
||||
import traceback
|
||||
import uio
|
||||
|
||||
def get_filename(path):
|
||||
def parse_manifest(manifest_path):
|
||||
name = "Unknown"
|
||||
main_script = "assets/main.py"
|
||||
try:
|
||||
with uio.open(manifest_path, 'r') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line.startswith("Name:"):
|
||||
name = line.split(":", 1)[1].strip()
|
||||
elif line.startswith("Main-Script:"):
|
||||
main_script = line.split(":", 1)[1].strip()
|
||||
except OSError:
|
||||
print(f"Error reading {manifest_path}")
|
||||
return name, main_script
|
||||
|
||||
def long_path_to_filename(path):
|
||||
try:
|
||||
if not path or not isinstance(path, str):
|
||||
return None
|
||||
@@ -248,6 +264,7 @@ def get_filename(path):
|
||||
print(f"Error extracting filename: {str(e)}")
|
||||
return None
|
||||
|
||||
# Run the script in the current thread:
|
||||
def execute_script(script_source, is_file, lvgl_obj, return_to_launcher):
|
||||
thread_id = _thread.get_ident()
|
||||
print(f"Thread {thread_id}: executing script")
|
||||
@@ -255,7 +272,8 @@ def execute_script(script_source, is_file, lvgl_obj, return_to_launcher):
|
||||
script_globals = {
|
||||
'lv': lv,
|
||||
'subwindow': lvgl_obj,
|
||||
'run_app': run_app,
|
||||
'start_app': start_app, # for launcher apps
|
||||
'parse_manifest': parse_manifest, # for launcher apps
|
||||
'__name__': "__main__"
|
||||
}
|
||||
if is_file:
|
||||
@@ -264,7 +282,7 @@ def execute_script(script_source, is_file, lvgl_obj, return_to_launcher):
|
||||
script_source = f.read()
|
||||
print(f"Thread {thread_id}: starting script")
|
||||
try:
|
||||
compile_name = 'script' if not is_file else get_filename(script_source) # Only filename, to avoid 'name too long' error
|
||||
compile_name = 'script' if not is_file else long_path_to_filename(script_source) # Only filename, to avoid 'name too long' error
|
||||
compiled_script = compile(script_source, compile_name, 'exec')
|
||||
exec(compiled_script, script_globals)
|
||||
except Exception as e:
|
||||
@@ -281,25 +299,29 @@ def execute_script(script_source, is_file, lvgl_obj, return_to_launcher):
|
||||
tb = getattr(e, '__traceback__', None)
|
||||
traceback.print_exception(type(e), e, tb)
|
||||
|
||||
def run_app(scriptname, is_file, return_to_launcher=True):
|
||||
gc.collect()
|
||||
print("/main.py: free memory before starting thread:", gc.mem_free())
|
||||
# Run the script in a new thread:
|
||||
def execute_script_new_thread(scriptname, is_file, return_to_launcher):
|
||||
print(f"/main.py: execute_script_new_thread({scriptname},{is_file},{return_to_launcher})")
|
||||
try:
|
||||
print("/main.py: cleaning subwindow...")
|
||||
print("/main.py: execute_script_new_thread(): cleaning subwindow...")
|
||||
subwindow.clean()
|
||||
# 168KB maximum at startup but 136KB after loading display, drivers, LVGL gui etc so let's go for 128KB for now, still a lot...
|
||||
# But then no additional threads can be created. A stacksize of 32KB allows for 4 threads, so 3 in the app itself, which might be tight.
|
||||
_thread.stack_size(16384) # A stack size of 16KB allows for around 10 threads in the app, which should be plenty.
|
||||
_thread.start_new_thread(execute_script, (scriptname, is_file, subwindow, return_to_launcher))
|
||||
except Exception as e:
|
||||
print("/main.py: error starting new thread thread: ", e)
|
||||
print("/main.py: execute_script_new_thread(): error starting new thread thread: ", e)
|
||||
|
||||
def start_app(app_dir, return_to_launcher=True):
|
||||
print(f"/main.py start_app({app_dir},{return_to_launcher}")
|
||||
manifest_path = f"{app_dir}/META-INF/MANIFEST.MF"
|
||||
app_name, main_script = parse_manifest(manifest_path)
|
||||
main_script_fullpath = f"{app_dir}/{main_script}"
|
||||
execute_script_new_thread(main_script_fullpath, True, return_to_launcher)
|
||||
|
||||
def run_launcher():
|
||||
run_app("/apps/com.example.launcher/assets/main.py", True, False)
|
||||
start_app("/apps/com.example.launcher", False)
|
||||
|
||||
#run_app("/autostart.py", True, subwindow, False)
|
||||
execute_script_new_thread("/autostart.py", True, False)
|
||||
run_launcher()
|
||||
|
||||
execute_script("/autostart.py", True, subwindow, False)
|
||||
|
||||
run_launcher() # not needed because autostart.py will return_to_launcher
|
||||
|
||||
Reference in New Issue
Block a user