|
| 1 | +// Canvas setup |
| 2 | +// ======================================= |
| 3 | + |
| 4 | +let _gPyodide = null; |
| 5 | + |
| 6 | +function _getEmscriptenCanvas() { |
| 7 | + let canvas = document.getElementById("canvas"); |
| 8 | + |
| 9 | + // WebGL2 context check |
| 10 | + canvas.addEventListener("webglcontextlost", function (event) { |
| 11 | + alert("WebGL context lost, please reload the page"); |
| 12 | + event.preventDefault(); |
| 13 | + }, false); |
| 14 | + |
| 15 | + if (typeof WebGL2RenderingContext === 'undefined') { |
| 16 | + alert("WebGL 2 not supported by this browser"); |
| 17 | + return null; |
| 18 | + } |
| 19 | + return canvas; |
| 20 | +} |
| 21 | + |
| 22 | +async function _passCanvasToPyodide() { |
| 23 | + const canvas = _getEmscriptenCanvas(); |
| 24 | + // console.log("initEmscriptenCanvas canvas:", canvas); |
| 25 | + // Example: Expose canvas to Python |
| 26 | + _gPyodide.canvas.setCanvas3D(canvas); // Set canvas for 3D rendering |
| 27 | + // console.log("initEmscriptenCanvas canvas set"); |
| 28 | +} |
| 29 | + |
| 30 | +// Handle canvas resizing (not called at the moment) |
| 31 | +function _passCanvasSizeToEmscripten() { |
| 32 | + const canvas = _getEmscriptenCanvas(); |
| 33 | + canvas.width = canvas.clientWidth; |
| 34 | + canvas.height = canvas.clientHeight; |
| 35 | + // Inform your rendering context about the resize if necessary |
| 36 | +} |
| 37 | + |
| 38 | +// GUI utilities |
| 39 | +// =============================================== |
| 40 | +function _showLoadingModal() { |
| 41 | +} |
| 42 | + |
| 43 | +function _hideLoadingModal() { |
| 44 | +} |
| 45 | + |
| 46 | +function _updateProgress(progress, message) { |
| 47 | + console.log(`Progress: ${progress} - ${message}`); |
| 48 | +} |
| 49 | + |
| 50 | +function _displayError(message) { |
| 51 | + console.error(message); |
| 52 | +} |
| 53 | + |
| 54 | +function _clearError() { |
| 55 | +} |
| 56 | + |
| 57 | +// Initial loading |
| 58 | +//================================================ |
| 59 | + |
| 60 | + |
| 61 | +// Initialize Pyodide and load packages with progress updates |
| 62 | +async function _loadPyodideAndPackages() { |
| 63 | + try { |
| 64 | + _showLoadingModal(); |
| 65 | + _updateProgress(0, 'Loading Pyodide...'); |
| 66 | + _gPyodide = await loadPyodide(); |
| 67 | + const pythonVersion = _gPyodide.runPython("import sys; sys.version"); |
| 68 | + _updateProgress(7, 'Pyodide loaded.'); |
| 69 | + console.log("Python version:", pythonVersion); |
| 70 | + await _gPyodide.loadPackage("micropip"); |
| 71 | + await _gPyodide.loadPackage("micropip"); // firefox needs this to be loaded twice... |
| 72 | + _updateProgress(10, 'micropip loaded.'); |
| 73 | + |
| 74 | + // Important: |
| 75 | + // SDL support in Pyodide is experimental. The flag is used to bypass certain issues. |
| 76 | + _gPyodide._api._skip_unwind_fatal_error = true; |
| 77 | + |
| 78 | + // Determine the base URL dynamically |
| 79 | + const baseUrl = `${window.location.origin}${window.location.pathname}`; |
| 80 | + console.log('Base URL:', baseUrl); |
| 81 | + |
| 82 | + // List of packages to install |
| 83 | + const packages = [ |
| 84 | + // For imgui_bundle below |
| 85 | + // ----------------------- |
| 86 | + 'numpy', // 2.8 MB |
| 87 | + 'pydantic', // 1.3 + 0.4 MB = 1.7 MB |
| 88 | + 'typing_extensions', // 34 KB |
| 89 | + 'munch', // 10 KB |
| 90 | + 'imgui_bundle', // 9.7 MB (with 3 MB for demos_assets, 6 MB native) |
| 91 | + 'pillow', // 964 KB |
| 92 | + |
| 93 | + // // For fiatlight below |
| 94 | + // // -------------------- |
| 95 | + // 'requests', // 61KB, For word count demo (we download the Hamlet text) |
| 96 | + // 'pandas', // 5.4 MB |
| 97 | + // 'matplotlib', // 6.2 MB |
| 98 | + // 'opencv-python', // 11 MB |
| 99 | + // baseUrl + `/pyodide_dist/fiatlight-0.1.0-py3-none-any.whl`, // 3.5 MB |
| 100 | + // |
| 101 | + // // For scatter_widget_bundle |
| 102 | + // // -------------------------- |
| 103 | + // "scikit-learn", // 6.3 MB |
| 104 | + // "scipy", // 13 MB |
| 105 | + // baseUrl + "/pyodide_dist/scatter_widget_bundle-0.1.0-py3-none-any.whl", // 8.3 KB |
| 106 | + ]; |
| 107 | + |
| 108 | + const totalSteps = packages.length; |
| 109 | + let currentStep = 1; |
| 110 | + |
| 111 | + for (const pkg of packages) { |
| 112 | + _updateProgress(10 + (currentStep / totalSteps) * 80, `Installing ${pkg}...`); |
| 113 | + await _gPyodide.runPythonAsync(` |
| 114 | +import micropip; |
| 115 | +await micropip.install('${pkg}') |
| 116 | + `); |
| 117 | + console.log(`${pkg} loaded.`); |
| 118 | + currentStep++; |
| 119 | + } |
| 120 | + |
| 121 | + _updateProgress(100, 'All packages loaded.'); |
| 122 | + // Optionally, add a slight delay before hiding the modal |
| 123 | + await new Promise(resolve => setTimeout(resolve, 500)); |
| 124 | + _hideLoadingModal(); |
| 125 | + console.log('Pyodide and packages loaded.'); |
| 126 | + } catch (error) { |
| 127 | + console.error('Error loading Pyodide or packages:', error); |
| 128 | + _displayError('Failed to load Pyodide or install packages. See console for details.'); |
| 129 | + _hideLoadingModal(); |
| 130 | + } |
| 131 | +} |
| 132 | + |
| 133 | +// Function to run Python code |
| 134 | +export async function runPythonCode(code) { |
| 135 | + if (!_gPyodide) { |
| 136 | + console.error('Pyodide not loaded yet'); |
| 137 | + displayError('Pyodide is still loading. Please wait a moment and try again.'); |
| 138 | + return; |
| 139 | + } |
| 140 | + |
| 141 | + // Clear previous errors before running new code |
| 142 | + _clearError(); |
| 143 | + |
| 144 | + try { |
| 145 | + // Redirect stdout and stderr |
| 146 | + _gPyodide.setStdout({ |
| 147 | + batched: (s) => console.log(s), |
| 148 | + }); |
| 149 | + _gPyodide.setStderr({ |
| 150 | + batched: (s) => { |
| 151 | + console.error(s); |
| 152 | + _displayError(s); |
| 153 | + }, |
| 154 | + }); |
| 155 | + |
| 156 | + // Execute the code |
| 157 | + await _gPyodide.runPythonAsync(code); |
| 158 | + |
| 159 | + // Optionally, call a specific function |
| 160 | + // await pyodide.runPythonAsync('main()'); |
| 161 | + |
| 162 | + } catch (err) { |
| 163 | + console.error('Caught PythonError:', err); |
| 164 | + _displayError(err.toString()); |
| 165 | + } |
| 166 | +} |
| 167 | + |
| 168 | + |
| 169 | +// |
| 170 | +export async function initializePyodideHelper() |
| 171 | +{ |
| 172 | + await _loadPyodideAndPackages(); |
| 173 | + _passCanvasToPyodide(); |
| 174 | +} |
0 commit comments