new example, fix input bug
This commit is contained in:
		
										
											Binary file not shown.
										
									
								
							| @ -1,5 +1,5 @@ | ||||
|  | ||||
| const pwa_version = "01_hello_world_202210161053" | ||||
| const pwa_version = "01_hello_world_202210161638" | ||||
| const assets = ["./index.html", | ||||
|     "./main.py", | ||||
|     "./resources/bootstrap.css", | ||||
|  | ||||
										
											Binary file not shown.
										
									
								
							| @ -1,5 +1,5 @@ | ||||
|  | ||||
| const pwa_version = "02_image_filter_202210161053" | ||||
| const pwa_version = "02_image_filter_202210161638" | ||||
| const assets = ["./index.html", | ||||
|     "./main.py", | ||||
|     "./resources/bootstrap.css", | ||||
|  | ||||
							
								
								
									
										16
									
								
								examples/03_numpy_grid_demo/config.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								examples/03_numpy_grid_demo/config.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| { | ||||
|     "root_folder": "03_numpy_grid_demo", | ||||
|     "title": "03_numpy_grid_demo", | ||||
|     "packages": [ | ||||
|         "numpy" | ||||
|     ], | ||||
|     "paths": [], | ||||
|     "pyscript_css_url": "https://pyscript.net/latest/pyscript.css", | ||||
|     "pyscript_js_url": "https://pyscript.net/latest/pyscript.js", | ||||
|     "pyscript_py_url": "https://pyscript.net/latest/pyscript.py", | ||||
|     "bootstrap_css_url": "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css", | ||||
|     "bootstrap_js_url": "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js", | ||||
|     "pyscript_bootstrap_templates_wheel_url": "../dist/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl", | ||||
|     "pwa_bg_color": "#000000", | ||||
|     "pwa_theme_color": "#ffffff" | ||||
| } | ||||
							
								
								
									
										36
									
								
								examples/03_numpy_grid_demo/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								examples/03_numpy_grid_demo/index.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | ||||
|  | ||||
| <!DOCTYPE html> | ||||
| <html lang="en" style="width: 100%;height: 100%"> | ||||
|     <head id="head"> | ||||
|         <meta charset="UTF-8"> | ||||
|         <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|  | ||||
|         <link rel="manifest" href="manifest.json" /> | ||||
|  | ||||
|         <script src="./resources/pwa.js"></script> | ||||
|  | ||||
|         <link rel="stylesheet" href="./resources/pyscript.css" /> | ||||
|         <script defer src="./resources/pyscript.js"></script> | ||||
|         <!-- Bootstrap CSS --> | ||||
|         <link href="./resources/bootstrap.css" rel="stylesheet"> | ||||
|  | ||||
|         <title>03_numpy_grid_demo</title> | ||||
|     </head> | ||||
|     <body style="width: 100%; height: 100%"> | ||||
|         <py-config type="json"> | ||||
|             { | ||||
|     "autoclose_loader": true, | ||||
|     "packages": [ | ||||
|         "./resources/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl", | ||||
|         "numpy" | ||||
|     ], | ||||
|     "paths": [] | ||||
| } | ||||
|         </py-config> | ||||
|         <div id="pyscript_app" style="height: 100%; min-height: 100%"></div> | ||||
|         <py-script src="./main.py"></py-script> | ||||
|  | ||||
|         <script src="./resources/bootstrap.js"></script> | ||||
|     </body> | ||||
| </html> | ||||
|      | ||||
							
								
								
									
										114
									
								
								examples/03_numpy_grid_demo/main.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								examples/03_numpy_grid_demo/main.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,114 @@ | ||||
|  | ||||
| from pyscript_bootstrap_templates import bootstrap_templates | ||||
| from pyscript_bootstrap_templates import bootstrap_HTML as bHTML | ||||
| from pyscript_bootstrap_templates import HTML as HTML | ||||
| from pyscript_bootstrap_templates import bootstrap_inputs as bInputs | ||||
|  | ||||
| import numpy as np | ||||
|  | ||||
| # wrapper class to create editable numpy matrices: | ||||
| class NumpyGrid2D(HTML.Table, bHTML.BootstrapContainer): | ||||
|     def __init__(self, numpy_grid: np.ndarray, parent: HTML.Element = None): | ||||
|  | ||||
|  | ||||
|         # store numpy grid | ||||
|         self._numpy_grid = numpy_grid.copy() | ||||
|         m,n = self._numpy_grid.shape | ||||
|  | ||||
|         super().__init__(parent=parent) | ||||
|  | ||||
|         self.m = 0 | ||||
|         self.p = 0 | ||||
|  | ||||
|         # create a grid of inputs | ||||
|         self._inputs = [] | ||||
|          | ||||
|         for i in range(m): | ||||
|             row = [] | ||||
|             # to keep it simple: use plain html table elements | ||||
|             tr = HTML.Tr(parent=self) | ||||
|             for j in range(n): | ||||
|                 th = HTML.Th(parent=tr) | ||||
|                 cell = bInputs.InputFloat(parent=th) | ||||
|                 cell.p = 0 | ||||
|                 cell.m = 0 | ||||
|                 cell.value = self._numpy_grid[i,j] | ||||
|                 row.append(cell) | ||||
|             self._inputs.append(row) | ||||
|      | ||||
|     def _update_ndarray(self): | ||||
|         for i, row in enumerate(self._inputs): | ||||
|             for j, cell in enumerate(row): | ||||
|                 self._numpy_grid[i,j] = float(cell.value) | ||||
|                  | ||||
|      | ||||
|     @property | ||||
|     def numpy_grid(self) -> np.ndarray: | ||||
|         self._update_ndarray() | ||||
|         return self._numpy_grid.copy() | ||||
|      | ||||
|     @numpy_grid.setter | ||||
|     def numpy_grid(self, value:np.ndarray): | ||||
|         assert value.shape == self._numpy_grid.shape | ||||
|         self._numpy_grid = value.copy() | ||||
|         # update input grid | ||||
|  | ||||
|         for i, row in enumerate(self._inputs): | ||||
|             for j, cell in enumerate(row): | ||||
|                 cell.value = self._numpy_grid[i,j] | ||||
|      | ||||
|  | ||||
| # create default values | ||||
| matrix_a = np.array([[1,2,3],[4,5,6],[7,8,9]]) | ||||
| matrix_b = np.array([[1,2,3],[4,5,6],[7,8,9]]) | ||||
|  | ||||
| matrix_result = np.zeros((3,3)) | ||||
|  | ||||
| app = bootstrap_templates.PyScriptBootstrapDashboard( | ||||
|     parent_element="pyscript_app", brand_name="03_numpy_grid_demo") | ||||
| div = bHTML.BootstrapContainer("Matrix operations", parent=app.sidebar) | ||||
| div.font_size = 4 | ||||
|  | ||||
| matrix_row = bHTML.Row(parent=app.main) | ||||
| matrix_row.p = 1 | ||||
| matrix_row.m = 1 | ||||
| matrix_row.display_property = bHTML.DisplayProperty.INLINE_FLEX | ||||
|  | ||||
| # create row of three matrices | ||||
| mat_widget_a = NumpyGrid2D(matrix_a) | ||||
| mat_widget_b = NumpyGrid2D(matrix_b) | ||||
| mat_widget_result = NumpyGrid2D(matrix_result) | ||||
|  | ||||
| # just for fanciness: wrap the matrices in bootstrap cards | ||||
| col_a = bHTML.Col(parent=matrix_row) | ||||
| col_b = bHTML.Col(parent=matrix_row) | ||||
| col_result = bHTML.Col(parent=matrix_row) | ||||
|  | ||||
| card_a = bHTML.Card(mat_widget_a, card_header=bHTML.Div("Matrix A"), parent=col_a) | ||||
| card_b = bHTML.Card(mat_widget_b, card_header=bHTML.Div("Matrix B"), parent=col_b) | ||||
| card_result = bHTML.Card(mat_widget_result, card_header=bHTML.Div("Result"), parent=col_result) | ||||
|  | ||||
| card_a.shadow = bHTML.Shadow.MEDIUM | ||||
| card_b.shadow = bHTML.Shadow.MEDIUM | ||||
| card_result.shadow = bHTML.Shadow.MEDIUM | ||||
|  | ||||
| operations = { | ||||
|     '+': np.add, | ||||
|     '-': np.subtract, | ||||
|     '*': np.multiply, | ||||
|     '/': np.divide, | ||||
|     'dot product': np.dot | ||||
| } | ||||
|  | ||||
| for op in operations: | ||||
|     btn = bHTML.ButtonPrimary(op, parent=app.sidebar) | ||||
|     btn.w = 100 | ||||
|     btn.m = 1 | ||||
|     def onclick(event, numpy_func=operations[op]): | ||||
|         mat_widget_result.numpy_grid = numpy_func(mat_widget_a.numpy_grid, mat_widget_b.numpy_grid) | ||||
|     btn.onclick = onclick | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|      | ||||
							
								
								
									
										1
									
								
								examples/03_numpy_grid_demo/manifest.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								examples/03_numpy_grid_demo/manifest.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| {"name": "03_numpy_grid_demo", "short_name": "03_numpy_grid_demo", "start_url": "./index.html", "scope": ".", "display": "standalone", "background_color": "#000000", "theme_color": "#ffffff", "icons": [{"src": "resources/icon-512x512.png", "type": "image/png", "sizes": "512x512"}]} | ||||
							
								
								
									
										7
									
								
								examples/03_numpy_grid_demo/resources/bootstrap.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								examples/03_numpy_grid_demo/resources/bootstrap.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										7
									
								
								examples/03_numpy_grid_demo/resources/bootstrap.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								examples/03_numpy_grid_demo/resources/bootstrap.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								examples/03_numpy_grid_demo/resources/icon-512x512.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								examples/03_numpy_grid_demo/resources/icon-512x512.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 18 KiB | 
							
								
								
									
										10
									
								
								examples/03_numpy_grid_demo/resources/pwa.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								examples/03_numpy_grid_demo/resources/pwa.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
|  | ||||
| if ("serviceWorker" in navigator) { | ||||
|     window.addEventListener("load", function () { | ||||
|         navigator.serviceWorker | ||||
|             .register("./serviceWorker.js") | ||||
|             .then(res => console.log("service worker registered", res)) | ||||
|             .catch(err => console.log("service worker not registered", err)) | ||||
|     }) | ||||
| } | ||||
|      | ||||
							
								
								
									
										244
									
								
								examples/03_numpy_grid_demo/resources/pyscript.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										244
									
								
								examples/03_numpy_grid_demo/resources/pyscript.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,244 @@ | ||||
| :not(:defined) { | ||||
|     display: none | ||||
| } | ||||
|  | ||||
| html{ | ||||
|     font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; | ||||
|     line-height: 1.5; | ||||
| } | ||||
|  | ||||
| .spinner::after { | ||||
|     content: ''; | ||||
|     box-sizing: border-box; | ||||
|     width: 40px; | ||||
|     height: 40px; | ||||
|     position: absolute; | ||||
|     top: calc(40% - 20px); | ||||
|     left: calc(50% - 20px); | ||||
|     border-radius: 50%; | ||||
| } | ||||
|  | ||||
| .spinner.smooth::after { | ||||
|     border-top: 4px solid rgba(255, 255, 255, 1); | ||||
|     border-left: 4px solid rgba(255, 255, 255, 1); | ||||
|     border-right: 4px solid rgba(255, 255, 255, 0); | ||||
|     animation: spinner 0.6s linear infinite; | ||||
| } | ||||
| @keyframes spinner { | ||||
|     to { | ||||
|         transform: rotate(360deg); | ||||
|     } | ||||
| } | ||||
|  | ||||
| .label { | ||||
|     text-align: center; | ||||
|     width: 100%; | ||||
|     display: block; | ||||
|     color: rgba(255, 255, 255, 0.8); | ||||
|     font-size: 0.8rem; | ||||
|     margin-top: 6rem; | ||||
| } | ||||
|  | ||||
| /* Pop-up second layer begin */ | ||||
|  | ||||
| .py-overlay { | ||||
|     position: fixed; | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|     align-items: center; | ||||
|     color: white; | ||||
|     top: 0; | ||||
|     bottom: 0; | ||||
|     left: 0; | ||||
|     right: 0; | ||||
|     background: rgba(0, 0, 0, 0.5); | ||||
|     transition: opacity 500ms; | ||||
|     visibility: hidden; | ||||
|     color: visible; | ||||
|     opacity: 1; | ||||
| } | ||||
|  | ||||
| .py-overlay { | ||||
|     visibility: visible; | ||||
|     opacity: 1; | ||||
| } | ||||
|  | ||||
| .py-pop-up { | ||||
|     text-align: center; | ||||
|     width: 600px; | ||||
| } | ||||
|  | ||||
| .py-pop-up p { | ||||
|     margin: 5px; | ||||
| } | ||||
|  | ||||
| .py-pop-up a { | ||||
|     position: absolute; | ||||
|     color: white; | ||||
|     text-decoration: none; | ||||
|     font-size: 200%; | ||||
|     top: 3.5%; | ||||
|     right: 5%; | ||||
| } | ||||
|  | ||||
| .py-box{ | ||||
|     display: flex; | ||||
|     flex-direction: row; | ||||
|     justify-content: flex-start; | ||||
| } | ||||
|  | ||||
| .py-box div.py-box-child * | ||||
| { | ||||
|     max-width: 100%; | ||||
| } | ||||
|  | ||||
| .py-repl-box{ | ||||
|     flex-direction: column; | ||||
| } | ||||
|  | ||||
| .editor-box{ | ||||
|     --tw-border-opacity: 1; | ||||
|     border-color: rgba(209, 213, 219, var(--tw-border-opacity)); | ||||
|     border-width: 1px; | ||||
|     position: relative; | ||||
|     --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); | ||||
|     --tw-ring-offset-width: 0px; | ||||
|     --tw-ring-offset-color: #fff; | ||||
|     --tw-ring-color: rgba(59, 130, 246, 0.5); | ||||
|     --tw-ring-offset-shadow: 0 0 #0000; | ||||
|     --tw-ring-shadow: 0 0 #0000; | ||||
|     --tw-shadow: 0 0 #0000; | ||||
|     position: relative; | ||||
|  | ||||
|     box-sizing: border-box; | ||||
|     border-width: 1px; | ||||
|     border-style: solid; | ||||
|     border-color: rgb(209, 213, 219) | ||||
| } | ||||
|  | ||||
| .editor-box:hover button{ | ||||
|     opacity: 1; | ||||
| } | ||||
|  | ||||
| .repl-play-button{ | ||||
|     opacity: 0; | ||||
|     bottom: 0.25rem; | ||||
|     right: 0.25rem; | ||||
|     position: absolute; | ||||
|     padding: 0; | ||||
|     line-height: inherit; | ||||
|     color: inherit; | ||||
|     cursor: pointer; | ||||
|     background-color: transparent; | ||||
|     background-image: none; | ||||
|     -webkit-appearance: button; | ||||
|     text-transform: none; | ||||
|     font-family: inherit; | ||||
|     font-size: 100%; | ||||
|     margin: 0; | ||||
|     text-rendering: auto; | ||||
|     letter-spacing: normal; | ||||
|     word-spacing: normal; | ||||
|     line-height: normal; | ||||
|     text-transform: none; | ||||
|     text-indent: 0px; | ||||
|     text-shadow: none; | ||||
|     display: inline-block; | ||||
|     text-align: center; | ||||
|     align-items: flex-start; | ||||
|     cursor: default; | ||||
|     box-sizing: border-box; | ||||
|     background-color: -internal-light-dark(rgb(239, 239, 239), rgb(59, 59, 59)); | ||||
|     margin: 0em; | ||||
|     padding: 1px 6px; | ||||
|     border: 0; | ||||
| } | ||||
|  | ||||
| .repl-play-button:hover{ | ||||
|     opacity: 1; | ||||
| } | ||||
|  | ||||
| .py-title{ | ||||
|     text-transform: uppercase; | ||||
|     text-align: center; | ||||
| } | ||||
|  | ||||
| .py-title h1{ | ||||
|     font-weight: 700; | ||||
|     font-size: 1.875rem; | ||||
| } | ||||
|  | ||||
| .py-input{ | ||||
|     padding: 0.5rem; | ||||
|     --tw-border-opacity: 1; | ||||
|     border-color: rgba(209, 213, 219, var(--tw-border-opacity)); | ||||
|     border-width: 1px; | ||||
|    border-radius: 0.25rem; | ||||
|     margin-right: 0.75rem; | ||||
|     border-style: solid; | ||||
|     width: auto; | ||||
| } | ||||
|  | ||||
| .py-box input.py-input{ | ||||
|     width: -webkit-fill-available; | ||||
| } | ||||
|  | ||||
| .central-content{ | ||||
|     max-width: 20rem; | ||||
|     margin-left: auto; | ||||
|     margin-right: auto; | ||||
| } | ||||
|  | ||||
| input { | ||||
|   text-rendering: auto; | ||||
|   color: -internal-light-dark(black, white); | ||||
|   letter-spacing: normal; | ||||
|   word-spacing: normal; | ||||
|   line-height: normal; | ||||
|   text-transform: none; | ||||
|   text-indent: 0px; | ||||
|   text-shadow: none; | ||||
|   display: inline-block; | ||||
|   text-align: start; | ||||
|   appearance: auto; | ||||
|   -webkit-rtl-ordering: logical; | ||||
|   cursor: text; | ||||
|   background-color: -internal-light-dark(rgb(255, 255, 255), rgb(59, 59, 59)); | ||||
|   margin: 0em; | ||||
|   padding: 1px 2px; | ||||
|   border-width: 2px; | ||||
|   border-style: inset; | ||||
|   border-color: -internal-light-dark(rgb(118, 118, 118), rgb(133, 133, 133)); | ||||
|   border-image: initial; | ||||
| } | ||||
|  | ||||
| .py-button{ | ||||
|     --tw-text-opacity: 1; | ||||
|     color: rgba(255, 255, 255, var(--tw-text-opacity)); | ||||
|     padding: 0.5rem; | ||||
|     --tw-bg-opacity: 1; | ||||
|     background-color: rgba(37, 99, 235, var(--tw-bg-opacity)); | ||||
|     --tw-border-opacity: 1; | ||||
|     border-color: rgba(37, 99, 235, var(--tw-border-opacity)); | ||||
|     border-width: 1px; | ||||
|     border-radius: 0.25rem; | ||||
| } | ||||
|  | ||||
| .py-li-element p{ | ||||
|     margin: 5px; | ||||
| } | ||||
|  | ||||
| .py-li-element p{ | ||||
|     display: inline; | ||||
| } | ||||
|  | ||||
| button, input, optgroup, select, textarea { | ||||
|     font-family: inherit; | ||||
|     font-size: 100%; | ||||
|     line-height: 1.15; | ||||
|     margin: 0; | ||||
| } | ||||
|  | ||||
| .line-through { | ||||
|     text-decoration: line-through; | ||||
| } | ||||
							
								
								
									
										27856
									
								
								examples/03_numpy_grid_demo/resources/pyscript.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27856
									
								
								examples/03_numpy_grid_demo/resources/pyscript.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										424
									
								
								examples/03_numpy_grid_demo/resources/pyscript.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										424
									
								
								examples/03_numpy_grid_demo/resources/pyscript.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,424 @@ | ||||
| import asyncio | ||||
| import base64 | ||||
| import io | ||||
| import sys | ||||
| import time | ||||
| from textwrap import dedent | ||||
|  | ||||
| import micropip  # noqa: F401 | ||||
| from js import console, document | ||||
|  | ||||
| loop = asyncio.get_event_loop() | ||||
|  | ||||
| MIME_METHODS = { | ||||
|     "__repr__": "text/plain", | ||||
|     "_repr_html_": "text/html", | ||||
|     "_repr_markdown_": "text/markdown", | ||||
|     "_repr_svg_": "image/svg+xml", | ||||
|     "_repr_png_": "image/png", | ||||
|     "_repr_pdf_": "application/pdf", | ||||
|     "_repr_jpeg_": "image/jpeg", | ||||
|     "_repr_latex": "text/latex", | ||||
|     "_repr_json_": "application/json", | ||||
|     "_repr_javascript_": "application/javascript", | ||||
|     "savefig": "image/png", | ||||
| } | ||||
|  | ||||
|  | ||||
| def render_image(mime, value, meta): | ||||
|     data = f"data:{mime};charset=utf-8;base64,{value}" | ||||
|     attrs = " ".join(['{k}="{v}"' for k, v in meta.items()]) | ||||
|     return f'<img src="{data}" {attrs}</img>' | ||||
|  | ||||
|  | ||||
| def identity(value, meta): | ||||
|     return value | ||||
|  | ||||
|  | ||||
| MIME_RENDERERS = { | ||||
|     "text/plain": identity, | ||||
|     "text/html": identity, | ||||
|     "image/png": lambda value, meta: render_image("image/png", value, meta), | ||||
|     "image/jpeg": lambda value, meta: render_image("image/jpeg", value, meta), | ||||
|     "image/svg+xml": identity, | ||||
|     "application/json": identity, | ||||
|     "application/javascript": lambda value, meta: f"<script>{value}</script>", | ||||
| } | ||||
|  | ||||
|  | ||||
| def eval_formatter(obj, print_method): | ||||
|     """ | ||||
|     Evaluates a formatter method. | ||||
|     """ | ||||
|     if print_method == "__repr__": | ||||
|         return repr(obj) | ||||
|     elif hasattr(obj, print_method): | ||||
|         if print_method == "savefig": | ||||
|             buf = io.BytesIO() | ||||
|             obj.savefig(buf, format="png") | ||||
|             buf.seek(0) | ||||
|             return base64.b64encode(buf.read()).decode("utf-8") | ||||
|         return getattr(obj, print_method)() | ||||
|     elif print_method == "_repr_mimebundle_": | ||||
|         return {}, {} | ||||
|     return None | ||||
|  | ||||
|  | ||||
| def format_mime(obj): | ||||
|     """ | ||||
|     Formats object using _repr_x_ methods. | ||||
|     """ | ||||
|     if isinstance(obj, str): | ||||
|         return obj, "text/plain" | ||||
|  | ||||
|     mimebundle = eval_formatter(obj, "_repr_mimebundle_") | ||||
|     if isinstance(mimebundle, tuple): | ||||
|         format_dict, _ = mimebundle | ||||
|     else: | ||||
|         format_dict = mimebundle | ||||
|  | ||||
|     output, not_available = None, [] | ||||
|     for method, mime_type in reversed(MIME_METHODS.items()): | ||||
|         if mime_type in format_dict: | ||||
|             output = format_dict[mime_type] | ||||
|         else: | ||||
|             output = eval_formatter(obj, method) | ||||
|  | ||||
|         if output is None: | ||||
|             continue | ||||
|         elif mime_type not in MIME_RENDERERS: | ||||
|             not_available.append(mime_type) | ||||
|             continue | ||||
|         break | ||||
|     if output is None: | ||||
|         if not_available: | ||||
|             console.warning( | ||||
|                 f"Rendered object requested unavailable MIME renderers: {not_available}" | ||||
|             ) | ||||
|         output = repr(output) | ||||
|         mime_type = "text/plain" | ||||
|     elif isinstance(output, tuple): | ||||
|         output, meta = output | ||||
|     else: | ||||
|         meta = {} | ||||
|     return MIME_RENDERERS[mime_type](output, meta), mime_type | ||||
|  | ||||
|  | ||||
| class PyScript: | ||||
|     loop = loop | ||||
|  | ||||
|     @staticmethod | ||||
|     def write(element_id, value, append=False, exec_id=0): | ||||
|         """Writes value to the element with id "element_id""" | ||||
|         console.log(f"APPENDING: {append} ==> {element_id} --> {value}") | ||||
|         if append: | ||||
|             child = document.createElement("div") | ||||
|             element = document.querySelector(f"#{element_id}") | ||||
|             if not element: | ||||
|                 return | ||||
|             exec_id = exec_id or element.childElementCount + 1 | ||||
|             element_id = child.id = f"{element_id}-{exec_id}" | ||||
|             element.appendChild(child) | ||||
|  | ||||
|         element = document.getElementById(element_id) | ||||
|         html, mime_type = format_mime(value) | ||||
|         if mime_type in ("application/javascript", "text/html"): | ||||
|             script_element = document.createRange().createContextualFragment(html) | ||||
|             element.appendChild(script_element) | ||||
|         else: | ||||
|             element.innerHTML = html | ||||
|  | ||||
|     @staticmethod | ||||
|     def run_until_complete(f): | ||||
|         _ = loop.run_until_complete(f) | ||||
|  | ||||
|  | ||||
| class Element: | ||||
|     def __init__(self, element_id, element=None): | ||||
|         self._id = element_id | ||||
|         self._element = element | ||||
|  | ||||
|     @property | ||||
|     def id(self): | ||||
|         return self._id | ||||
|  | ||||
|     @property | ||||
|     def element(self): | ||||
|         """Return the dom element""" | ||||
|         if not self._element: | ||||
|             self._element = document.querySelector(f"#{self._id}") | ||||
|         return self._element | ||||
|  | ||||
|     @property | ||||
|     def value(self): | ||||
|         return self.element.value | ||||
|  | ||||
|     @property | ||||
|     def innerHtml(self): | ||||
|         return self.element.innerHtml | ||||
|  | ||||
|     def write(self, value, append=False): | ||||
|         console.log(f"Element.write: {value} --> {append}") | ||||
|         # TODO: it should be the opposite... pyscript.write should use the Element.write | ||||
|         #       so we can consolidate on how we write depending on the element type | ||||
|         pyscript.write(self._id, value, append=append) | ||||
|  | ||||
|     def clear(self): | ||||
|         if hasattr(self.element, "value"): | ||||
|             self.element.value = "" | ||||
|         else: | ||||
|             self.write("", append=False) | ||||
|  | ||||
|     def select(self, query, from_content=False): | ||||
|         el = self.element | ||||
|         if from_content: | ||||
|             el = el.content | ||||
|  | ||||
|         _el = el.querySelector(query) | ||||
|         if _el: | ||||
|             return Element(_el.id, _el) | ||||
|         else: | ||||
|             console.log(f"WARNING: can't find element matching query {query}") | ||||
|  | ||||
|     def clone(self, new_id=None, to=None): | ||||
|         if new_id is None: | ||||
|             new_id = self.element.id | ||||
|  | ||||
|         clone = self.element.cloneNode(True) | ||||
|         clone.id = new_id | ||||
|  | ||||
|         if to: | ||||
|             to.element.appendChild(clone) | ||||
|  | ||||
|         # Inject it into the DOM | ||||
|         self.element.after(clone) | ||||
|  | ||||
|         return Element(clone.id, clone) | ||||
|  | ||||
|     def remove_class(self, classname): | ||||
|         if isinstance(classname, list): | ||||
|             for cl in classname: | ||||
|                 self.remove_class(cl) | ||||
|         else: | ||||
|             self.element.classList.remove(classname) | ||||
|  | ||||
|     def add_class(self, classname): | ||||
|         self.element.classList.add(classname) | ||||
|  | ||||
|  | ||||
| def add_classes(element, class_list): | ||||
|     for klass in class_list.split(" "): | ||||
|         element.classList.add(klass) | ||||
|  | ||||
|  | ||||
| def create(what, id_=None, classes=""): | ||||
|     element = document.createElement(what) | ||||
|     if id_: | ||||
|         element.id = id_ | ||||
|     add_classes(element, classes) | ||||
|     return Element(id_, element) | ||||
|  | ||||
|  | ||||
| class PyWidgetTheme: | ||||
|     def __init__(self, main_style_classes): | ||||
|         self.main_style_classes = main_style_classes | ||||
|  | ||||
|     def theme_it(self, widget): | ||||
|         for klass in self.main_style_classes.split(" "): | ||||
|             widget.classList.add(klass) | ||||
|  | ||||
|  | ||||
| class PyItemTemplate(Element): | ||||
|     label_fields = None | ||||
|  | ||||
|     def __init__(self, data, labels=None, state_key=None, parent=None): | ||||
|         self.data = data | ||||
|  | ||||
|         self.register_parent(parent) | ||||
|  | ||||
|         if not labels: | ||||
|             labels = list(self.data.keys()) | ||||
|         self.labels = labels | ||||
|  | ||||
|         self.state_key = state_key | ||||
|  | ||||
|         super().__init__(self._id) | ||||
|  | ||||
|     def register_parent(self, parent): | ||||
|         self._parent = parent | ||||
|         if parent: | ||||
|             self._id = f"{self._parent._id}-c-{len(self._parent._children)}" | ||||
|             self.data["id"] = self._id | ||||
|         else: | ||||
|             self._id = None | ||||
|  | ||||
|     def create(self): | ||||
|         console.log("creating section") | ||||
|         new_child = create("section", self._id, "task bg-white my-1") | ||||
|         console.log("creating values") | ||||
|  | ||||
|         console.log("creating innerHtml") | ||||
|         new_child._element.innerHTML = dedent( | ||||
|             f""" | ||||
|             <label for="flex items-center p-2 "> | ||||
|               <input class="mr-2" type="checkbox" class="task-check"> | ||||
|               <p class="m-0 inline">{self.render_content()}</p> | ||||
|             </label> | ||||
|             """ | ||||
|         ) | ||||
|  | ||||
|         console.log("returning") | ||||
|         return new_child | ||||
|  | ||||
|     def on_click(self, evt): | ||||
|         pass | ||||
|  | ||||
|     def pre_append(self): | ||||
|         pass | ||||
|  | ||||
|     def post_append(self): | ||||
|         self.element.click = self.on_click | ||||
|         self.element.onclick = self.on_click | ||||
|  | ||||
|         self._post_append() | ||||
|  | ||||
|     def _post_append(self): | ||||
|         pass | ||||
|  | ||||
|     def strike(self, value, extra=None): | ||||
|         if value: | ||||
|             self.add_class("line-through") | ||||
|         else: | ||||
|             self.remove_class("line-through") | ||||
|  | ||||
|     def render_content(self): | ||||
|         return " - ".join([self.data[f] for f in self.labels]) | ||||
|  | ||||
|  | ||||
| class PyListTemplate: | ||||
|     theme = PyWidgetTheme("flex flex-col-reverse mt-8 mx-8") | ||||
|     item_class = PyItemTemplate | ||||
|  | ||||
|     def __init__(self, parent): | ||||
|         self.parent = parent | ||||
|         self._children = [] | ||||
|         self._id = self.parent.id | ||||
|  | ||||
|     @property | ||||
|     def children(self): | ||||
|         return self._children | ||||
|  | ||||
|     @property | ||||
|     def data(self): | ||||
|         return [c.data for c in self._children] | ||||
|  | ||||
|     def render_children(self): | ||||
|         binds = {} | ||||
|         for i, c in enumerate(self._children): | ||||
|             txt = c.element.innerHTML | ||||
|             rnd = str(time.time()).replace(".", "")[-5:] | ||||
|             new_id = f"{c.element.id}-{i}-{rnd}" | ||||
|             binds[new_id] = c.element.id | ||||
|             txt = txt.replace(">", f" id='{new_id}'>") | ||||
|             print(txt) | ||||
|  | ||||
|         def foo(evt): | ||||
|             console.log(evt) | ||||
|             evtEl = evt.srcElement | ||||
|             srcEl = Element(binds[evtEl.id]) | ||||
|             srcEl.element.onclick() | ||||
|             evtEl.classList = srcEl.element.classList | ||||
|  | ||||
|         for new_id in binds: | ||||
|             Element(new_id).element.onclick = foo | ||||
|  | ||||
|     def connect(self): | ||||
|         self.md = main_div = document.createElement("div") | ||||
|         main_div.id = self._id + "-list-tasks-container" | ||||
|  | ||||
|         if self.theme: | ||||
|             self.theme.theme_it(main_div) | ||||
|  | ||||
|         self.parent.appendChild(main_div) | ||||
|  | ||||
|     def add(self, *args, **kws): | ||||
|         if not isinstance(args[0], self.item_class): | ||||
|             child = self.item_class(*args, **kws) | ||||
|         else: | ||||
|             child = args[0] | ||||
|         child.register_parent(self) | ||||
|         return self._add(child) | ||||
|  | ||||
|     def _add(self, child_elem): | ||||
|         console.log("appending child", child_elem.element) | ||||
|         self.pre_child_append(child_elem) | ||||
|         child_elem.pre_append() | ||||
|         self._children.append(child_elem) | ||||
|         self.md.appendChild(child_elem.create().element) | ||||
|         child_elem.post_append() | ||||
|         self.child_appended(child_elem) | ||||
|         return child_elem | ||||
|  | ||||
|     def pre_child_append(self, child): | ||||
|         pass | ||||
|  | ||||
|     def child_appended(self, child): | ||||
|         """Overwrite me to define logic""" | ||||
|         pass | ||||
|  | ||||
|  | ||||
| class OutputCtxManager: | ||||
|     def __init__(self, out=None, output_to_console=True, append=True): | ||||
|         self._out = out | ||||
|         self._prev = out | ||||
|         self.output_to_console = output_to_console | ||||
|         self._append = append | ||||
|  | ||||
|     def change(self, out=None, err=None, output_to_console=True, append=True): | ||||
|         self._prev = self._out | ||||
|         self._out = out | ||||
|         self.output_to_console = output_to_console | ||||
|         self._append = append | ||||
|         console.log("----> changed out to", self._out, self._append) | ||||
|  | ||||
|     def revert(self): | ||||
|         console.log("----> reverted") | ||||
|         self._out = self._prev | ||||
|  | ||||
|     def write(self, txt): | ||||
|         console.log("writing to", self._out, txt, self._append) | ||||
|         if self._out: | ||||
|             pyscript.write(self._out, txt, append=self._append) | ||||
|         if self.output_to_console: | ||||
|             console.log(self._out, txt) | ||||
|  | ||||
|  | ||||
| class OutputManager: | ||||
|     def __init__(self, out=None, err=None, output_to_console=True, append=True): | ||||
|         sys.stdout = self._out_manager = OutputCtxManager( | ||||
|             out, output_to_console, append | ||||
|         ) | ||||
|         sys.stderr = self._err_manager = OutputCtxManager( | ||||
|             err, output_to_console, append | ||||
|         ) | ||||
|         self.output_to_console = output_to_console | ||||
|         self._append = append | ||||
|  | ||||
|     def change(self, out=None, err=None, output_to_console=True, append=True): | ||||
|         self._out_manager.change(out, output_to_console, append) | ||||
|         sys.stdout = self._out_manager | ||||
|         self._err_manager.change(err, output_to_console, append) | ||||
|         sys.stderr = self._err_manager | ||||
|         self.output_to_console = output_to_console | ||||
|         self.append = append | ||||
|  | ||||
|     def revert(self): | ||||
|         self._out_manager.revert() | ||||
|         self._err_manager.revert() | ||||
|         sys.stdout = self._out_manager | ||||
|         sys.stderr = self._err_manager | ||||
|         console.log("----> reverted") | ||||
|  | ||||
|  | ||||
| pyscript = PyScript() | ||||
| output_manager = OutputManager() | ||||
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										51
									
								
								examples/03_numpy_grid_demo/serviceWorker.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								examples/03_numpy_grid_demo/serviceWorker.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | ||||
|  | ||||
| const pwa_version = "03_numpy_grid_demo_202210161638" | ||||
| const assets = ["./index.html", | ||||
|     "./main.py", | ||||
|     "./resources/bootstrap.css", | ||||
|     "./resources/bootstrap.js", | ||||
|     "./resources/pyscript.css", | ||||
|     "./resources/pyscript.js", | ||||
|     "./resources/pyscript.py", | ||||
|     "./resources/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl", | ||||
|     "./resources/pwa.js", | ||||
|     "./site.js", | ||||
| ] | ||||
| self.addEventListener("install", installEvent => { | ||||
|     installEvent.waitUntil( | ||||
|         caches.open(pwa_version).then(cache => { | ||||
|             cache.addAll(assets).then(r => { | ||||
|                 console.log("Cache assets downloaded"); | ||||
|             }).catch(err => console.log("Error caching item", err)) | ||||
|             console.log(`Cache ${pwa_version} opened.`); | ||||
|         }).catch(err => console.log("Error opening cache", err)) | ||||
|     ) | ||||
| }) | ||||
|  | ||||
| self.addEventListener('activate', e => { | ||||
| 	console.log('Service Worker: Activated'); | ||||
| }); | ||||
|   | ||||
| self.addEventListener('fetch', event => { | ||||
|     if (event.request.method != 'GET') | ||||
|         return; | ||||
| 	event.respondWith((async () => { | ||||
|         const cachedResponse = await caches.match(event.request); | ||||
|         if (cachedResponse) { | ||||
|             return cachedResponse; | ||||
|         } | ||||
|  | ||||
|         const response = await fetch(event.request); | ||||
|  | ||||
|         if (!response || response.status !== 200 || response.type !== 'basic') { | ||||
|             return response; | ||||
|         } | ||||
|  | ||||
|         const responseToCache = response.clone(); | ||||
|         const cache = await caches.open(pwa_version) | ||||
|         await cache.put(event.request, response.clone()); | ||||
|  | ||||
|         return response; | ||||
|     })()); | ||||
| }); | ||||
|      | ||||
							
								
								
									
										2
									
								
								examples/03_numpy_grid_demo/site.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								examples/03_numpy_grid_demo/site.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
|  | ||||
|      | ||||
		Reference in New Issue
	
	Block a user