new advanced example, small fixes
This commit is contained in:
parent
c51c1ba64a
commit
95c4319340
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 @@
|
|||||||
|
|
Loading…
Reference in New Issue
Block a user