Skip to content

Commit fb61318

Browse files
committed
Make simulator MicroPython-only
1 parent 4fa88d1 commit fb61318

7 files changed

Lines changed: 40 additions & 289 deletions

File tree

simulator/README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ Live at <https://pydevices.github.io/simulator/>.
77
## Architecture
88

99
- **Editor**: Monaco Editor with Python syntax highlighting and LZ-String compressed URL hash sharing.
10-
- **Runtimes**:
11-
- **MicroPython WebAssembly**: Pre-compiled binary (`/vendor/micropython/micropython.mjs`) containing built-in `displaydev`, `multimer`, `appdev`, `board_config`, `lvgl`, `pydevices-lvgl`, `display_driver`, `palettes`, `pdwidgets`, `pygraphics`, and `usdl2`.
12-
- **Pyodide (CPython WASM)**: Python 3.12+ in the browser with dynamic wheel loading for companion packages.
10+
- **Runtime**: Direct MicroPython WebAssembly (`/vendor/micropython/micropython.mjs`) containing built-in `displaydev`, `multimer`, `appdev`, `board_config`, `lvgl`, `pydevices-lvgl`, `display_driver`, `palettes`, `pdwidgets`, `pygraphics`, and `usdl2`.
1311
- **Display Emulation**: Direct HTML5 Canvas binding with configurable resolution and shape presets (round watches, rectangular displays, landscape panels).
1412
- **Progressive Web App (PWA)**: Includes `manifest.json` and `sw.js` with Cross-Origin-Isolation (`COOP`/`COEP`/`CORP`) headers, enabling offline installation and execution on desktop and mobile browsers.

simulator/index.html

Lines changed: 6 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<script>try{if(localStorage.getItem('pydevices-theme')==='light'){document.documentElement.setAttribute('data-theme','light');}}catch(e){}</script>
66
<meta name="viewport" content="width=device-width, initial-scale=1">
77
<title>PyDevices Simulator — Interactive Python Device Playground</title>
8-
<meta name="description" content="Interactive browser-based Python playground for LVGL, pdwidgets, pygraphics, and displaydev on MicroPython and Pyodide.">
8+
<meta name="description" content="Interactive MicroPython WebAssembly playground for LVGL, pdwidgets, pygraphics, and displaydev.">
99
<link rel="icon" type="image/svg+xml" href="/assets/img/logo.svg">
1010
<link rel="manifest" href="manifest.json">
1111
<meta name="theme-color" content="#100e0b">
@@ -18,8 +18,6 @@
1818
<script src="https://cdnjs.cloudflare.com/ajax/libs/lz-string/1.5.0/lz-string.min.js"></script>
1919
<!-- Monaco Editor AMD Loader -->
2020
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs/loader.js"></script>
21-
<!-- PyScript is retained only for the optional Pyodide runtime. -->
22-
<link rel="stylesheet" href="https://pyscript.net/releases/2024.11.1/core.css">
2321
</head>
2422
<body>
2523
<!-- Top Global App Bar -->
@@ -65,13 +63,6 @@
6563
</select>
6664
</div>
6765

68-
<!-- Runtime Engine Selector -->
69-
<div class="sim-select-wrap">
70-
<select id="runtime-select" aria-label="Select Execution Runtime">
71-
<option value="mpy" selected>MicroPython</option>
72-
<option value="pyodide">Pyodide (CPython)</option>
73-
</select>
74-
</div>
7566
</div>
7667

7768
<div class="sim-header-right">
@@ -139,144 +130,24 @@
139130
<!-- Horizontal Drag Splitter -->
140131
<div class="sim-splitter-h" id="splitter-h" role="separator" aria-orientation="horizontal"></div>
141132

142-
<!-- Bottom Sub-Pane: runtime output / Pyodide terminal -->
143-
<section class="sim-console-pane" id="console-pane" aria-label="Interactive Python Terminal REPL">
133+
<!-- Bottom Sub-Pane: MicroPython runtime output -->
134+
<section class="sim-console-pane" id="console-pane" aria-label="MicroPython runtime output">
144135
<div class="sim-pane-header">
145136
<div class="sim-tab">
146137
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg>
147-
<span>REPL</span>
138+
<span>MicroPython Output</span>
148139
</div>
149-
<button type="button" class="sim-btn sim-icon-btn" id="clear-console-btn" title="Clear REPL Log">
140+
<button type="button" class="sim-btn sim-icon-btn" id="clear-console-btn" title="Clear runtime output">
150141
<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
151142
</button>
152143
</div>
153144
<div class="sim-console-body sim-terminal-body" id="console-log">
154-
<!-- simulator.js mounts the runtime <script> tag here. -->
145+
<!-- simulator.js mounts direct MicroPython output here. -->
155146
</div>
156147
</section>
157148
</section>
158149
</main>
159150

