new advanced example, small fixes
This commit is contained in:
		
							
								
								
									
										19
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								README.md
									
									
									
									
									
								
							| @ -1,3 +1,20 @@ | |||||||
| # pyscript-bootstrap-templates | # pyscript-bootstrap-templates | ||||||
|  |  | ||||||
| templates and pyscript wrappers for various bootstrap properties | templates and pyscript wrappers for various bootstrap properties | ||||||
|  |  | ||||||
|  | ## installation | ||||||
|  |  | ||||||
|  | ```bash | ||||||
|  | pip install git+https://the-cake-is-a-lie.net/gogs/jonas/pyscript-bootstrap-templates.git | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## usage | ||||||
|  |  | ||||||
|  | to create a new project, run | ||||||
|  |  | ||||||
|  | ```bash | ||||||
|  | pyscript_bootstrap_app create <project_name> <title> | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | this will create a new folder containing a PWA app skeleton to start with. | ||||||
|  | The main entry point should be in main.py | ||||||
| @ -8,7 +8,7 @@ | |||||||
|     "pyscript_py_url": "https://pyscript.net/latest/pyscript.py", |     "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_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", |     "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", |     "pyscript_bootstrap_templates_wheel_url": "../dist/pyscript_bootstrap_templates-0.2.0-py3-none-any.whl", | ||||||
|     "pwa_bg_color": "#000000", |     "pwa_bg_color": "#000000", | ||||||
|     "pwa_theme_color": "#ffffff" |     "pwa_theme_color": "#ffffff" | ||||||
| } | } | ||||||
| @ -19,9 +19,11 @@ | |||||||
|     <body style="width: 100%; height: 100%"> |     <body style="width: 100%; height: 100%"> | ||||||
|         <py-config type="json"> |         <py-config type="json"> | ||||||
|             { |             { | ||||||
|     "autoclose_loader": true, |     "splashscreen": { | ||||||
|  |         "autoclose": true | ||||||
|  |     }, | ||||||
|     "packages": [ |     "packages": [ | ||||||
|         "./resources/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl" |         "./resources/pyscript_bootstrap_templates-0.2.0-py3-none-any.whl" | ||||||
|     ], |     ], | ||||||
|     "paths": [] |     "paths": [] | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,9 +1,35 @@ | |||||||
| :not(:defined) { | /* py-config - not a component */ | ||||||
|     display: none | py-config { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  | /* py-{el} - components not defined */ | ||||||
|  | py-script:not(:defined) { | ||||||
|  |     display: none; | ||||||
| } | } | ||||||
|  |  | ||||||
| html{ | py-repl:not(:defined) { | ||||||
|     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"; |     display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | py-title:not(:defined) { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | py-inputbox:not(:defined) { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | py-button:not(:defined) { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | py-box: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; |     line-height: 1.5; | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -24,6 +50,7 @@ html{ | |||||||
|     border-right: 4px solid rgba(255, 255, 255, 0); |     border-right: 4px solid rgba(255, 255, 255, 0); | ||||||
|     animation: spinner 0.6s linear infinite; |     animation: spinner 0.6s linear infinite; | ||||||
| } | } | ||||||
|  |  | ||||||
| @keyframes spinner { | @keyframes spinner { | ||||||
|     to { |     to { | ||||||
|         transform: rotate(360deg); |         transform: rotate(360deg); | ||||||
| @ -81,27 +108,68 @@ html{ | |||||||
|     right: 5%; |     right: 5%; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-box{ | /* Pop-up second layer end */ | ||||||
|  | .alert-banner { | ||||||
|  |     position: relative; | ||||||
|  |     padding: 0.5rem 1.5rem 0.5rem 0.5rem; | ||||||
|  |     margin: 5px 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .alert-banner p { | ||||||
|  |     margin: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .py-error { | ||||||
|  |     background-color: #ffe9e8; | ||||||
|  |     border: solid; | ||||||
|  |     border-color: #f0625f; | ||||||
|  |     color: #9d041c; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .py-warning { | ||||||
|  |     background-color: rgb(255, 244, 229); | ||||||
|  |     border: solid; | ||||||
|  |     border-color: #ffa016; | ||||||
|  |     color: #794700; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .alert-banner.py-error > #alert-close-button { | ||||||
|  |     color: #9d041c; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .alert-banner.py-warning > #alert-close-button { | ||||||
|  |     color: #794700; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #alert-close-button { | ||||||
|  |     position: absolute; | ||||||
|  |     right: 0.5rem; | ||||||
|  |     top: 0.5rem; | ||||||
|  |     cursor: pointer; | ||||||
|  |     background: transparent; | ||||||
|  |     border: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .py-box { | ||||||
|     display: flex; |     display: flex; | ||||||
|     flex-direction: row; |     flex-direction: row; | ||||||
|     justify-content: flex-start; |     justify-content: flex-start; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-box div.py-box-child * | .py-box div.py-box-child * { | ||||||
| { |  | ||||||
|     max-width: 100%; |     max-width: 100%; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-repl-box{ | .py-repl-box { | ||||||
|     flex-direction: column; |     flex-direction: column; | ||||||
| } | } | ||||||
|  |  | ||||||
| .editor-box{ | .py-repl-editor { | ||||||
|     --tw-border-opacity: 1; |     --tw-border-opacity: 1; | ||||||
|     border-color: rgba(209, 213, 219, var(--tw-border-opacity)); |     border-color: rgba(209, 213, 219, var(--tw-border-opacity)); | ||||||
|     border-width: 1px; |     border-width: 1px; | ||||||
|     position: relative; |     position: relative; | ||||||
|     --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); |     --tw-ring-inset: var(--tw-empty, /*!*/ /*!*/); | ||||||
|     --tw-ring-offset-width: 0px; |     --tw-ring-offset-width: 0px; | ||||||
|     --tw-ring-offset-color: #fff; |     --tw-ring-offset-color: #fff; | ||||||
|     --tw-ring-color: rgba(59, 130, 246, 0.5); |     --tw-ring-color: rgba(59, 130, 246, 0.5); | ||||||
| @ -113,14 +181,14 @@ html{ | |||||||
|     box-sizing: border-box; |     box-sizing: border-box; | ||||||
|     border-width: 1px; |     border-width: 1px; | ||||||
|     border-style: solid; |     border-style: solid; | ||||||
|     border-color: rgb(209, 213, 219) |     border-color: rgb(209, 213, 219); | ||||||
| } | } | ||||||
|  |  | ||||||
| .editor-box:hover button{ | .editor-box:hover button { | ||||||
|     opacity: 1; |     opacity: 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| .repl-play-button{ | .py-repl-run-button { | ||||||
|     opacity: 0; |     opacity: 0; | ||||||
|     bottom: 0.25rem; |     bottom: 0.25rem; | ||||||
|     right: 0.25rem; |     right: 0.25rem; | ||||||
| @ -154,65 +222,64 @@ html{ | |||||||
|     border: 0; |     border: 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| .repl-play-button:hover{ | .py-repl-run-button:hover { | ||||||
|     opacity: 1; |     opacity: 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-title{ | .py-title { | ||||||
|     text-transform: uppercase; |     text-transform: uppercase; | ||||||
|     text-align: center; |     text-align: center; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-title h1{ | .py-title h1 { | ||||||
|     font-weight: 700; |     font-weight: 700; | ||||||
|     font-size: 1.875rem; |     font-size: 1.875rem; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-input{ | .py-input { | ||||||
|     padding: 0.5rem; |     padding: 0.5rem; | ||||||
|     --tw-border-opacity: 1; |     --tw-border-opacity: 1; | ||||||
|     border-color: rgba(209, 213, 219, var(--tw-border-opacity)); |     border-color: rgba(209, 213, 219, var(--tw-border-opacity)); | ||||||
|     border-width: 1px; |     border-width: 1px; | ||||||
|    border-radius: 0.25rem; |     border-radius: 0.25rem; | ||||||
|     margin-right: 0.75rem; |     margin-right: 0.75rem; | ||||||
|     border-style: solid; |     border-style: solid; | ||||||
|     width: auto; |     width: auto; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-box input.py-input{ | .py-box input.py-input { | ||||||
|     width: -webkit-fill-available; |     width: -webkit-fill-available; | ||||||
| } | } | ||||||
|  |  | ||||||
| .central-content{ | .central-content { | ||||||
|     max-width: 20rem; |     max-width: 20rem; | ||||||
|     margin-left: auto; |     margin-left: auto; | ||||||
|     margin-right: auto; |     margin-right: auto; | ||||||
| } | } | ||||||
|  |  | ||||||
| input { | input { | ||||||
|   text-rendering: auto; |     text-rendering: auto; | ||||||
|   color: -internal-light-dark(black, white); |     color: -internal-light-dark(black, white); | ||||||
|   letter-spacing: normal; |     letter-spacing: normal; | ||||||
|   word-spacing: normal; |     word-spacing: normal; | ||||||
|   line-height: normal; |     line-height: normal; | ||||||
|   text-transform: none; |     text-transform: none; | ||||||
|   text-indent: 0px; |     text-indent: 0px; | ||||||
|   text-shadow: none; |     text-shadow: none; | ||||||
|   display: inline-block; |     display: inline-block; | ||||||
|   text-align: start; |     text-align: start; | ||||||
|   appearance: auto; |     appearance: auto; | ||||||
|   -webkit-rtl-ordering: logical; |     -webkit-rtl-ordering: logical; | ||||||
|   cursor: text; |     background-color: -internal-light-dark(rgb(255, 255, 255), rgb(59, 59, 59)); | ||||||
|   background-color: -internal-light-dark(rgb(255, 255, 255), rgb(59, 59, 59)); |     margin: 0em; | ||||||
|   margin: 0em; |     padding: 1px 2px; | ||||||
|   padding: 1px 2px; |     border-width: 2px; | ||||||
|   border-width: 2px; |     border-style: inset; | ||||||
|   border-style: inset; |     border-color: -internal-light-dark(rgb(118, 118, 118), rgb(133, 133, 133)); | ||||||
|   border-color: -internal-light-dark(rgb(118, 118, 118), rgb(133, 133, 133)); |     border-image: initial; | ||||||
|   border-image: initial; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-button{ | .py-button { | ||||||
|     --tw-text-opacity: 1; |     --tw-text-opacity: 1; | ||||||
|     color: rgba(255, 255, 255, var(--tw-text-opacity)); |     color: rgba(255, 255, 255, var(--tw-text-opacity)); | ||||||
|     padding: 0.5rem; |     padding: 0.5rem; | ||||||
| @ -222,17 +289,22 @@ input { | |||||||
|     border-color: rgba(37, 99, 235, var(--tw-border-opacity)); |     border-color: rgba(37, 99, 235, var(--tw-border-opacity)); | ||||||
|     border-width: 1px; |     border-width: 1px; | ||||||
|     border-radius: 0.25rem; |     border-radius: 0.25rem; | ||||||
|  |     cursor: pointer; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-li-element p{ | .py-li-element p { | ||||||
|     margin: 5px; |     margin: 5px; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-li-element p{ | .py-li-element p { | ||||||
|     display: inline; |     display: inline; | ||||||
| } | } | ||||||
|  |  | ||||||
| button, input, optgroup, select, textarea { | button, | ||||||
|  | input, | ||||||
|  | optgroup, | ||||||
|  | select, | ||||||
|  | textarea { | ||||||
|     font-family: inherit; |     font-family: inherit; | ||||||
|     font-size: 100%; |     font-size: 100%; | ||||||
|     line-height: 1.15; |     line-height: 1.15; | ||||||
| @ -242,3 +314,19 @@ button, input, optgroup, select, textarea { | |||||||
| .line-through { | .line-through { | ||||||
|     text-decoration: line-through; |     text-decoration: line-through; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* ===== py-terminal plugin ===== */ | ||||||
|  | /* XXX: it would be nice if these rules were stored in e.g. pyterminal.css and | ||||||
|  |    bundled together at build time (by rollup?) */ | ||||||
|  |  | ||||||
|  | .py-terminal { | ||||||
|  |     min-height: 10em; | ||||||
|  |     background-color: black; | ||||||
|  |     color: white; | ||||||
|  |     padding: 0.5rem; | ||||||
|  |     overflow: auto; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .py-terminal-hidden { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  | |||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @ -1,5 +1,5 @@ | |||||||
|  |  | ||||||
| const pwa_version = "01_hello_world_202210161638" | const pwa_version = "01_hello_world_202306031006" | ||||||
| const assets = ["./index.html", | const assets = ["./index.html", | ||||||
|     "./main.py", |     "./main.py", | ||||||
|     "./resources/bootstrap.css", |     "./resources/bootstrap.css", | ||||||
| @ -7,7 +7,7 @@ const assets = ["./index.html", | |||||||
|     "./resources/pyscript.css", |     "./resources/pyscript.css", | ||||||
|     "./resources/pyscript.js", |     "./resources/pyscript.js", | ||||||
|     "./resources/pyscript.py", |     "./resources/pyscript.py", | ||||||
|     "./resources/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl", |     "./resources/pyscript_bootstrap_templates-0.2.0-py3-none-any.whl", | ||||||
|     "./resources/pwa.js", |     "./resources/pwa.js", | ||||||
|     "./site.js", |     "./site.js", | ||||||
| ] | ] | ||||||
|  | |||||||
| @ -11,7 +11,7 @@ | |||||||
|     "pyscript_py_url": "https://pyscript.net/latest/pyscript.py", |     "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_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", |     "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", |     "pyscript_bootstrap_templates_wheel_url": "../dist/pyscript_bootstrap_templates-0.2.0-py3-none-any.whl", | ||||||
|     "pwa_bg_color": "#000000", |     "pwa_bg_color": "#000000", | ||||||
|     "pwa_theme_color": "#ffffff" |     "pwa_theme_color": "#ffffff" | ||||||
| } | } | ||||||
| @ -19,9 +19,11 @@ | |||||||
|     <body style="width: 100%; height: 100%"> |     <body style="width: 100%; height: 100%"> | ||||||
|         <py-config type="json"> |         <py-config type="json"> | ||||||
|             { |             { | ||||||
|     "autoclose_loader": true, |     "splashscreen": { | ||||||
|  |         "autoclose": true | ||||||
|  |     }, | ||||||
|     "packages": [ |     "packages": [ | ||||||
|         "./resources/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl", |         "./resources/pyscript_bootstrap_templates-0.2.0-py3-none-any.whl", | ||||||
|         "numpy", |         "numpy", | ||||||
|         "scikit-image" |         "scikit-image" | ||||||
|     ], |     ], | ||||||
|  | |||||||
| @ -1,9 +1,35 @@ | |||||||
| :not(:defined) { | /* py-config - not a component */ | ||||||
|     display: none | py-config { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  | /* py-{el} - components not defined */ | ||||||
|  | py-script:not(:defined) { | ||||||
|  |     display: none; | ||||||
| } | } | ||||||
|  |  | ||||||
| html{ | py-repl:not(:defined) { | ||||||
|     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"; |     display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | py-title:not(:defined) { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | py-inputbox:not(:defined) { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | py-button:not(:defined) { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | py-box: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; |     line-height: 1.5; | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -24,6 +50,7 @@ html{ | |||||||
|     border-right: 4px solid rgba(255, 255, 255, 0); |     border-right: 4px solid rgba(255, 255, 255, 0); | ||||||
|     animation: spinner 0.6s linear infinite; |     animation: spinner 0.6s linear infinite; | ||||||
| } | } | ||||||
|  |  | ||||||
| @keyframes spinner { | @keyframes spinner { | ||||||
|     to { |     to { | ||||||
|         transform: rotate(360deg); |         transform: rotate(360deg); | ||||||
| @ -81,27 +108,68 @@ html{ | |||||||
|     right: 5%; |     right: 5%; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-box{ | /* Pop-up second layer end */ | ||||||
|  | .alert-banner { | ||||||
|  |     position: relative; | ||||||
|  |     padding: 0.5rem 1.5rem 0.5rem 0.5rem; | ||||||
|  |     margin: 5px 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .alert-banner p { | ||||||
|  |     margin: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .py-error { | ||||||
|  |     background-color: #ffe9e8; | ||||||
|  |     border: solid; | ||||||
|  |     border-color: #f0625f; | ||||||
|  |     color: #9d041c; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .py-warning { | ||||||
|  |     background-color: rgb(255, 244, 229); | ||||||
|  |     border: solid; | ||||||
|  |     border-color: #ffa016; | ||||||
|  |     color: #794700; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .alert-banner.py-error > #alert-close-button { | ||||||
|  |     color: #9d041c; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .alert-banner.py-warning > #alert-close-button { | ||||||
|  |     color: #794700; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #alert-close-button { | ||||||
|  |     position: absolute; | ||||||
|  |     right: 0.5rem; | ||||||
|  |     top: 0.5rem; | ||||||
|  |     cursor: pointer; | ||||||
|  |     background: transparent; | ||||||
|  |     border: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .py-box { | ||||||
|     display: flex; |     display: flex; | ||||||
|     flex-direction: row; |     flex-direction: row; | ||||||
|     justify-content: flex-start; |     justify-content: flex-start; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-box div.py-box-child * | .py-box div.py-box-child * { | ||||||
| { |  | ||||||
|     max-width: 100%; |     max-width: 100%; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-repl-box{ | .py-repl-box { | ||||||
|     flex-direction: column; |     flex-direction: column; | ||||||
| } | } | ||||||
|  |  | ||||||
| .editor-box{ | .py-repl-editor { | ||||||
|     --tw-border-opacity: 1; |     --tw-border-opacity: 1; | ||||||
|     border-color: rgba(209, 213, 219, var(--tw-border-opacity)); |     border-color: rgba(209, 213, 219, var(--tw-border-opacity)); | ||||||
|     border-width: 1px; |     border-width: 1px; | ||||||
|     position: relative; |     position: relative; | ||||||
|     --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); |     --tw-ring-inset: var(--tw-empty, /*!*/ /*!*/); | ||||||
|     --tw-ring-offset-width: 0px; |     --tw-ring-offset-width: 0px; | ||||||
|     --tw-ring-offset-color: #fff; |     --tw-ring-offset-color: #fff; | ||||||
|     --tw-ring-color: rgba(59, 130, 246, 0.5); |     --tw-ring-color: rgba(59, 130, 246, 0.5); | ||||||
| @ -113,14 +181,14 @@ html{ | |||||||
|     box-sizing: border-box; |     box-sizing: border-box; | ||||||
|     border-width: 1px; |     border-width: 1px; | ||||||
|     border-style: solid; |     border-style: solid; | ||||||
|     border-color: rgb(209, 213, 219) |     border-color: rgb(209, 213, 219); | ||||||
| } | } | ||||||
|  |  | ||||||
| .editor-box:hover button{ | .editor-box:hover button { | ||||||
|     opacity: 1; |     opacity: 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| .repl-play-button{ | .py-repl-run-button { | ||||||
|     opacity: 0; |     opacity: 0; | ||||||
|     bottom: 0.25rem; |     bottom: 0.25rem; | ||||||
|     right: 0.25rem; |     right: 0.25rem; | ||||||
| @ -154,65 +222,64 @@ html{ | |||||||
|     border: 0; |     border: 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| .repl-play-button:hover{ | .py-repl-run-button:hover { | ||||||
|     opacity: 1; |     opacity: 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-title{ | .py-title { | ||||||
|     text-transform: uppercase; |     text-transform: uppercase; | ||||||
|     text-align: center; |     text-align: center; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-title h1{ | .py-title h1 { | ||||||
|     font-weight: 700; |     font-weight: 700; | ||||||
|     font-size: 1.875rem; |     font-size: 1.875rem; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-input{ | .py-input { | ||||||
|     padding: 0.5rem; |     padding: 0.5rem; | ||||||
|     --tw-border-opacity: 1; |     --tw-border-opacity: 1; | ||||||
|     border-color: rgba(209, 213, 219, var(--tw-border-opacity)); |     border-color: rgba(209, 213, 219, var(--tw-border-opacity)); | ||||||
|     border-width: 1px; |     border-width: 1px; | ||||||
|    border-radius: 0.25rem; |     border-radius: 0.25rem; | ||||||
|     margin-right: 0.75rem; |     margin-right: 0.75rem; | ||||||
|     border-style: solid; |     border-style: solid; | ||||||
|     width: auto; |     width: auto; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-box input.py-input{ | .py-box input.py-input { | ||||||
|     width: -webkit-fill-available; |     width: -webkit-fill-available; | ||||||
| } | } | ||||||
|  |  | ||||||
| .central-content{ | .central-content { | ||||||
|     max-width: 20rem; |     max-width: 20rem; | ||||||
|     margin-left: auto; |     margin-left: auto; | ||||||
|     margin-right: auto; |     margin-right: auto; | ||||||
| } | } | ||||||
|  |  | ||||||
| input { | input { | ||||||
|   text-rendering: auto; |     text-rendering: auto; | ||||||
|   color: -internal-light-dark(black, white); |     color: -internal-light-dark(black, white); | ||||||
|   letter-spacing: normal; |     letter-spacing: normal; | ||||||
|   word-spacing: normal; |     word-spacing: normal; | ||||||
|   line-height: normal; |     line-height: normal; | ||||||
|   text-transform: none; |     text-transform: none; | ||||||
|   text-indent: 0px; |     text-indent: 0px; | ||||||
|   text-shadow: none; |     text-shadow: none; | ||||||
|   display: inline-block; |     display: inline-block; | ||||||
|   text-align: start; |     text-align: start; | ||||||
|   appearance: auto; |     appearance: auto; | ||||||
|   -webkit-rtl-ordering: logical; |     -webkit-rtl-ordering: logical; | ||||||
|   cursor: text; |     background-color: -internal-light-dark(rgb(255, 255, 255), rgb(59, 59, 59)); | ||||||
|   background-color: -internal-light-dark(rgb(255, 255, 255), rgb(59, 59, 59)); |     margin: 0em; | ||||||
|   margin: 0em; |     padding: 1px 2px; | ||||||
|   padding: 1px 2px; |     border-width: 2px; | ||||||
|   border-width: 2px; |     border-style: inset; | ||||||
|   border-style: inset; |     border-color: -internal-light-dark(rgb(118, 118, 118), rgb(133, 133, 133)); | ||||||
|   border-color: -internal-light-dark(rgb(118, 118, 118), rgb(133, 133, 133)); |     border-image: initial; | ||||||
|   border-image: initial; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-button{ | .py-button { | ||||||
|     --tw-text-opacity: 1; |     --tw-text-opacity: 1; | ||||||
|     color: rgba(255, 255, 255, var(--tw-text-opacity)); |     color: rgba(255, 255, 255, var(--tw-text-opacity)); | ||||||
|     padding: 0.5rem; |     padding: 0.5rem; | ||||||
| @ -222,17 +289,22 @@ input { | |||||||
|     border-color: rgba(37, 99, 235, var(--tw-border-opacity)); |     border-color: rgba(37, 99, 235, var(--tw-border-opacity)); | ||||||
|     border-width: 1px; |     border-width: 1px; | ||||||
|     border-radius: 0.25rem; |     border-radius: 0.25rem; | ||||||
|  |     cursor: pointer; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-li-element p{ | .py-li-element p { | ||||||
|     margin: 5px; |     margin: 5px; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-li-element p{ | .py-li-element p { | ||||||
|     display: inline; |     display: inline; | ||||||
| } | } | ||||||
|  |  | ||||||
| button, input, optgroup, select, textarea { | button, | ||||||
|  | input, | ||||||
|  | optgroup, | ||||||
|  | select, | ||||||
|  | textarea { | ||||||
|     font-family: inherit; |     font-family: inherit; | ||||||
|     font-size: 100%; |     font-size: 100%; | ||||||
|     line-height: 1.15; |     line-height: 1.15; | ||||||
| @ -242,3 +314,19 @@ button, input, optgroup, select, textarea { | |||||||
| .line-through { | .line-through { | ||||||
|     text-decoration: line-through; |     text-decoration: line-through; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* ===== py-terminal plugin ===== */ | ||||||
|  | /* XXX: it would be nice if these rules were stored in e.g. pyterminal.css and | ||||||
|  |    bundled together at build time (by rollup?) */ | ||||||
|  |  | ||||||
|  | .py-terminal { | ||||||
|  |     min-height: 10em; | ||||||
|  |     background-color: black; | ||||||
|  |     color: white; | ||||||
|  |     padding: 0.5rem; | ||||||
|  |     overflow: auto; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .py-terminal-hidden { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  | |||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @ -1,5 +1,5 @@ | |||||||
|  |  | ||||||
| const pwa_version = "02_image_filter_202210161638" | const pwa_version = "02_image_filter_202306031006" | ||||||
| const assets = ["./index.html", | const assets = ["./index.html", | ||||||
|     "./main.py", |     "./main.py", | ||||||
|     "./resources/bootstrap.css", |     "./resources/bootstrap.css", | ||||||
| @ -7,7 +7,7 @@ const assets = ["./index.html", | |||||||
|     "./resources/pyscript.css", |     "./resources/pyscript.css", | ||||||
|     "./resources/pyscript.js", |     "./resources/pyscript.js", | ||||||
|     "./resources/pyscript.py", |     "./resources/pyscript.py", | ||||||
|     "./resources/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl", |     "./resources/pyscript_bootstrap_templates-0.2.0-py3-none-any.whl", | ||||||
|     "./resources/pwa.js", |     "./resources/pwa.js", | ||||||
|     "./site.js", |     "./site.js", | ||||||
| ] | ] | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ | |||||||
|     "pyscript_py_url": "https://pyscript.net/latest/pyscript.py", |     "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_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", |     "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", |     "pyscript_bootstrap_templates_wheel_url": "../dist/pyscript_bootstrap_templates-0.2.0-py3-none-any.whl", | ||||||
|     "pwa_bg_color": "#000000", |     "pwa_bg_color": "#000000", | ||||||
|     "pwa_theme_color": "#ffffff" |     "pwa_theme_color": "#ffffff" | ||||||
| } | } | ||||||
| @ -19,9 +19,11 @@ | |||||||
|     <body style="width: 100%; height: 100%"> |     <body style="width: 100%; height: 100%"> | ||||||
|         <py-config type="json"> |         <py-config type="json"> | ||||||
|             { |             { | ||||||
|     "autoclose_loader": true, |     "splashscreen": { | ||||||
|  |         "autoclose": true | ||||||
|  |     }, | ||||||
|     "packages": [ |     "packages": [ | ||||||
|         "./resources/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl", |         "./resources/pyscript_bootstrap_templates-0.2.0-py3-none-any.whl", | ||||||
|         "numpy" |         "numpy" | ||||||
|     ], |     ], | ||||||
|     "paths": [] |     "paths": [] | ||||||
|  | |||||||
| @ -1,9 +1,35 @@ | |||||||
| :not(:defined) { | /* py-config - not a component */ | ||||||
|     display: none | py-config { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  | /* py-{el} - components not defined */ | ||||||
|  | py-script:not(:defined) { | ||||||
|  |     display: none; | ||||||
| } | } | ||||||
|  |  | ||||||
| html{ | py-repl:not(:defined) { | ||||||
|     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"; |     display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | py-title:not(:defined) { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | py-inputbox:not(:defined) { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | py-button:not(:defined) { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | py-box: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; |     line-height: 1.5; | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -24,6 +50,7 @@ html{ | |||||||
|     border-right: 4px solid rgba(255, 255, 255, 0); |     border-right: 4px solid rgba(255, 255, 255, 0); | ||||||
|     animation: spinner 0.6s linear infinite; |     animation: spinner 0.6s linear infinite; | ||||||
| } | } | ||||||
|  |  | ||||||
| @keyframes spinner { | @keyframes spinner { | ||||||
|     to { |     to { | ||||||
|         transform: rotate(360deg); |         transform: rotate(360deg); | ||||||
| @ -81,27 +108,68 @@ html{ | |||||||
|     right: 5%; |     right: 5%; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-box{ | /* Pop-up second layer end */ | ||||||
|  | .alert-banner { | ||||||
|  |     position: relative; | ||||||
|  |     padding: 0.5rem 1.5rem 0.5rem 0.5rem; | ||||||
|  |     margin: 5px 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .alert-banner p { | ||||||
|  |     margin: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .py-error { | ||||||
|  |     background-color: #ffe9e8; | ||||||
|  |     border: solid; | ||||||
|  |     border-color: #f0625f; | ||||||
|  |     color: #9d041c; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .py-warning { | ||||||
|  |     background-color: rgb(255, 244, 229); | ||||||
|  |     border: solid; | ||||||
|  |     border-color: #ffa016; | ||||||
|  |     color: #794700; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .alert-banner.py-error > #alert-close-button { | ||||||
|  |     color: #9d041c; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .alert-banner.py-warning > #alert-close-button { | ||||||
|  |     color: #794700; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #alert-close-button { | ||||||
|  |     position: absolute; | ||||||
|  |     right: 0.5rem; | ||||||
|  |     top: 0.5rem; | ||||||
|  |     cursor: pointer; | ||||||
|  |     background: transparent; | ||||||
|  |     border: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .py-box { | ||||||
|     display: flex; |     display: flex; | ||||||
|     flex-direction: row; |     flex-direction: row; | ||||||
|     justify-content: flex-start; |     justify-content: flex-start; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-box div.py-box-child * | .py-box div.py-box-child * { | ||||||
| { |  | ||||||
|     max-width: 100%; |     max-width: 100%; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-repl-box{ | .py-repl-box { | ||||||
|     flex-direction: column; |     flex-direction: column; | ||||||
| } | } | ||||||
|  |  | ||||||
| .editor-box{ | .py-repl-editor { | ||||||
|     --tw-border-opacity: 1; |     --tw-border-opacity: 1; | ||||||
|     border-color: rgba(209, 213, 219, var(--tw-border-opacity)); |     border-color: rgba(209, 213, 219, var(--tw-border-opacity)); | ||||||
|     border-width: 1px; |     border-width: 1px; | ||||||
|     position: relative; |     position: relative; | ||||||
|     --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); |     --tw-ring-inset: var(--tw-empty, /*!*/ /*!*/); | ||||||
|     --tw-ring-offset-width: 0px; |     --tw-ring-offset-width: 0px; | ||||||
|     --tw-ring-offset-color: #fff; |     --tw-ring-offset-color: #fff; | ||||||
|     --tw-ring-color: rgba(59, 130, 246, 0.5); |     --tw-ring-color: rgba(59, 130, 246, 0.5); | ||||||
| @ -113,14 +181,14 @@ html{ | |||||||
|     box-sizing: border-box; |     box-sizing: border-box; | ||||||
|     border-width: 1px; |     border-width: 1px; | ||||||
|     border-style: solid; |     border-style: solid; | ||||||
|     border-color: rgb(209, 213, 219) |     border-color: rgb(209, 213, 219); | ||||||
| } | } | ||||||
|  |  | ||||||
| .editor-box:hover button{ | .editor-box:hover button { | ||||||
|     opacity: 1; |     opacity: 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| .repl-play-button{ | .py-repl-run-button { | ||||||
|     opacity: 0; |     opacity: 0; | ||||||
|     bottom: 0.25rem; |     bottom: 0.25rem; | ||||||
|     right: 0.25rem; |     right: 0.25rem; | ||||||
| @ -154,65 +222,64 @@ html{ | |||||||
|     border: 0; |     border: 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| .repl-play-button:hover{ | .py-repl-run-button:hover { | ||||||
|     opacity: 1; |     opacity: 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-title{ | .py-title { | ||||||
|     text-transform: uppercase; |     text-transform: uppercase; | ||||||
|     text-align: center; |     text-align: center; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-title h1{ | .py-title h1 { | ||||||
|     font-weight: 700; |     font-weight: 700; | ||||||
|     font-size: 1.875rem; |     font-size: 1.875rem; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-input{ | .py-input { | ||||||
|     padding: 0.5rem; |     padding: 0.5rem; | ||||||
|     --tw-border-opacity: 1; |     --tw-border-opacity: 1; | ||||||
|     border-color: rgba(209, 213, 219, var(--tw-border-opacity)); |     border-color: rgba(209, 213, 219, var(--tw-border-opacity)); | ||||||
|     border-width: 1px; |     border-width: 1px; | ||||||
|    border-radius: 0.25rem; |     border-radius: 0.25rem; | ||||||
|     margin-right: 0.75rem; |     margin-right: 0.75rem; | ||||||
|     border-style: solid; |     border-style: solid; | ||||||
|     width: auto; |     width: auto; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-box input.py-input{ | .py-box input.py-input { | ||||||
|     width: -webkit-fill-available; |     width: -webkit-fill-available; | ||||||
| } | } | ||||||
|  |  | ||||||
| .central-content{ | .central-content { | ||||||
|     max-width: 20rem; |     max-width: 20rem; | ||||||
|     margin-left: auto; |     margin-left: auto; | ||||||
|     margin-right: auto; |     margin-right: auto; | ||||||
| } | } | ||||||
|  |  | ||||||
| input { | input { | ||||||
|   text-rendering: auto; |     text-rendering: auto; | ||||||
|   color: -internal-light-dark(black, white); |     color: -internal-light-dark(black, white); | ||||||
|   letter-spacing: normal; |     letter-spacing: normal; | ||||||
|   word-spacing: normal; |     word-spacing: normal; | ||||||
|   line-height: normal; |     line-height: normal; | ||||||
|   text-transform: none; |     text-transform: none; | ||||||
|   text-indent: 0px; |     text-indent: 0px; | ||||||
|   text-shadow: none; |     text-shadow: none; | ||||||
|   display: inline-block; |     display: inline-block; | ||||||
|   text-align: start; |     text-align: start; | ||||||
|   appearance: auto; |     appearance: auto; | ||||||
|   -webkit-rtl-ordering: logical; |     -webkit-rtl-ordering: logical; | ||||||
|   cursor: text; |     background-color: -internal-light-dark(rgb(255, 255, 255), rgb(59, 59, 59)); | ||||||
|   background-color: -internal-light-dark(rgb(255, 255, 255), rgb(59, 59, 59)); |     margin: 0em; | ||||||
|   margin: 0em; |     padding: 1px 2px; | ||||||
|   padding: 1px 2px; |     border-width: 2px; | ||||||
|   border-width: 2px; |     border-style: inset; | ||||||
|   border-style: inset; |     border-color: -internal-light-dark(rgb(118, 118, 118), rgb(133, 133, 133)); | ||||||
|   border-color: -internal-light-dark(rgb(118, 118, 118), rgb(133, 133, 133)); |     border-image: initial; | ||||||
|   border-image: initial; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-button{ | .py-button { | ||||||
|     --tw-text-opacity: 1; |     --tw-text-opacity: 1; | ||||||
|     color: rgba(255, 255, 255, var(--tw-text-opacity)); |     color: rgba(255, 255, 255, var(--tw-text-opacity)); | ||||||
|     padding: 0.5rem; |     padding: 0.5rem; | ||||||
| @ -222,17 +289,22 @@ input { | |||||||
|     border-color: rgba(37, 99, 235, var(--tw-border-opacity)); |     border-color: rgba(37, 99, 235, var(--tw-border-opacity)); | ||||||
|     border-width: 1px; |     border-width: 1px; | ||||||
|     border-radius: 0.25rem; |     border-radius: 0.25rem; | ||||||
|  |     cursor: pointer; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-li-element p{ | .py-li-element p { | ||||||
|     margin: 5px; |     margin: 5px; | ||||||
| } | } | ||||||
|  |  | ||||||
| .py-li-element p{ | .py-li-element p { | ||||||
|     display: inline; |     display: inline; | ||||||
| } | } | ||||||
|  |  | ||||||
| button, input, optgroup, select, textarea { | button, | ||||||
|  | input, | ||||||
|  | optgroup, | ||||||
|  | select, | ||||||
|  | textarea { | ||||||
|     font-family: inherit; |     font-family: inherit; | ||||||
|     font-size: 100%; |     font-size: 100%; | ||||||
|     line-height: 1.15; |     line-height: 1.15; | ||||||
| @ -242,3 +314,19 @@ button, input, optgroup, select, textarea { | |||||||
| .line-through { | .line-through { | ||||||
|     text-decoration: line-through; |     text-decoration: line-through; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* ===== py-terminal plugin ===== */ | ||||||
|  | /* XXX: it would be nice if these rules were stored in e.g. pyterminal.css and | ||||||
|  |    bundled together at build time (by rollup?) */ | ||||||
|  |  | ||||||
|  | .py-terminal { | ||||||
|  |     min-height: 10em; | ||||||
|  |     background-color: black; | ||||||
|  |     color: white; | ||||||
|  |     padding: 0.5rem; | ||||||
|  |     overflow: auto; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .py-terminal-hidden { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  | |||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @ -1,5 +1,5 @@ | |||||||
|  |  | ||||||
| const pwa_version = "03_numpy_grid_demo_202210161638" | const pwa_version = "03_numpy_grid_demo_202306031006" | ||||||
| const assets = ["./index.html", | const assets = ["./index.html", | ||||||
|     "./main.py", |     "./main.py", | ||||||
|     "./resources/bootstrap.css", |     "./resources/bootstrap.css", | ||||||
| @ -7,7 +7,7 @@ const assets = ["./index.html", | |||||||
|     "./resources/pyscript.css", |     "./resources/pyscript.css", | ||||||
|     "./resources/pyscript.js", |     "./resources/pyscript.js", | ||||||
|     "./resources/pyscript.py", |     "./resources/pyscript.py", | ||||||
|     "./resources/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl", |     "./resources/pyscript_bootstrap_templates-0.2.0-py3-none-any.whl", | ||||||
|     "./resources/pwa.js", |     "./resources/pwa.js", | ||||||
|     "./site.js", |     "./site.js", | ||||||
| ] | ] | ||||||
|  | |||||||
							
								
								
									
										18
									
								
								examples/04_cell_detector/config.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								examples/04_cell_detector/config.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | { | ||||||
|  |     "root_folder": "04_cell_detector", | ||||||
|  |     "title": "04_cell_detector", | ||||||
|  |     "packages": [ | ||||||
|  |         "numpy", | ||||||
|  |         "Pillow", | ||||||
|  |         "opencv-python" | ||||||
|  |     ], | ||||||
|  |     "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.2.0-py3-none-any.whl", | ||||||
|  |     "pwa_bg_color": "#000000", | ||||||
|  |     "pwa_theme_color": "#ffffff" | ||||||
|  | } | ||||||
							
								
								
									
										40
									
								
								examples/04_cell_detector/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								examples/04_cell_detector/index.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | |||||||
|  |  | ||||||
|  | <!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>04_cell_detector</title> | ||||||
|  |     </head> | ||||||
|  |     <body style="width: 100%; height: 100%"> | ||||||
|  |         <py-config type="json"> | ||||||
|  |             { | ||||||
|  |     "splashscreen": { | ||||||
|  |         "autoclose": true | ||||||
|  |     }, | ||||||
|  |     "packages": [ | ||||||
|  |         "./resources/pyscript_bootstrap_templates-0.2.0-py3-none-any.whl", | ||||||
|  |         "numpy", | ||||||
|  |         "Pillow", | ||||||
|  |         "opencv-python" | ||||||
|  |     ], | ||||||
|  |     "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> | ||||||
|  |      | ||||||
							
								
								
									
										214
									
								
								examples/04_cell_detector/main.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								examples/04_cell_detector/main.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,214 @@ | |||||||
|  |  | ||||||
|  | from pyscript_bootstrap_templates import bootstrap_templates | ||||||
|  | from pyscript_bootstrap_templates import bootstrap_HTML as bHTML | ||||||
|  | from pyscript_bootstrap_templates import bootstrap_inputs as bInputs | ||||||
|  | from pyscript_bootstrap_templates import HTML as HTML | ||||||
|  |  | ||||||
|  |  | ||||||
|  | import numpy as np | ||||||
|  | from PIL import Image | ||||||
|  | import cv2 | ||||||
|  |  | ||||||
|  | loaded_img = None | ||||||
|  |  | ||||||
|  | app = bootstrap_templates.PyScriptBootstrapDashboard(parent_element="pyscript_app", brand_name="Pyscript Cell Detector") | ||||||
|  |  | ||||||
|  | main_div = bHTML.BootstrapContainer(parent=app.main) | ||||||
|  | main_div.w = 100 | ||||||
|  | result_div = bHTML.BootstrapContainer(parent=main_div) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def process_image(image, | ||||||
|  |                   hough_min_dist = 500, | ||||||
|  |                   hough_param1 = 80, | ||||||
|  |                   hough_param2 = 500, | ||||||
|  |                   minRadius = 100, | ||||||
|  |                   maxRadius=500, | ||||||
|  |                   inner_hough_circles=True): | ||||||
|  |      | ||||||
|  |     for child in result_div.children: | ||||||
|  |         child.destroy() | ||||||
|  |     #image = cv2.imread(str(test_img_path), cv2.IMREAD_COLOR) | ||||||
|  |     # Convert the image to grayscale | ||||||
|  |     gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | ||||||
|  |  | ||||||
|  |     # Use median blur to reduce noise | ||||||
|  |     gray = cv2.medianBlur(gray, 5) | ||||||
|  |  | ||||||
|  |     # Apply Hough transform on the image to find circles | ||||||
|  |     circles = cv2.HoughCircles(gray, | ||||||
|  |                                cv2.HOUGH_GRADIENT, | ||||||
|  |                                2, | ||||||
|  |                                minDist=hough_min_dist, | ||||||
|  |                                param1=hough_param1, | ||||||
|  |                                param2=hough_param2, | ||||||
|  |                                minRadius=minRadius, | ||||||
|  |                                maxRadius=maxRadius) | ||||||
|  |  | ||||||
|  |     if circles is None: | ||||||
|  |         bHTML.AlertDanger("No cell areas detected in image", parent=app.main) | ||||||
|  |         return False | ||||||
|  |      | ||||||
|  |     # Convert to integers | ||||||
|  |     circles = np.uint16(np.around(circles)) | ||||||
|  |  | ||||||
|  |     # Loop over all detected circles and add them to the image | ||||||
|  |     #for i in circles[0,:]: | ||||||
|  |     #    # Draw outer circle | ||||||
|  |     #    cv2.circle(image,(i[0],i[1]),i[2],(0,255,0),2) | ||||||
|  |     #    # Draw center of circle | ||||||
|  |     #    cv2.circle(image,(i[0],i[1]),2,(0,0,255),3) | ||||||
|  |  | ||||||
|  |     first_wells = [] | ||||||
|  |     first_wells_gray = [] | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     for i in circles[0,:]: | ||||||
|  |          | ||||||
|  |         center = (i[0], i[1]) | ||||||
|  |         radius = i[2] | ||||||
|  |          | ||||||
|  |  | ||||||
|  |         # Cut out the well | ||||||
|  |         well = image[center[1]-radius:center[1]+radius, center[0]-radius:center[0]+radius] | ||||||
|  |         well_gray = gray[center[1]-radius:center[1]+radius, center[0]-radius:center[0]+radius] | ||||||
|  |          | ||||||
|  |  | ||||||
|  |         first_wells.append(well) | ||||||
|  |         first_wells_gray.append(well_gray) | ||||||
|  |  | ||||||
|  |     if inner_hough_circles: | ||||||
|  |  | ||||||
|  |         second_wells = [] | ||||||
|  |         second_wells_gray = [] | ||||||
|  |  | ||||||
|  |         for well, well_gray in zip(first_wells, first_wells_gray): | ||||||
|  |              | ||||||
|  |             circles = cv2.HoughCircles(well_gray, cv2.HOUGH_GRADIENT, 1, minDist=hough_min_dist, param1=50, param2=50, minRadius=(well.shape[0] // 2) - 50, maxRadius=(well.shape[0] // 2) - 10) | ||||||
|  |             if circles is not None: | ||||||
|  |                 circles = np.uint16(np.around(circles)) | ||||||
|  |                 i = circles[0,:][0] | ||||||
|  |                 center = (i[0], i[1]) | ||||||
|  |                 radius = i[2] - 1 | ||||||
|  |                  | ||||||
|  |                 min_y = max(center[1]-radius, 0) | ||||||
|  |                 max_y = center[1]+radius | ||||||
|  |                  | ||||||
|  |                 min_x = max(center[0]-radius, 0) | ||||||
|  |                 max_x = center[0]+radius | ||||||
|  |                  | ||||||
|  |  | ||||||
|  |                  | ||||||
|  |                 second_wells.append(well[min_y:max_y, min_x:max_x]) | ||||||
|  |                 second_wells_gray.append(well_gray[min_y:max_y, min_x:max_x])     | ||||||
|  |             else: | ||||||
|  |                 second_wells.append(well) | ||||||
|  |                 second_wells_gray.append(well_gray) | ||||||
|  |          | ||||||
|  |     else: | ||||||
|  |         second_wells = first_wells | ||||||
|  |         second_wells_gray = first_wells_gray | ||||||
|  |      | ||||||
|  |  | ||||||
|  |     for well, well_gray in zip(second_wells, second_wells_gray): | ||||||
|  |          | ||||||
|  |         _, binary = cv2.threshold(255 - well_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) | ||||||
|  |      | ||||||
|  |  | ||||||
|  |         # Perform some morphological operations to remove small noise - you can change the kernel size | ||||||
|  |         kernel = np.ones((3,3),np.uint8) | ||||||
|  |         cleaned = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel) | ||||||
|  |          | ||||||
|  |  | ||||||
|  |         radius = well.shape[0] // 2 -1 | ||||||
|  |         # Now clear all pixels outside the circular well. | ||||||
|  |         # We do this by creating a mask for the well and applying it to the image. | ||||||
|  |         well_mask = np.zeros_like(cleaned, dtype=np.uint8) | ||||||
|  |         cv2.circle(well_mask, (radius, radius), radius, 1, thickness=-1) | ||||||
|  |         cleaned = cleaned * well_mask | ||||||
|  |  | ||||||
|  |         circle = well_mask > 0 | ||||||
|  |  | ||||||
|  |         # now create an image overlay to display | ||||||
|  |         ratio = np.sum((cleaned > 0).astype(int)) / np.sum(circle.astype(int)) | ||||||
|  |         div_result = bHTML.BootstrapContainer(f"ratio: {ratio * 100}%", parent=result_div) | ||||||
|  |         div_result.shadow = bHTML.Shadow.LARGE | ||||||
|  |         div_result.rounded = True | ||||||
|  |         div_result.w = 50 | ||||||
|  |  | ||||||
|  |         well[cleaned > 0,0] = 255 | ||||||
|  |         final_img = bHTML.Image.from_numpy_array(well, parent=div_result) | ||||||
|  |         final_img.rounded = True | ||||||
|  |         final_img.rounded_size = 10 | ||||||
|  |         final_img.shadow = bHTML.Shadow.LARGE | ||||||
|  |         final_img.w = 100 | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | div = bHTML.BootstrapContainer("Controls", parent=app.sidebar) | ||||||
|  | div.font_size = 4 | ||||||
|  |  | ||||||
|  | image_input = bInputs.InputFile(label_text="choose image file", parent=app.sidebar) | ||||||
|  |  | ||||||
|  | i_hough_min_dist = bInputs.InputInt("hough min distance [px]", parent=app.sidebar) | ||||||
|  | i_hough_min_dist.value = 500 | ||||||
|  |  | ||||||
|  | i_hough_param1 = bInputs.InputInt("hough param1", parent=app.sidebar) | ||||||
|  | i_hough_param1.value = 80 | ||||||
|  |  | ||||||
|  | i_hough_param2 = bInputs.InputInt("hough param2", parent=app.sidebar) | ||||||
|  | i_hough_param2.value = 500 | ||||||
|  |  | ||||||
|  | i_hough_min_radius = bInputs.InputInt("hough min radius [px]", parent=app.sidebar) | ||||||
|  | i_hough_min_radius.value = 100 | ||||||
|  |  | ||||||
|  | i_hough_max_radius = bInputs.InputInt("hough max radius [px]", parent=app.sidebar) | ||||||
|  | i_hough_max_radius.value = 500 | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | btn = bHTML.ButtonPrimary("Process", parent=app.sidebar) | ||||||
|  | btn.w = 100 | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def on_image_change(f, *args): | ||||||
|  |     global loaded_img | ||||||
|  |     f.seek(0) | ||||||
|  |     img = np.array(Image.open(f)) | ||||||
|  |     for child in result_div.children: | ||||||
|  |         child.destroy() | ||||||
|  |      | ||||||
|  |     output = bHTML.Image.from_numpy_array(img, parent=result_div) | ||||||
|  |     output.rounded = True | ||||||
|  |     output.rounded_size = 10 | ||||||
|  |     output.shadow = bHTML.Shadow.LARGE | ||||||
|  |     output.w=50 | ||||||
|  |      | ||||||
|  |     loaded_img = img | ||||||
|  |  | ||||||
|  | image_input.onchange = on_image_change     | ||||||
|  |  | ||||||
|  | def on_click(*args, **kwargs): | ||||||
|  |     if loaded_img is None: | ||||||
|  |         bHTML.AlertDanger("No image loaded", parent=app.main) | ||||||
|  |         return | ||||||
|  |      | ||||||
|  |     h_min_dist = int(i_hough_min_dist.value) | ||||||
|  |     h_param1 = int(i_hough_param1.value) | ||||||
|  |     h_param2 = int(i_hough_param2.value) | ||||||
|  |     h_min_radius = int(i_hough_min_radius.value) | ||||||
|  |     h_max_radius = int(i_hough_max_radius.value) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     process_image(loaded_img, | ||||||
|  |                   hough_min_dist=h_min_dist, | ||||||
|  |                   hough_param1=h_param1, | ||||||
|  |                   hough_param2=h_param2, | ||||||
|  |                   minRadius=h_min_radius, | ||||||
|  |                   maxRadius=h_max_radius, | ||||||
|  |                   inner_hough_circles=True) | ||||||
|  |  | ||||||
|  | btn.onclick = on_click | ||||||
|  |  | ||||||
|  |      | ||||||
							
								
								
									
										1
									
								
								examples/04_cell_detector/manifest.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								examples/04_cell_detector/manifest.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | {"name": "04_cell_detector", "short_name": "04_cell_detector", "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/04_cell_detector/resources/bootstrap.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								examples/04_cell_detector/resources/bootstrap.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										7
									
								
								examples/04_cell_detector/resources/bootstrap.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								examples/04_cell_detector/resources/bootstrap.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								examples/04_cell_detector/resources/icon-512x512.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								examples/04_cell_detector/resources/icon-512x512.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 18 KiB | 
							
								
								
									
										10
									
								
								examples/04_cell_detector/resources/pwa.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								examples/04_cell_detector/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)) | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  |      | ||||||
							
								
								
									
										332
									
								
								examples/04_cell_detector/resources/pyscript.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										332
									
								
								examples/04_cell_detector/resources/pyscript.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,332 @@ | |||||||
|  | /* py-config - not a component */ | ||||||
|  | py-config { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  | /* py-{el} - components not defined */ | ||||||
|  | py-script:not(:defined) { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | py-repl:not(:defined) { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | py-title:not(:defined) { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | py-inputbox:not(:defined) { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | py-button:not(:defined) { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | py-box: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%; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Pop-up second layer end */ | ||||||
|  | .alert-banner { | ||||||
|  |     position: relative; | ||||||
|  |     padding: 0.5rem 1.5rem 0.5rem 0.5rem; | ||||||
|  |     margin: 5px 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .alert-banner p { | ||||||
|  |     margin: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .py-error { | ||||||
|  |     background-color: #ffe9e8; | ||||||
|  |     border: solid; | ||||||
|  |     border-color: #f0625f; | ||||||
|  |     color: #9d041c; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .py-warning { | ||||||
|  |     background-color: rgb(255, 244, 229); | ||||||
|  |     border: solid; | ||||||
|  |     border-color: #ffa016; | ||||||
|  |     color: #794700; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .alert-banner.py-error > #alert-close-button { | ||||||
|  |     color: #9d041c; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .alert-banner.py-warning > #alert-close-button { | ||||||
|  |     color: #794700; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #alert-close-button { | ||||||
|  |     position: absolute; | ||||||
|  |     right: 0.5rem; | ||||||
|  |     top: 0.5rem; | ||||||
|  |     cursor: pointer; | ||||||
|  |     background: transparent; | ||||||
|  |     border: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .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; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .py-repl-editor { | ||||||
|  |     --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; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .py-repl-run-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; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .py-repl-run-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; | ||||||
|  |     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; | ||||||
|  |     cursor: pointer; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .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; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* ===== py-terminal plugin ===== */ | ||||||
|  | /* XXX: it would be nice if these rules were stored in e.g. pyterminal.css and | ||||||
|  |    bundled together at build time (by rollup?) */ | ||||||
|  |  | ||||||
|  | .py-terminal { | ||||||
|  |     min-height: 10em; | ||||||
|  |     background-color: black; | ||||||
|  |     color: white; | ||||||
|  |     padding: 0.5rem; | ||||||
|  |     overflow: auto; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .py-terminal-hidden { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
							
								
								
									
										28907
									
								
								examples/04_cell_detector/resources/pyscript.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28907
									
								
								examples/04_cell_detector/resources/pyscript.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										424
									
								
								examples/04_cell_detector/resources/pyscript.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										424
									
								
								examples/04_cell_detector/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.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										51
									
								
								examples/04_cell_detector/serviceWorker.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								examples/04_cell_detector/serviceWorker.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | |||||||
|  |  | ||||||
|  | const pwa_version = "04_cell_detector_202306031006" | ||||||
|  | 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.2.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/04_cell_detector/site.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								examples/04_cell_detector/site.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  |  | ||||||
|  |      | ||||||
| @ -1 +1 @@ | |||||||
| __version__ = "0.1.0" | __version__ = "0.2.0" | ||||||
|  | |||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pyscript_bootstrap_templates/__pycache__/create.cpython-310.pyc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pyscript_bootstrap_templates/__pycache__/create.cpython-310.pyc
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| @ -323,8 +323,6 @@ class InputFile(InputElement): | |||||||
|  |  | ||||||
|             name = f.name |             name = f.name | ||||||
|  |  | ||||||
|             print("loaded ", name) |  | ||||||
|  |  | ||||||
|             uint8_array = Uint8Array.new(await f.arrayBuffer()) |             uint8_array = Uint8Array.new(await f.arrayBuffer()) | ||||||
|             buffer = io.BytesIO(bytearray(uint8_array)) |             buffer = io.BytesIO(bytearray(uint8_array)) | ||||||
|             self._files[name] = buffer |             self._files[name] = buffer | ||||||
|  | |||||||
| @ -1,10 +1,7 @@ | |||||||
|  |  | ||||||
| from distutils.command.config import config |  | ||||||
| from multiprocessing.sharedctypes import Value |  | ||||||
| import pathlib | import pathlib | ||||||
| from typing import List | from typing import List | ||||||
| import argparse | import argparse | ||||||
| from numpy import isin |  | ||||||
| import requests | import requests | ||||||
| import json | import json | ||||||
| import shutil | import shutil | ||||||
| @ -53,6 +50,7 @@ def create_pwa_manifest(root_folder: pathlib.Path, **kwargs): | |||||||
|  |  | ||||||
| def create_pwa_service_worker(root_folder: pathlib.Path, **kwargs): | def create_pwa_service_worker(root_folder: pathlib.Path, **kwargs): | ||||||
|     version = f"{kwargs['title']}_{dt.datetime.utcnow().strftime('%Y%m%d%H%M')}" |     version = f"{kwargs['title']}_{dt.datetime.utcnow().strftime('%Y%m%d%H%M')}" | ||||||
|  |     bslash = "\\" | ||||||
|     assets = [ |     assets = [ | ||||||
|         *[f'"./{p}",' for p in kwargs['paths']], |         *[f'"./{p}",' for p in kwargs['paths']], | ||||||
|         '"./index.html",', |         '"./index.html",', | ||||||
| @ -62,7 +60,7 @@ def create_pwa_service_worker(root_folder: pathlib.Path, **kwargs): | |||||||
|         '"./resources/pyscript.css",', |         '"./resources/pyscript.css",', | ||||||
|         '"./resources/pyscript.js",', |         '"./resources/pyscript.js",', | ||||||
|         '"./resources/pyscript.py",', |         '"./resources/pyscript.py",', | ||||||
|         f'"./resources/{kwargs["pyscript_bootstrap_templates_wheel_url"].split("/")[-1]}",', # FIXME: not completely os independent |         f'"./resources/{kwargs["pyscript_bootstrap_templates_wheel_url"].replace(bslash,"/").split("/")[-1]}",', # FIXME: not completely os independent | ||||||
|         '"./resources/pwa.js",', |         '"./resources/pwa.js",', | ||||||
|         '"./site.js",' |         '"./site.js",' | ||||||
|     ] |     ] | ||||||
| @ -148,7 +146,9 @@ def generate_project_files(root_folder: pathlib.Path, | |||||||
|         paths = [] |         paths = [] | ||||||
|  |  | ||||||
|     pyconfig = { |     pyconfig = { | ||||||
|         "autoclose_loader": True, |         "splashscreen":{ | ||||||
|  |             "autoclose": True, | ||||||
|  |         }, | ||||||
|         "packages": [ |         "packages": [ | ||||||
|             f"./resources/{pyscript_bootstrap_templates_wheel_url.split('/')[-1]}", |             f"./resources/{pyscript_bootstrap_templates_wheel_url.split('/')[-1]}", | ||||||
|             *packages |             *packages | ||||||
|  | |||||||
| @ -0,0 +1,18 @@ | |||||||
|  | Metadata-Version: 2.1 | ||||||
|  | Name: pyscript-bootstrap-templates | ||||||
|  | Version: 0.1.0 | ||||||
|  | Summary: templates and basic python/pyscript wrappers for bootstrap 5 | ||||||
|  | Home-page: https://github.com/pypa/sampleproject | ||||||
|  | Author: Jonas Weinz | ||||||
|  | Author-email: author@example.com | ||||||
|  | License: UNKNOWN | ||||||
|  | Keywords: sample,setuptools,development | ||||||
|  | Platform: UNKNOWN | ||||||
|  | Requires-Python: >=3.7, <4 | ||||||
|  | Description-Content-Type: text/markdown | ||||||
|  | License-File: LICENSE | ||||||
|  |  | ||||||
|  | # pyscript-bootstrap-templates | ||||||
|  |  | ||||||
|  | templates and pyscript wrappers for various bootstrap properties | ||||||
|  |  | ||||||
| @ -0,0 +1,9 @@ | |||||||
|  | LICENSE | ||||||
|  | README.md | ||||||
|  | setup.py | ||||||
|  | pyscript-bootstrap-templates/pyscript_bootstrap_templates.egg-info/PKG-INFO | ||||||
|  | pyscript-bootstrap-templates/pyscript_bootstrap_templates.egg-info/SOURCES.txt | ||||||
|  | pyscript-bootstrap-templates/pyscript_bootstrap_templates.egg-info/dependency_links.txt | ||||||
|  | pyscript-bootstrap-templates/pyscript_bootstrap_templates.egg-info/entry_points.txt | ||||||
|  | pyscript-bootstrap-templates/pyscript_bootstrap_templates.egg-info/requires.txt | ||||||
|  | pyscript-bootstrap-templates/pyscript_bootstrap_templates.egg-info/top_level.txt | ||||||
| @ -0,0 +1 @@ | |||||||
|  |  | ||||||
| @ -0,0 +1,2 @@ | |||||||
|  | [console_scripts] | ||||||
|  | create_pyscript_bootstrap_app = create:main | ||||||
| @ -0,0 +1,2 @@ | |||||||
|  | pillow | ||||||
|  | parse | ||||||
| @ -0,0 +1 @@ | |||||||
|  |  | ||||||
		Reference in New Issue
	
	Block a user