|
5 | 5 | <script>try{if(localStorage.getItem('pydevices-theme')==='light'){document.documentElement.setAttribute('data-theme','light');}}catch(e){}</script> |
6 | 6 | <meta name="viewport" content="width=device-width, initial-scale=1"> |
7 | 7 | <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."> |
9 | 9 | <link rel="icon" type="image/svg+xml" href="/assets/img/logo.svg"> |
10 | 10 | <link rel="manifest" href="manifest.json"> |
11 | 11 | <meta name="theme-color" content="#100e0b"> |
|
18 | 18 | <script src="https://cdnjs.cloudflare.com/ajax/libs/lz-string/1.5.0/lz-string.min.js"></script> |
19 | 19 | <!-- Monaco Editor AMD Loader --> |
20 | 20 | <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"> |
23 | 21 | </head> |
24 | 22 | <body> |
25 | 23 | <!-- Top Global App Bar --> |
|
65 | 63 | </select> |
66 | 64 | </div> |
67 | 65 |
|
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> |
75 | 66 | </div> |
76 | 67 |
|
77 | 68 | <div class="sim-header-right"> |
|
139 | 130 | <!-- Horizontal Drag Splitter --> |
140 | 131 | <div class="sim-splitter-h" id="splitter-h" role="separator" aria-orientation="horizontal"></div> |
141 | 132 |
|
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"> |
144 | 135 | <div class="sim-pane-header"> |
145 | 136 | <div class="sim-tab"> |
146 | 137 | <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> |
148 | 139 | </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"> |
150 | 141 | <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> |
151 | 142 | </button> |
152 | 143 | </div> |
153 | 144 | <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. --> |
155 | 146 | </div> |
156 | 147 | </section> |
157 | 148 | </section> |
158 | 149 | </main> |
159 | 150 |
|
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 | | - |
280 | 151 | <!-- Toast Notification --> |
281 | 152 | <div class="sim-toast" id="sim-toast">✓ Link copied to clipboard!</div> |
282 | 153 |
|
|
0 commit comments