160-
<!--
161-
Pyodide bootstrap. Direct MicroPython is booted by simulator.js using the
162-
vendored runtime and the PyDevices MIP index. Pyodide has nothing bundled,
163-
so every package is a micropip install from
164-
TestPyPI, where they all carry the pydevices- pip prefix. The interpreter
165-
is pinned to a Pyodide whose ABI (2026_0) matches the pyemscripten wheels
166-
published for pydevices-lvgl and pydevices-pygraphics.
167-
-->
168-
<template id="bootstrap-py">
169-
import sys
170-
171-
# TestPyPI first, PyPI as fallback -- the micropip translation of the documented
172-
# pip --index-url / --extra-index-url pair.
173-
INDEX_URLS = [
174-
"https://test.pypi.org/simple/{package_name}/",
175-
"https://pypi.org/simple/{package_name}/",
176-
]
177-
PACKAGES = [
178-
"pydevices-desktop",
179-
"pydevices-lvgl",
180-
"pydevices-pdwidgets",
181-
"pydevices-palettes",
182-
"pydevices-pygraphics",
183-
]
184-
185-
186-
async def _ensure_micropip():
187-
"""micropip ships with Pyodide but is not loaded until asked for."""
188-
try:
189-
import micropip
190-
except ImportError:
191-
from pyodide_js import loadPackage
192-
193-
await loadPackage("micropip")
194-
import micropip
195-
196-
return micropip
197-
198-
199-
async def _refresh_environment():
200-
"""Install the whole stack; Pyodide has none of it bundled."""
201-
micropip = await _ensure_micropip()
202-
203-
for name in PACKAGES:
204-
try:
205-
await micropip.install(name, index_urls=INDEX_URLS)
206-
except Exception as exc:
207-
print("Could not install {}: {}".format(name, exc))
208-
209-
210-
await _refresh_environment()
211-
</template>
212-
213-
<!--
214-
Pyodide tail: run the editor's code as main.py, then open its output-only
215-
main-thread REPL. Direct MicroPython execution is intentionally DOM-free.
216-
-->
217-
<template id="bootstrap-tail">
218-
def _set_board_size():
219-
"""Pin the panel size before board_config builds the display.
220-
221-
Without this, PSDisplay falls back to reading canvas.width/.height off the
222-
DOM. In worker mode that read yields a JS proxy value rather than an int,
223-
and assigning it back to canvas.height fails with "'set' on proxy: trap
224-
returned falsish". Passing real ints through the environment sidesteps the
225-
DOM round-trip entirely, and is what the PyScript gallery does too.
226-
"""
227-
from displaydev import env_set
228-
from pyscript import window
229-
230-
env_set("PYDEVICES_WIDTH", int(window.SIM_WIDTH))
231-
env_set("PYDEVICES_HEIGHT", int(window.SIM_HEIGHT))
232-
233-
234-
def _run_user_code():
235-
from pyscript import window
236-
237-
source = window.getEditorCode()
238-
if not source:
239-
return
240-
with open("main.py", "w") as f:
241-
f.write(source)
242-
sys.modules.pop("main", None)
243-
try:
244-
import main # noqa: F401
245-
except Exception as exc:
246-
_report(exc)
247-
248-
249-
def _report(exc):
250-
if hasattr(sys, "print_exception"):
251-
sys.print_exception(exc)
252-
else:
253-
import traceback
254-
255-
traceback.print_exception(type(exc), exc, exc.__traceback__)
256-
257-
258-
_set_board_size()
259-
_run_user_code()
260-
261-
import code
262-
263-
try:
264-
code.interact(local=globals())
265-
except Exception as exc:
266-
# PyScript's input() raises on the main thread, so code.interact() dies on
267-
# its first prompt and Pyodide's REPL pane would otherwise show a traceback
268-
# instead of a banner. The runtime stays on the main thread on purpose:
269-
# a worker would fix input(), but psdisplay stores a Python object on the
270-
# canvas element, and a PyProxy cannot cross the worker boundary.
271-
if "input()" in str(exc):
272-
print()
273-
print("--- Script finished. This runtime's REPL is output-only. ---")
274-
print("Switch to MicroPython for an interactive prompt, or press RESET")
275-
print("to run your edited code again.")
276-
else:
277-
_report(exc)
278-
</template>
279-
280151
<!-- Toast Notification -->
281152
<div class="sim-toast" id="sim-toast">✓ Link copied to clipboard!</div>
282153

simulator/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "PyDevices Simulator",
33
"short_name": "Simulator",
4-
"description": "Interactive Python Device Playground for LVGL, pdwidgets, pygraphics, and displaydev",
4+
"description": "Interactive MicroPython WebAssembly playground for LVGL, pdwidgets, pygraphics, and displaydev",
55
"start_url": "./",
66
"scope": "./",
77
"display": "standalone",

simulator/pyodide.json

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)