Compare commits

...

8 commits
1.0.10 ... main

Author SHA1 Message Date
foamyguy
508a0f5b87
Merge pull request #66 from FoamyGuy/order_app_icons
Some checks failed
Validate Build / validate-build (push) Has been cancelled
sort app icons by title. Remove unused iterator var
2025-08-22 11:46:37 -05:00
foamyguy
c24e40fd3f sort app icons by title. Remove unused iterator var 2025-08-22 11:42:23 -05:00
foamyguy
d96ee58a58
Merge pull request #64 from FoamyGuy/launcher_layout
Some checks failed
Validate Build / validate-build (push) Has been cancelled
Launcher layout improvements
2025-08-21 11:18:04 -05:00
foamyguy
a2253d6d5c
Merge pull request #63 from RetiredWizard/fruitjamaddminesw
Add PyPaint to included apps
2025-08-21 11:16:30 -05:00
foamyguy
13f6751504 more narrow arrow buttons. more room for app icon grid. move help text to single row at bottom. remove leftover hardcoded apps list. 2025-08-21 11:07:29 -05:00
RetiredWizard
bbbe417f6f Add PyPaint to included apps 2025-08-21 10:52:21 -04:00
foamyguy
f9ce279198
Merge pull request #53 from FoamyGuy/use_learn_repo
Some checks failed
Validate Build / validate-build (push) Has been cancelled
use learn repo instead of bundle zip URLs.
2025-08-19 13:40:49 -05:00
foamyguy
28cdd13f5c use learn repo instead of bundle zip URLs. 2025-08-14 12:11:02 -05:00
5 changed files with 37 additions and 127 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
dist dist
latest_dl latest_dl
Adafruit_Learning_System_Guides

114
build.py
View file

@ -1,26 +1,25 @@
from datetime import datetime from datetime import datetime
import os import os
import time
import zipfile import zipfile
import shutil import shutil
from pathlib import Path from pathlib import Path
import requests
from circup.commands import main as circup_cli from circup.commands import main as circup_cli
# TODO: maybe change these to use the first URLs i.e. https://learn.adafruit.com/elements/3198279/download?type=zip
# instead of the redirect URLs that are direct to the CDN. That will make easier for users to add apps here. # each path is a tuple that contains:
# The code will need to follow the redirect and get the filename from the next URL. # (path within learn repo, directory name to use inside of apps/)
LEARN_PROJECT_URLS = [ LEARN_PROJECT_PATHS = [
"https://cdn-learn.adafruit.com/downloads/zip/3194974/Metro/Metro_RP2350_Snake.zip?timestamp={}", ("Metro/Metro_RP2350_Snake/","Metro_RP2350_Snake"),
"https://cdn-learn.adafruit.com/downloads/zip/3195762/Metro/Metro_RP2350_Memory/memory_game.zip?timestamp={}", ("Metro/Metro_RP2350_Memory/memory_game/", "Metro_RP2350_Memory"),
"https://cdn-learn.adafruit.com/downloads/zip/3195805/Metro/Metro_RP2350_CircuitPython_Matrix.zip?timestamp={}", ("Metro/Metro_RP2350_CircuitPython_Matrix/", "Metro_RP2350_CircuitPython_Matrix"),
"https://cdn-learn.adafruit.com/downloads/zip/3194658/Metro/Metro_RP2350_FlappyNyanCat.zip?timestamp={}", ("Metro/Metro_RP2350_FlappyNyanCat/", "Metro_RP2350_FlappyNyanCat"),
"https://cdn-learn.adafruit.com/downloads/zip/3196927/Metro/Metro_RP2350_Match3/match3_game.zip?timestamp={}", ("Metro/Metro_RP2350_Match3/match3_game/", "Metro_RP2350_Match3"),
"https://cdn-learn.adafruit.com/downloads/zip/3194422/Metro/Metro_RP2350_Breakout.zip?timestamp={}", ("Metro/Metro_RP2350_Breakout/", "Metro_RP2350_Breakout"),
"https://cdn-learn.adafruit.com/downloads/zip/3196755/Metro/Metro_RP2350_Chips_Challenge.zip?timestamp={}", ("Metro/Metro_RP2350_Chips_Challenge/", "Metro_RP2350_Chips_Challenge"),
"https://cdn-learn.adafruit.com/downloads/zip/3198116/Metro/Metro_RP2350_Minesweeper.zip?timestamp={}", ("Metro/Metro_RP2350_Minesweeper/", "Metro_RP2350_Minesweeper"),
"https://cdn-learn.adafruit.com/downloads/zip/3198279/Fruit_Jam/Larsio_Paint_Music.zip?timestamp={}", ("Fruit_Jam/Larsio_Paint_Music/", "Larsio_Paint_Music"),
"https://cdn-learn.adafruit.com/downloads/zip/3203853/Fruit_Jam/Fruit_Jam_IRC_Client.zip?timestamp={}", ("Fruit_Jam/Fruit_Jam_IRC_Client/", "Fruit_Jam_IRC_Client"),
("Fruit_Jam/Fruit_Jam_PyPaint/", "Fruit_Jam_PyPaint"),
] ]
def create_font_specific_zip(font_path: Path, src_dir: Path, learn_projects_dir: Path, output_dir: Path): def create_font_specific_zip(font_path: Path, src_dir: Path, learn_projects_dir: Path, output_dir: Path):
@ -39,6 +38,8 @@ def create_font_specific_zip(font_path: Path, src_dir: Path, learn_projects_dir:
try: try:
# Copy src contents # Copy src contents
shutil.copytree(src_dir, temp_dir, dirs_exist_ok=True) shutil.copytree(src_dir, temp_dir, dirs_exist_ok=True)
# remove empty __init__.py file
os.remove(temp_dir / "__init__.py")
# Create fonts directory and copy the specific font # Create fonts directory and copy the specific font
fonts_dir = temp_dir / "fonts" fonts_dir = temp_dir / "fonts"
@ -48,65 +49,9 @@ def create_font_specific_zip(font_path: Path, src_dir: Path, learn_projects_dir:
# Extract learn-projects contents into apps directory # Extract learn-projects contents into apps directory
apps_dir = temp_dir / "apps" apps_dir = temp_dir / "apps"
apps_dir.mkdir(parents=True, exist_ok=True) apps_dir.mkdir(parents=True, exist_ok=True)
for zip_path in learn_projects_dir.glob("*.zip"): # copy learn apps
# Create app-specific directory using zip name without extension for learn_app_path, dir_name in LEARN_PROJECT_PATHS:
app_name = zip_path.stem shutil.copytree(f"Adafruit_Learning_System_Guides/{learn_app_path}", apps_dir / dir_name, dirs_exist_ok=True)
app_dir = apps_dir / app_name
app_dir.mkdir(parents=True, exist_ok=True)
# Extract zip contents and process them
with zipfile.ZipFile(zip_path, 'r') as zf:
# Find the directory containing code.py
code_dir = None
for path in zf.namelist():
if path.endswith('/code.py'):
code_dir = str(Path(path).parent) + '/'
break
if not code_dir:
print(f"Warning: No code.py found in {zip_path}")
continue
# Extract files from the code.py directory to app directory
for path in zf.namelist():
if path.startswith(code_dir):
# Skip the lib directory as we'll handle it separately
if 'lib/' in path:
continue
if path.endswith("/"):
# skip directories, they will get created by
# mkdir(parents=True) below
continue
# Get the relative path from code_dir
rel_path = path[len(code_dir):]
if rel_path:
# Extract the file
source = zf.open(path)
target = app_dir / rel_path
target.parent.mkdir(parents=True, exist_ok=True)
with open(target, 'wb') as f:
f.write(source.read())
# Handle lib directory specially - move to root
for path in zf.namelist():
if '/lib/' in path:
# Get the part of the path after 'lib/'
lib_index = path.index('/lib/') + 5 # skip past '/lib/'
rel_path = path[lib_index:]
# Skip directory entries
if not rel_path or path.endswith('/'):
continue
# Extract the file to root lib directory
source = zf.open(path)
target = temp_dir / 'lib' / rel_path
# Ensure parent directory exists
target.parent.mkdir(parents=True, exist_ok=True)
# Write the file
with open(target, 'wb') as f:
f.write(source.read())
# copy builtin apps # copy builtin apps
shutil.copytree("builtin_apps", apps_dir, dirs_exist_ok=True) shutil.copytree("builtin_apps", apps_dir, dirs_exist_ok=True)
@ -116,9 +61,9 @@ def create_font_specific_zip(font_path: Path, src_dir: Path, learn_projects_dir:
circup_cli(["--path", temp_dir, "install", "--auto"], circup_cli(["--path", temp_dir, "install", "--auto"],
standalone_mode=False) standalone_mode=False)
# install builtin apps required libs # install apps required libs
for builtin_app_dir in os.listdir("builtin_apps"): for app_dir in os.listdir(apps_dir):
circup_cli(["--path", temp_dir, "install", "--auto", "--auto-file", f"apps/{builtin_app_dir}/code.py"], circup_cli(["--path", temp_dir, "install", "--auto", "--auto-file", f"apps/{app_dir}/code.py"],
standalone_mode=False) standalone_mode=False)
os.remove(temp_dir / "boot_out.txt") os.remove(temp_dir / "boot_out.txt")
# Create the final zip file # Create the final zip file
@ -139,13 +84,12 @@ def create_font_specific_zip(font_path: Path, src_dir: Path, learn_projects_dir:
def download_learn_projects(): def download_learn_projects():
for url in LEARN_PROJECT_URLS: try:
response = requests.get(url.format(int(time.time())), allow_redirects=True) shutil.rmtree("Adafruit_Learning_System_Guides/")
resp_url = response.url except FileNotFoundError:
#print(resp_url) pass
filename = resp_url.split("/")[-1].split("?")[0]
with open(f"learn-projects/{filename}", 'wb') as f: os.system("git clone https://github.com/adafruit/Adafruit_Learning_System_Guides.git")
f.write(response.content)
def main(): def main():

View file

@ -146,45 +146,13 @@ if "use_mouse" in launcher_config and launcher_config["use_mouse"]:
mouse_buf = array.array("b", [0] * 8) mouse_buf = array.array("b", [0] * 8)
WIDTH = int(280 / 360 * display.width // scale) WIDTH = int(298 / 360 * display.width // scale)
HEIGHT = int(182 / 200 * display.height // scale) HEIGHT = int(182 / 200 * display.height // scale)
config = { config = {
"menu_title": "Launcher Menu", "menu_title": "Launcher Menu",
"width": 3, "width": 3,
"height": 2, "height": 2,
"apps": [
{
"title": "🐍Snake🐍",
"icon": "icon_snake.bmp",
"file": "code_snake_game.py"
},
{
"title": "Nyan😺Flap",
"icon": "icon_flappynyan.bmp",
"file": "code_flappy_nyan.py"
},
{
"title": "Memory🧠",
"icon": "icon_memory.bmp",
"file": "code_memory.py"
},
{
"title": "Matrix",
"icon": "/apps/matrix/icon.bmp",
"file": "/apps/matrix/code.py"
},
{
"title": "Breakout",
"icon": "icon_breakout.bmp",
"file": "code_breakout.py"
},
{
"title": "Paint🖌",
"icon": "icon_paint.bmp",
}
]
} }
cell_width = WIDTH // config["width"] cell_width = WIDTH // config["width"]
@ -207,7 +175,6 @@ scaled_group.append(menu_title_txt)
app_titles = [] app_titles = []
apps = [] apps = []
app_path = pathlib.Path("/apps") app_path = pathlib.Path("/apps")
i = 0
pages = [{}] pages = [{}]
@ -248,7 +215,7 @@ for path in app_path.iterdir():
"dir": path "dir": path
}) })
i += 1 apps = sorted(apps, key=lambda app: app["title"].lower())
print("launcher config", launcher_config) print("launcher config", launcher_config)
if "favorites" in launcher_config: if "favorites" in launcher_config:
@ -371,11 +338,11 @@ if "arrow" in launcher_config["palette"]:
left_tg = AnchoredTileGrid(bitmap=left_bmp, pixel_shader=left_palette) left_tg = AnchoredTileGrid(bitmap=left_bmp, pixel_shader=left_palette)
left_tg.anchor_point = (0, 0.5) left_tg.anchor_point = (0, 0.5)
left_tg.anchored_position = (4, (display.height // 2 // scale) - 2) left_tg.anchored_position = (0, (display.height // 2 // scale) - 2)
right_tg = AnchoredTileGrid(bitmap=right_bmp, pixel_shader=right_palette) right_tg = AnchoredTileGrid(bitmap=right_bmp, pixel_shader=right_palette)
right_tg.anchor_point = (1.0, 0.5) right_tg.anchor_point = (1.0, 0.5)
right_tg.anchored_position = ((display.width // scale) - 4, (display.height // 2 // scale) - 2) right_tg.anchored_position = ((display.width // scale), (display.height // 2 // scale) - 2)
original_arrow_btn_color = left_palette[2] original_arrow_btn_color = left_palette[2]
scaled_group.append(left_tg) scaled_group.append(left_tg)
@ -389,13 +356,11 @@ if mouse:
scaled_group.append(mouse_tg) scaled_group.append(mouse_tg)
help_txt = Label(terminalio.FONT, text="[Arrow]: Move\n[E]: Edit\n[Enter]: Run\n[1-9]: Page", help_txt = Label(terminalio.FONT, text="[Arrow]: Move [E]: Edit [Enter]: Run [1-9]: Page",
color=int(launcher_config["palette"].get("fg", "0xffffff"), 16)) color=int(launcher_config["palette"].get("fg", "0xffffff"), 16))
# help_txt = TextBox(terminalio.FONT, width=88, height=30, align=TextBox.ALIGN_RIGHT, background_color=int(launcher_config["palette"].get("accent", "0x008800"), 16), text="[E]: Edit\n[Enter]: Run")
help_txt.anchor_point = (0, 0)
help_txt.anchored_position = (2, 2) help_txt.anchor_point = (0.0, 1.0)
# help_txt.anchored_position = (display.width - 89, 1) help_txt.anchored_position = (2, display.height-2)
print(help_txt.bounding_box) print(help_txt.bounding_box)
main_group.append(help_txt) main_group.append(help_txt)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 918 B

After

Width:  |  Height:  |  Size: 918 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 918 B

After

Width:  |  Height:  |  Size: 918 B