mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-21 11:15:36 -05:00
Compare commits
254 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25809660ef | ||
|
|
6b9eff45bb | ||
|
|
08e83feaf5 | ||
|
|
4f05b5afc6 | ||
|
|
9a5bf9918e | ||
|
|
1c7cf0ba7d | ||
|
|
ce2d1a4513 | ||
|
|
7d50d7eea0 | ||
|
|
e9411dc796 | ||
|
|
5cf2de16d1 | ||
|
|
e53bcf15a9 | ||
|
|
bec70b60b8 | ||
|
|
8b7fb89c68 | ||
|
|
b2bbdda73d | ||
|
|
ee2f46cfb9 | ||
|
|
4337e6833a | ||
|
|
a73c73b814 | ||
|
|
e8318a98f0 | ||
|
|
94f2ac6204 | ||
|
|
cc6cb4ded0 | ||
|
|
c61d4191c3 | ||
|
|
e284da7c09 | ||
|
|
9992096654 | ||
|
|
c696d92f40 | ||
|
|
af60299324 | ||
|
|
5aa9135a34 | ||
|
|
72e23ac86f | ||
|
|
33d49ad87d | ||
|
|
aa2335ca2e | ||
|
|
b31428006c | ||
|
|
dc1d583791 | ||
|
|
4299a74e40 | ||
|
|
3e408b7baa | ||
|
|
446c131ccb | ||
|
|
b062efcf17 | ||
|
|
30e31a86ef | ||
|
|
182272e8c7 | ||
|
|
06df21e8e3 | ||
|
|
cafebd68f2 | ||
|
|
061d4b3f72 | ||
|
|
6586e79d5e | ||
|
|
cda6c6bc7e | ||
|
|
a628026838 | ||
|
|
6700856b9f | ||
|
|
411aa0bbed | ||
|
|
0d79d31b96 | ||
|
|
cb05a9b067 | ||
|
|
536f359fb9 | ||
|
|
56e888ed33 | ||
|
|
687b93d148 | ||
|
|
0e1c396d7c | ||
|
|
7e24289703 | ||
|
|
0b23310a06 | ||
|
|
41ebaaf366 | ||
|
|
b79ceea7a8 | ||
|
|
b990bcb67a | ||
|
|
3f0f2d9910 | ||
|
|
4333f5f979 | ||
|
|
07e75293b8 | ||
|
|
a9ca7106cb | ||
|
|
da2728e6df | ||
|
|
adfa9a9b05 | ||
|
|
be9b9f66d3 | ||
|
|
9521bc7175 | ||
|
|
b445f8a834 | ||
|
|
3c3dffd5ed | ||
|
|
4c8443fd00 | ||
|
|
06a5a54103 | ||
|
|
0d3c3eef4e | ||
|
|
f0a6fb913f | ||
|
|
5f0c508fed | ||
|
|
16d9657982 | ||
|
|
40d098310e | ||
|
|
1345449d57 | ||
|
|
515858f313 | ||
|
|
2f452e9dc7 | ||
|
|
66119157a7 | ||
|
|
5b671dd1d0 | ||
|
|
1017362eec | ||
|
|
f67b8e0285 | ||
|
|
4c635fe84c | ||
|
|
68e463493e | ||
|
|
f1979d60b7 | ||
|
|
9150ebafec | ||
|
|
9543019336 | ||
|
|
1c53d91c6b | ||
|
|
4850f39b5a | ||
|
|
ab085c2d92 | ||
|
|
bf4d835948 | ||
|
|
214e39537b | ||
|
|
2d33afc195 | ||
|
|
87ea24ebd4 | ||
|
|
5380f8b9b3 | ||
|
|
00121ff8ba | ||
|
|
80e5d20e37 | ||
|
|
58f7c2137d | ||
|
|
f9194cc833 | ||
|
|
d9b8b48972 | ||
|
|
aa85f5f596 | ||
|
|
5341a0be4a | ||
|
|
0cfe20ca65 | ||
|
|
c352b502c4 | ||
|
|
58b4df6b3d | ||
|
|
29ba9436c8 | ||
|
|
2a044e88ad | ||
|
|
63092f9d72 | ||
|
|
0209324d57 | ||
|
|
1587273868 | ||
|
|
beb3aa1574 | ||
|
|
fe708c9fb4 | ||
|
|
e45d8bf973 | ||
|
|
b184c92f01 | ||
|
|
6c8afb05a7 | ||
|
|
d3dd4573cf | ||
|
|
d5cf68391a | ||
|
|
4e54e93450 | ||
|
|
e4f6387f18 | ||
|
|
54cb35b60a | ||
|
|
18ede2b729 | ||
|
|
f138b5a4f4 | ||
|
|
11a517bba4 | ||
|
|
66b57bf812 | ||
|
|
a9357bd97e | ||
|
|
d7c6d42c3d | ||
|
|
4dd1dc28b1 | ||
|
|
1e05ff7c95 | ||
|
|
e8e2e65584 | ||
|
|
3727e60152 | ||
|
|
0254012db6 | ||
|
|
8b97e4757f | ||
|
|
c70e121078 | ||
|
|
a4f97e6e46 | ||
|
|
800145a83c | ||
|
|
c75f885cb4 | ||
|
|
4011a51013 | ||
|
|
f4165dabcf | ||
|
|
de6c26eb05 | ||
|
|
5f319452d5 | ||
|
|
60d505d2d1 | ||
|
|
f64cc4dcae | ||
|
|
60e6f4293a | ||
|
|
00ab9a8d02 | ||
|
|
7d5f6c9ead | ||
|
|
a295edf19d | ||
|
|
b674515d06 | ||
|
|
d033ab04da | ||
|
|
304d76d088 | ||
|
|
978afdad97 | ||
|
|
d4e41e679d | ||
|
|
146264ff12 | ||
|
|
dcb107ae65 | ||
|
|
c236269d13 | ||
|
|
8f658e6d85 | ||
|
|
d203b60f44 | ||
|
|
a1a16aba74 | ||
|
|
6ded003447 | ||
|
|
4841e29fc6 | ||
|
|
0b014eea56 | ||
|
|
1c0be16f30 | ||
|
|
27ba8bea2f | ||
|
|
c566977749 | ||
|
|
5c1b785b4b | ||
|
|
8657dfb5da | ||
|
|
dfa837754e | ||
|
|
0a7df78770 | ||
|
|
066ecbe022 | ||
|
|
6c80db810f | ||
|
|
6023c413ab | ||
|
|
7910d040b6 | ||
|
|
5bd99f5224 | ||
|
|
f3157b377f | ||
|
|
e31e03afde | ||
|
|
eddde7c94c | ||
|
|
7be72ee4c1 | ||
|
|
6731467514 | ||
|
|
8dd699d235 | ||
|
|
6cb81b5c3d | ||
|
|
17187ba3ec | ||
|
|
531ee928b0 | ||
|
|
db806a5df9 | ||
|
|
9de154595a | ||
|
|
9e4cb79679 | ||
|
|
b7834073b8 | ||
|
|
b0e56577b5 | ||
|
|
ccb0e6b269 | ||
|
|
edfd4baa1f | ||
|
|
0f50f4a9fd | ||
|
|
47494e62a7 | ||
|
|
7aa25712d9 | ||
|
|
1db155570d | ||
|
|
1054e8e644 | ||
|
|
aa429f34d8 | ||
|
|
e351889811 | ||
|
|
24a70a8273 | ||
|
|
3f26657116 | ||
|
|
d41669af8b | ||
|
|
56466c2a00 | ||
|
|
fa7a97ca30 | ||
|
|
8aba271a42 | ||
|
|
8275aa2810 | ||
|
|
410ddf314c | ||
|
|
fa217bee20 | ||
|
|
817d0edc69 | ||
|
|
513dfe0b42 | ||
|
|
bd7a20309b | ||
|
|
a726be3c7c | ||
|
|
10f2054e9a | ||
|
|
2fa47f310d | ||
|
|
5b927a70c2 | ||
|
|
2a59ff8e68 | ||
|
|
e4d1befcdb | ||
|
|
844e04ff96 | ||
|
|
cc05a98b0e | ||
|
|
006d161a32 | ||
|
|
a4839db79a | ||
|
|
87e3b5b1dc | ||
|
|
faa900d502 | ||
|
|
a5275db3ec | ||
|
|
77e017a574 | ||
|
|
8ed8ddbf76 | ||
|
|
eb31978488 | ||
|
|
677d708588 | ||
|
|
ade0dca8f9 | ||
|
|
8e1cd0b268 | ||
|
|
9102768366 | ||
|
|
0c722b9164 | ||
|
|
b6f514451a | ||
|
|
c49fdfc56c | ||
|
|
676e04b28e | ||
|
|
6aa864a351 | ||
|
|
72acb4826c | ||
|
|
734be5f355 | ||
|
|
cede06ae19 | ||
|
|
19491d8010 | ||
|
|
c580aac991 | ||
|
|
032d1aaad7 | ||
|
|
afa216dc5e | ||
|
|
69339fe3de | ||
|
|
571bb2b294 | ||
|
|
91a09a09f7 | ||
|
|
9b3433f6ae | ||
|
|
ee9b0960f7 | ||
|
|
506ac2574f | ||
|
|
dc84d7c1b5 | ||
|
|
fcaa57307f | ||
|
|
d25e754beb | ||
|
|
7f6f411ea8 | ||
|
|
96a73e31f3 | ||
|
|
c7942d7d8f | ||
|
|
479348eec9 | ||
|
|
ebfed27630 | ||
|
|
8923485169 | ||
|
|
d62de26683 | ||
|
|
1dd9c5b009 |
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,4 +1,4 @@
|
||||
blank_issues_enabled: false
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Question
|
||||
url: https://community.anaconda.cloud/c/tech-topics/pyscript
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
2
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@@ -35,7 +35,7 @@ body:
|
||||
required: true
|
||||
- label: I searched for other feature requests and couldn't find a duplicate (including also the ``type-feature`` tag)
|
||||
required: true
|
||||
- label: I confirmed that it's not related to another project are area (see the above section)
|
||||
- label: I confirmed that it's not related to another project area (see the above section)
|
||||
required: true
|
||||
- type: textarea
|
||||
id: request-idea
|
||||
|
||||
18
.github/dashboard.yaml
vendored
18
.github/dashboard.yaml
vendored
@@ -1,18 +0,0 @@
|
||||
name: Push issue to Github Project dashboard
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- opened
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
|
||||
jobs:
|
||||
add_to_project:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/add-to-project@v0.0.3
|
||||
with:
|
||||
project-url: https://github.com/orgs/pyscript/projects/4/
|
||||
github-token: ${{ secrets.PROJECT_TOKEN }}
|
||||
26
.github/stale.yaml
vendored
Normal file
26
.github/stale.yaml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 60
|
||||
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- backlog
|
||||
- needs-triage
|
||||
- needs-work
|
||||
- epic
|
||||
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: >
|
||||
This issue has been automatically closed because it has not had
|
||||
recent activity. Thank you for your contributions.
|
||||
@@ -1,34 +1,32 @@
|
||||
name: '[CI] Build Latest'
|
||||
name: '[CI] Build Unstable'
|
||||
|
||||
on:
|
||||
push: # Only run on merges into main that modify files under pyscriptjs/
|
||||
push: # Only run on merges into main that modify files under pyscriptjs/ and examples/
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- pyscriptjs/**
|
||||
- examples/**
|
||||
- .github/workflows/build-latest.yml # Test that workflow works when changed
|
||||
|
||||
pull_request: # Run on any PR that modifies files in pyscriptjs/
|
||||
pull_request: # Run on any PR that modifies files under pyscriptjs/ and examples/
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- pyscriptjs/**
|
||||
- .github/workflows/build-latest.yml # Test that workflow works when changed
|
||||
|
||||
env:
|
||||
MINICONDA_PYTHON_VERSION: py38
|
||||
MINICONDA_VERSION: 4.11.0
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: pyscriptjs
|
||||
- examples/**
|
||||
- .github/workflows/build-unstable.yml # Test that workflow works when changed
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
BuildAndTest:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
defaults:
|
||||
run:
|
||||
working-directory: pyscriptjs
|
||||
env:
|
||||
MINICONDA_PYTHON_VERSION: py38
|
||||
MINICONDA_VERSION: 4.11.0
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
@@ -58,17 +56,46 @@ jobs:
|
||||
- name: Setup Environment
|
||||
run: make setup
|
||||
|
||||
- name: Build and Test
|
||||
run: make test
|
||||
- name: Build
|
||||
run: make build
|
||||
|
||||
- name: TypeScript Tests
|
||||
run: make test-ts
|
||||
|
||||
- name: Python Tests
|
||||
run: make test-py
|
||||
|
||||
- name: Integration Tests
|
||||
run: make test-integration-parallel
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: pyscript
|
||||
path: |
|
||||
pyscriptjs/build/
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
||||
Deploy:
|
||||
runs-on: ubuntu-latest
|
||||
needs: BuildAndTest
|
||||
if: github.ref == 'refs/heads/main' # Only deploy on merge into main
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: pyscript
|
||||
path: ./build/
|
||||
|
||||
# Deploy to S3
|
||||
- name: Configure AWS credentials
|
||||
if: github.ref == 'refs/heads/main' # Only deploy on merge into main
|
||||
uses: aws-actions/configure-aws-credentials@v1.6.1
|
||||
with:
|
||||
aws-region: ${{secrets.AWS_REGION}}
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
role-to-assume: ${{ secrets.AWS_OIDC_RUNNER_ROLE }}
|
||||
|
||||
- name: Sync to S3
|
||||
if: github.ref == 'refs/heads/main'
|
||||
run: aws s3 sync --quiet ./examples/build/ s3://pyscript.net/latest/
|
||||
run: aws s3 sync --quiet ./build/ s3://pyscript.net/unstable/
|
||||
11
.github/workflows/docs-release.yml
vendored
11
.github/workflows/docs-release.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v1.6.1
|
||||
with:
|
||||
aws-region: ${{secrets.AWS_REGION}}
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
role-to-assume: ${{ secrets.AWS_OIDC_RUNNER_ROLE }}
|
||||
|
||||
- name: Copy redirect file
|
||||
@@ -55,3 +55,12 @@ jobs:
|
||||
|
||||
- name: Sync to S3
|
||||
run: aws s3 sync --quiet ./docs/_build/html/ s3://docs.pyscript.net/${{ github.ref_name }}/
|
||||
|
||||
# Make sure to remove the latest folder so we sync the full docs upon release
|
||||
- name: Delete latest directory
|
||||
run: aws s3 rm --recursive s3://docs.pyscript.net/latest/
|
||||
|
||||
# Note that the files are the same as above, but we want to have folders with
|
||||
# /<tag name>/ AND /latest/ which latest will always point to the latest release
|
||||
- name: Sync to /latest
|
||||
run: aws s3 sync --quiet ./docs/_build/html/ s3://docs.pyscript.net/latest/
|
||||
|
||||
8
.github/workflows/docs-review.yml
vendored
8
.github/workflows/docs-review.yml
vendored
@@ -18,8 +18,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: >-
|
||||
!github.event.repository.fork
|
||||
if: github.repository_owner == 'pyscript'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -58,15 +57,12 @@ jobs:
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v1.6.1
|
||||
with:
|
||||
aws-region: ${{secrets.AWS_REGION}}
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
role-to-assume: ${{ secrets.AWS_OIDC_RUNNER_ROLE }}
|
||||
|
||||
- name: Copy redirect file
|
||||
run: aws s3 cp --quiet ./docs/_build/html/_static/redirect.html s3://docs.pyscript.net/index.html
|
||||
|
||||
# - name: Delete review directory
|
||||
# run: aws s3 rm --recursive s3://docs.pyscript.net/review/${{ github.event.number }}/
|
||||
|
||||
- name: Sync to S3
|
||||
run: aws s3 sync --quiet ./docs/_build/html/ s3://docs.pyscript.net/review/${{ github.event.number }}/
|
||||
|
||||
|
||||
@@ -47,14 +47,9 @@ jobs:
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v1.6.1
|
||||
with:
|
||||
aws-region: ${{secrets.AWS_REGION}}
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
role-to-assume: ${{ secrets.AWS_OIDC_RUNNER_ROLE }}
|
||||
|
||||
- name: Copy redirect file
|
||||
run: aws s3 cp --quiet ./docs/_build/html/_static/redirect.html s3://docs.pyscript.net/index.html
|
||||
|
||||
# - name: Delete latest directory
|
||||
# run: aws s3 rm --recursive s3://docs.pyscript.net/latest/
|
||||
|
||||
# Sync will only copy changed files
|
||||
- name: Sync to S3
|
||||
run: aws s3 sync --quiet ./docs/_build/html/ s3://docs.pyscript.net/latest/
|
||||
run: aws s3 sync --quiet ./docs/_build/html/ s3://docs.pyscript.net/unstable/
|
||||
@@ -1,4 +1,4 @@
|
||||
name: '[CI] Build Release'
|
||||
name: '[CI] Prepare Release'
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -48,9 +48,13 @@ jobs:
|
||||
- name: Build and Test
|
||||
run: make test
|
||||
|
||||
- name: Zip build folder
|
||||
run: zip -r -q ./build.zip ./build
|
||||
|
||||
- name: Prepare Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
prerelease: true
|
||||
generate_release_notes: true
|
||||
files: ./build.zip
|
||||
7
.github/workflows/publish-release.yml
vendored
7
.github/workflows/publish-release.yml
vendored
@@ -53,9 +53,10 @@ jobs:
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v1.6.1
|
||||
with:
|
||||
aws-region: ${{secrets.AWS_REGION}}
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
role-to-assume: ${{ secrets.AWS_OIDC_RUNNER_ROLE }}
|
||||
|
||||
- name: Sync to S3
|
||||
run: | # Overwrite "latest" alpha + versioned subdirectory
|
||||
aws s3 sync --quiet ./examples/build/ s3://pyscript.net/releases/${{ github.ref_name }}
|
||||
run: | # Update /latest and create an explicitly versioned directory under releases/YYYY.MM.MICRO/
|
||||
aws s3 sync --quiet ./build/ s3://pyscript.net/latest/
|
||||
aws s3 sync --quiet ./build/ s3://pyscript.net/releases/${{ github.ref_name }}/
|
||||
|
||||
26
.github/workflows/publish-snapshot.yml
vendored
Normal file
26
.github/workflows/publish-snapshot.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: '[CI] Publish Snapshot'
|
||||
# Copy /unstable/ to /snapshots/2022.09.1.RC1/
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
snapshot_version:
|
||||
description: 'The calver version of this snapshot: 2022.09.1 or 2022.09.1.RC1'
|
||||
type: string
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
snapshot:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v1.6.1
|
||||
with:
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
role-to-assume: ${{ secrets.AWS_OIDC_RUNNER_ROLE }}
|
||||
- name: Sync to S3
|
||||
run: >
|
||||
aws s3 sync s3://pyscript.net/unstable/ s3://pyscript.net/snapshots/${{ inputs.snapshot_version }}/
|
||||
10
.github/workflows/sync-examples.yml
vendored
10
.github/workflows/sync-examples.yml
vendored
@@ -1,12 +1,8 @@
|
||||
name: '[CI] Sync Examples'
|
||||
|
||||
on:
|
||||
push: # Only run on merges into main that modify files under examples/
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- examples/**
|
||||
- .github/workflows/sync-examples.yml # Test that workflow works when changed
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -26,7 +22,7 @@ jobs:
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v1.6.1
|
||||
with:
|
||||
aws-region: ${{secrets.AWS_REGION}}
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
role-to-assume: ${{ secrets.AWS_OIDC_RUNNER_ROLE }}
|
||||
- name: Sync to S3
|
||||
# Sync outdated or new files, delete ones no longer in source
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -136,3 +136,5 @@ dmypy.json
|
||||
.pyre/
|
||||
|
||||
node_modules/
|
||||
|
||||
coverage/
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
default_stages: [commit]
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.3.0
|
||||
rev: v4.4.0
|
||||
hooks:
|
||||
- id: check-builtin-literals
|
||||
- id: check-case-conflict
|
||||
@@ -25,57 +25,57 @@ repos:
|
||||
hooks:
|
||||
- id: bandit
|
||||
args:
|
||||
- --skip=B201
|
||||
- --skip=B101,B201
|
||||
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.3.0
|
||||
rev: 22.12.0
|
||||
hooks:
|
||||
- id: black
|
||||
|
||||
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.1.0
|
||||
rev: v2.2.2
|
||||
hooks:
|
||||
- id: codespell # See 'setup.cfg' for args
|
||||
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 4.0.1
|
||||
rev: 6.0.0
|
||||
hooks:
|
||||
- id: flake8 # See 'setup.cfg' for args
|
||||
additional_dependencies: [flake8-bugbear, flake8-comprehensions]
|
||||
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.10.1
|
||||
rev: 5.11.1
|
||||
hooks:
|
||||
- id: isort
|
||||
name: isort (python)
|
||||
args: [--profile, black]
|
||||
|
||||
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
|
||||
rev: v2.3.0
|
||||
rev: v2.5.0
|
||||
hooks:
|
||||
- id: pretty-format-yaml
|
||||
args: [--autofix, --indent, '4']
|
||||
exclude: .github/ISSUE_TEMPLATE/.*\.yml$
|
||||
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v2.34.0
|
||||
rev: v3.3.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args:
|
||||
- --py310-plus
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-eslint
|
||||
rev: v8.18.0
|
||||
rev: v8.29.0
|
||||
hooks:
|
||||
- id: eslint
|
||||
files: pyscriptjs/src/.*\.[jt]sx?$ # *.js, *.jsx, *.ts and *.tsx
|
||||
types: [file]
|
||||
additional_dependencies:
|
||||
- eslint
|
||||
- eslint-plugin-svelte3
|
||||
- typescript
|
||||
- '@typescript-eslint/eslint-plugin'
|
||||
- '@typescript-eslint/parser'
|
||||
- eslint@8.25.0
|
||||
- typescript@4.8.4
|
||||
- '@typescript-eslint/eslint-plugin@5.39.0'
|
||||
- '@typescript-eslint/parser@5.39.0'
|
||||
|
||||
# Commented out until mdformat-myst supports custom extensions
|
||||
# See https://github.com/executablebooks/mdformat-myst/pull/9
|
||||
|
||||
@@ -9,58 +9,39 @@ Thank you for wanting to contribute to the PyScript project!
|
||||
* [Reporting bugs](#reporting-bugs)
|
||||
* [Reporting security issues](#reporting-security-issues)
|
||||
* [Asking questions](#asking-questions)
|
||||
* [Setting up your environment](#setting-up-your-environment)
|
||||
* [Setting up your local environment and developing](#setting-up-your-local-environment-and-developing)
|
||||
* [Places to start](#places-to-start)
|
||||
* [Submitting a change](#submitting-a-change)
|
||||
* [License terms for contributions](#license-terms-for-contributions)
|
||||
* [Becoming a maintainer](#becoming-a-maintainer)
|
||||
* [Trademarks](#trademarks)
|
||||
|
||||
## Code of Conduct
|
||||
# Code of Conduct
|
||||
|
||||
The [PyScript Code of Conduct](https://github.com/pyscript/governance/blob/main/CODE-OF-CONDUCT.md) governs the project and everyone participating in it. By participating, you are expected to uphold this code. Please report unacceptable behavior to the maintainers or administrators as described in that document.
|
||||
|
||||
## Contributing
|
||||
# Contributing
|
||||
|
||||
### Reporting bugs
|
||||
## Reporting bugs
|
||||
|
||||
Bugs are tracked on the [project issues page](https://github.com/pyscript/pyscript/issues). Please check if your issue has already been filed by someone else by searching the existing issues before filing a new one. Once your issue is filed, it will be triaged by another contributor or maintainer. If there are questions raised about your issue, please respond promptly.
|
||||
|
||||
#### Creating useful issues
|
||||
## Creating useful issues
|
||||
|
||||
* Use a clear and descriptive title.
|
||||
* Describe the specific steps that reproduce the problem with as many details as possible so that someone can verify the issue.
|
||||
* Describe the behavior you observed, and the behavior you had expected.
|
||||
* Include screenshots if they help make the issue clear.
|
||||
|
||||
### Reporting security issues
|
||||
## Reporting security issues
|
||||
|
||||
If you aren't confident that it is appropriate to submit a security issue using the above process, you can e-mail it to security@pyscript.net
|
||||
|
||||
### Asking questions
|
||||
## Asking questions
|
||||
|
||||
If you have questions about the project, using PyScript, or anything else, please ask in the [PyScript forum](https://community.anaconda.cloud/c/tech-topics/pyscript).
|
||||
|
||||
### Setting up your environment
|
||||
|
||||
* clone the repo
|
||||
```
|
||||
git clone https://github.com/pyscript/pyscript
|
||||
```
|
||||
* cd into the main project folder
|
||||
```
|
||||
cd pyscript/pyscriptjs
|
||||
```
|
||||
* install the dependencies with npm install - make sure to use nodejs version >= 16
|
||||
```
|
||||
npm install
|
||||
```
|
||||
* run npm run dev to build and run the dev server. This will also watch for changes and rebuild when a file is saved.
|
||||
```
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Places to start
|
||||
## Places to start
|
||||
|
||||
If you would like to contribute to PyScript, but you aren't sure where to begin, here are some suggestions.
|
||||
|
||||
@@ -69,22 +50,16 @@ If you would like to contribute to PyScript, but you aren't sure where to begin,
|
||||
* **Look over the open pull requests.** Do you have comments or suggestions for the proposed changes? Add them.
|
||||
* **Check out the examples.** Is there a use case that would be good to have sample code for? Create an example for it.
|
||||
|
||||
### Submitting a change
|
||||
## Setting up your local environment and developing
|
||||
|
||||
All contributions must be licensed Apache 2.0, and all files must have a copy of the boilerplate license comment (can be copied from an existing file).
|
||||
If you would like to contribute to PyScript, you will need to set up a local development environment. The [following instructions](https://docs.pyscript.net/latest/development/setting-up-environment.html) will help you get started.
|
||||
|
||||
To create a change for PyScript, you can follow the process described [here](https://docs.github.com/en/get-started/quickstart/contributing-to-projects).
|
||||
|
||||
* Fork a personal copy of the PyScript project.
|
||||
* Make the changes you would like (don't forget to test them!)
|
||||
* Please squash all commits for a change into a single commit (this can be done using "git rebase -i"). Do your best to have a well-formed commit message for the change.
|
||||
* Open a pull request back to the PyScript project and address any comments/questions from the maintainers and other contributors.
|
||||
You can also read the [developing process](https://docs.pyscript.net/latest/development/developing.html) and how to rebase your branch with the latest changes.
|
||||
|
||||
## License terms for contributions
|
||||
|
||||
This Project welcomes contributions, suggestions, and feedback. All contributions, suggestions, and feedback you submitted are accepted under the [Apache 2.0](./LICENSE) license. You represent that if you do not own copyright in the code that you have the authority to submit it under the [Apache 2.0](./LICENSE) license. All feedback, suggestions, or contributions are not confidential.
|
||||
|
||||
|
||||
## Becoming a maintainer
|
||||
|
||||
Contributors are invited to be maintainers of the project by demonstrating good decision making in their contributions, a commitment to the goals of the project, and consistent adherence to the [code of conduct](https://github.com/pyscript/governance/blob/main/CODE-OF-CONDUCT.md). New maintainers are invited by a 3/4 vote of the existing maintainers.
|
||||
|
||||
@@ -2,15 +2,20 @@
|
||||
|
||||
This document lists the Maintainers of the Project. Maintainers may be added once approved by the existing maintainers as described in the [Governance document](https://github.com/pyscript/pyscript/blob/main/GOVERNANCE.md). By adding your name to this list you are agreeing to abide by the Project governance documents and to abide by all of the Organization's polices, including the [code of conduct](https://github.com/pyscript/governance/blob/main/CODE-OF-CONDUCT.md), [trademark policy](https://github.com/pyscript/governance/blob/main/TRADEMARKS.md), and [antitrust policy](https://github.com/pyscript/governance/blob/main/TRADEMARKS.md). If you are participating because of your affiliation with another organization (designated below), you represent that you have the authority to bind that organization to these policies.
|
||||
|
||||
| **NAME** | **Organization** |
|
||||
| ---------------- | ---------------- |
|
||||
| Fabio Pliger | Anaconda, Inc |
|
||||
| Antonio Cuni | Anaconda, Inc |
|
||||
| Philipp Rudiger | Anaconda, Inc |
|
||||
| Peter Wang | Anaconda, Inc |
|
||||
| Kevin Goldsmith | Anaconda, Inc |
|
||||
| Mariana Meireles | Anaconda, Inc |
|
||||
| --- | --- |
|
||||
| **NAME** | **Organization** |
|
||||
| -------------------- | ---------------- |
|
||||
| Fabio Pliger | Anaconda, Inc |
|
||||
| Antonio Cuni | Anaconda, Inc |
|
||||
| Philipp Rudiger | Anaconda, Inc |
|
||||
| Peter Wang | Anaconda, Inc |
|
||||
| Kevin Goldsmith | Anaconda, Inc |
|
||||
| Mariana Meireles | Anaconda, Inc |
|
||||
| Nicholas H.Tollervey | Anaconda, Inc |
|
||||
| Madhur Tandon | Anaconda, Inc |
|
||||
| Ted Patrick | Anaconda, Inc |
|
||||
| Jeff Glass | --- |
|
||||
| Paul Everitt | --- |
|
||||
| Fabio Rosado | --- |
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
|
||||
12
README.md
12
README.md
@@ -4,7 +4,7 @@
|
||||
|
||||
### Summary
|
||||
|
||||
PyScript is a Pythonic alternative to Scratch, JSFiddle, and other "easy to use" programming frameworks, with the goal of making the web a friendly, hackable place where anyone can author interesting and interactive applications.
|
||||
PyScript is a framework that allows users to create rich Python applications in the browser using HTML's interface and the power of [Pyodide](https://pyodide.org/en/stable/), [WASM](https://webassembly.org/), and modern web technologies.
|
||||
|
||||
To get started see the [getting started tutorial](docs/tutorials/getting-started.md).
|
||||
|
||||
@@ -15,10 +15,12 @@ PyScript is a meta project that aims to combine multiple open technologies into
|
||||
|
||||
## Try PyScript
|
||||
|
||||
To try PyScript, import the appropriate pyscript files to your html page with:
|
||||
To try PyScript, import the appropriate pyscript files into the ```<head>``` tag of your html page with:
|
||||
```html
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
```
|
||||
You can then use PyScript components in your html page. PyScript currently implements the following elements:
|
||||
|
||||
@@ -33,9 +35,11 @@ Read the [contributing guide](CONTRIBUTING.md) to learn about our development pr
|
||||
|
||||
## Resources
|
||||
|
||||
* [Official docs](https://docs.pyscript.net)
|
||||
* [Discussion board](https://community.anaconda.cloud/c/tech-topics/pyscript)
|
||||
* [Home Page](https://pyscript.net/)
|
||||
* [Blog Post](https://engineering.anaconda.com/2022/04/welcome-pyscript.html)
|
||||
* [Discord Channel](https://discord.gg/BYB2kvyFwm)
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
18
TROUBLESHOOTING.md
Normal file
18
TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Troubleshooting
|
||||
|
||||
This page is meant for troubleshooting common problems with PyScript.
|
||||
|
||||
## Table of contents:
|
||||
* [Make Setup](#make-setup)
|
||||
|
||||
## Make setup
|
||||
|
||||
A lot of problems related to `make setup` are related to node and npm being outdated. Once npm and node are updated, `make setup` should work. You can follow the steps on the [npm documentation](https://docs.npmjs.com/try-the-latest-stable-version-of-npm) to update npm (the update command for Linux should work for Mac as well). Once npm has been updated you can continue to the instructions to update node below.
|
||||
|
||||
To update Node run the following commands in order (Most likely you'll be prompted for your user password, this is normal):
|
||||
|
||||
```
|
||||
sudo npm cache clean -f
|
||||
sudo npm install -g n
|
||||
sudo n stable
|
||||
```
|
||||
@@ -31,7 +31,8 @@ shell:
|
||||
@echo 'conda activate $(env)'
|
||||
|
||||
htmlserve:
|
||||
python -m http.server -d "$(BUILDDIR)/html/"
|
||||
@echo 'visit docs at http://localhost:8080'
|
||||
python -m http.server -d "$(BUILDDIR)/html/" 8080
|
||||
|
||||
livehtml:
|
||||
sphinx-autobuild "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
@@ -9,9 +9,23 @@ Before you start contributing to the documentation, it's worthwhile to
|
||||
take a look at the general contributing guidelines for the PyScript project. You can find these guidelines here
|
||||
[Contributing Guidelines](https://github.com/pyscript/pyscript/blob/main/CONTRIBUTING.md)
|
||||
|
||||
## Documentation Principles
|
||||
|
||||
The PyScript documentation is based on a documentation framework called [Diátaxis](https://diataxis.fr/). This framework helps to solve the problem of structure in technical documentation and identifies four modes of documentation - **tutorials, how-to guides, technical reference and explanation**. Each one of these modes answers to a different user need, fulfills a different purpose and requires a different approach to its creation.
|
||||
|
||||
The picture below gives a good visual representation of that separation of concerns:
|
||||
|
||||

|
||||
|
||||
So, please keep that in mind when contributing to the project documentation. For more information on, make sure to check [their website](https://diataxis.fr/).
|
||||
|
||||
### Setup
|
||||
|
||||
The `docs` directory in the pyscript repository contains a
|
||||
[Sphinx](https://www.sphinx-doc.org/) documentation project. Sphinx is a system
|
||||
that takes plaintext files containing documentation written in Markdown, along with
|
||||
and static files like templates and themes, to build the static end result.
|
||||
static files like templates and themes, to build the static end result.
|
||||
|
||||
### Build
|
||||
|
||||
To learn how to build the docs, head over the [CONTRIBUTING](../CONTRIBUTING.md) page.
|
||||
|
||||
16
docs/_static/examples/what-is-pyscript.html
vendored
16
docs/_static/examples/what-is-pyscript.html
vendored
@@ -1,7 +1,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<style>
|
||||
.pulse {
|
||||
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
@@ -16,10 +16,6 @@
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<py-env>
|
||||
- numpy
|
||||
- matplotlib
|
||||
</py-env>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -29,6 +25,14 @@
|
||||
<p style='font-family: monospace sans-serif;'><big><big><big><big>❰py❱</big></big></big></big></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<py-config>
|
||||
packages = [
|
||||
"numpy",
|
||||
"matplotlib"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
<py-script>
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
BIN
docs/_static/images/avatar.jpg
vendored
BIN
docs/_static/images/avatar.jpg
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
16
docs/_static/images/pyscript.svg
vendored
Normal file
16
docs/_static/images/pyscript.svg
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<svg width="100%" viewBox="0 0 2057 974" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#fda703" stroke="none" transform="translate(0 100)">
|
||||
<path
|
||||
d="M 1092.534 158.364 C 1095.764 169.589 1102.374 179.795 1107.224 190.364 C 1119.104 216.243 1131.874 241.728 1144.274 267.364 C 1179.204 339.56 1214.064 411.844 1248.314 484.364 C 1260.474 510.112 1273.154 535.617 1285.314 561.364 C 1290.014 571.319 1299.154 583.378 1300.684 594.364 C 1301.444 599.785 1296.944 606.478 1294.984 611.364 C 1289.004 626.289 1282.004 640.557 1273.734 654.364 C 1265.284 668.483 1256.704 683.257 1245.444 695.364 C 1237.304 704.123 1228.664 712.851 1218.534 719.31 C 1176.654 746.023 1130.104 739.811 1084.534 729.364 L 1084.534 796.364 C 1137.744 803.235 1191.744 806.988 1241.534 782.094 C 1291.224 757.25 1321.144 708.125 1345.794 660.364 C 1391.424 571.949 1425.474 477.074 1463.954 385.364 C 1484.774 335.759 1505.144 285.968 1525.954 236.364 C 1532.804 220.048 1539.454 203.643 1546.384 187.364 C 1550.314 178.14 1555.824 168.274 1557.534 158.364 L 1503.534 158.364 C 1498.104 158.364 1487.624 156.363 1482.924 159.392 C 1477.284 163.031 1474.824 176.375 1472.254 182.364 C 1463.294 203.198 1455.174 224.401 1446.524 245.364 C 1422.624 303.289 1398.764 361.248 1375.334 419.364 C 1365.024 444.923 1349.894 471.569 1343.534 498.364 L 1341.534 498.364 L 1326.784 467.364 L 1300.794 414.364 L 1219.784 248.364 L 1188.284 184.364 L 1174.894 159.392 L 1152.534 158.364 L 1092.534 158.364 Z">
|
||||
</path>
|
||||
<path
|
||||
d="M 100.534 391.364 C 109.625 398.897 122.97 403.329 133.534 408.611 L 197.534 440.611 L 405.534 544.611 C 436.606 560.147 467.458 576.073 498.534 591.611 C 511.98 598.334 527.713 609.722 542.534 612.364 L 542.534 563.364 L 541.506 543.754 L 518.534 531.117 L 460.534 502.117 L 307.534 425.117 L 240.534 391.364 L 307.534 358.117 L 459.534 282.611 L 518.534 253.117 L 541.506 240.727 L 542.534 221.364 L 542.534 171.364 C 527.073 174.12 510.565 186.102 496.534 193.117 L 398.534 242.117 L 200.534 341.117 C 167.367 357.701 132.553 372.676 100.534 391.364 Z">
|
||||
</path>
|
||||
<path
|
||||
d="M 1600.534 171.364 L 1600.534 220.364 C 1600.534 225.605 1598.654 235.422 1601.564 239.974 C 1605.194 245.662 1617.614 249.159 1623.534 252.117 L 1680.534 280.611 C 1730.924 305.806 1781.134 331.41 1831.534 356.611 C 1853.974 367.829 1877.404 384.412 1901.534 391.364 L 1901.534 393.364 C 1875.624 400.829 1849.674 418.049 1825.534 430.117 L 1679.534 503.117 C 1661.964 511.903 1644.564 521.567 1626.534 529.364 C 1619.964 532.203 1605.494 536.596 1601.564 542.754 C 1598.654 547.306 1600.534 557.122 1600.534 562.364 L 1600.534 612.364 L 1655.534 585.611 L 1763.534 531.611 L 1947.534 439.611 L 2041.534 392.364 C 2031.474 382.202 2012.324 376.511 1999.534 370.117 L 1907.534 324.117 L 1701.534 221.117 L 1635.534 188.117 C 1624.294 182.495 1612.624 174.847 1600.534 171.364 Z">
|
||||
</path>
|
||||
<path
|
||||
d="M 704.534 384.364 C 704.534 374.13 702.051 360.064 705.503 350.364 C 710.589 336.071 722.183 321.459 731.164 309.364 C 737.516 300.809 743.992 292.429 750.959 284.364 C 786.81 242.863 854.576 189.488 905.519 239.403 C 931.848 265.201 939.204 301.065 941.623 336.364 C 946.631 409.413 926.04 491.22 860.534 532.928 C 811.862 563.917 757.912 556.382 704.534 545.364 Z M 705.534 259.364 L 704.534 259.364 L 704.534 158.364 L 628.534 158.364 L 628.534 789.364 L 704.534 789.364 L 704.534 613.364 C 728.157 613.38 751.915 618.29 775.534 619.325 C 816.206 621.106 857.009 614.508 893.534 596.116 C 989.069 548.011 1025.008 434.77 1024.535 335.364 C 1024.298 285.5 1013.766 232.452 979.364 194.364 C 968.209 182.013 954.851 171.287 940.534 162.816 C 875.388 124.27 794.704 158.21 745.534 207.364 C 730.887 222.007 713.84 240.114 705.534 259.364 Z">
|
||||
</path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -19,7 +19,6 @@ To try it in your browser, copy the code below into an online HTML editor like W
|
||||
|
||||
```{literalinclude} ../_static/examples/what-is-pyscript.html
|
||||
---
|
||||
linenos:
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
@@ -54,7 +54,7 @@ exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "_env", "README.md"]
|
||||
#
|
||||
html_theme = "pydata_sphinx_theme"
|
||||
|
||||
html_logo = "_static/images/avatar.jpg"
|
||||
html_logo = "_static/images/pyscript.svg"
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
|
||||
30
docs/development/deprecation-cycle.md
Normal file
30
docs/development/deprecation-cycle.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Deprecation Cycle
|
||||
|
||||
Pyscript is under heavy development, which means that some things may change, and some features might need to be deprecated so you can use different alternative implementations.
|
||||
|
||||
This page describes the deprecation cycle for pyscript.
|
||||
|
||||
## Deprecation Steps
|
||||
|
||||
1. Remove usage of deprecated features from all examples.
|
||||
2. Add warnings to all elements/features marked for deprecation.
|
||||
3. Release a new version of pyscript with the deprecation warnings.
|
||||
4. Next release, remove the deprecated features.
|
||||
|
||||
## Deprecation Warnings
|
||||
|
||||
Deprecation warnings are added to the codebase using the `showWarning` function from the `pyscriptjs.utils` module.
|
||||
|
||||
This function creates a warning banner on the page if any of the deprecated features was used. You can use HTML to write the message; ideally, you should provide an alternative to the deprecated feature.
|
||||
|
||||
### Example
|
||||
|
||||
```js
|
||||
import {showWarning} from './utils'
|
||||
|
||||
showWarning(`
|
||||
<p>
|
||||
The <code>py-deprecated</code> tag is deprecated. Please use the <code>py-actual</code> tag instead. Please refer to <a href="#">this documentation page</a> for more information.
|
||||
</p>
|
||||
`, "html")
|
||||
```
|
||||
203
docs/development/developing.md
Normal file
203
docs/development/developing.md
Normal file
@@ -0,0 +1,203 @@
|
||||
# Developing Process
|
||||
|
||||
This document is intended to help you get started developing for pyscript, it assumes that you have [setup your development environment](setting-up-environment.md).
|
||||
|
||||
* First, make sure you are using the latest version of the pyscript main branch
|
||||
|
||||
```
|
||||
git pull upstream main
|
||||
```
|
||||
|
||||
* Update your fork with the latest changes
|
||||
|
||||
```
|
||||
git push origin main
|
||||
```
|
||||
|
||||
* Activate the conda environment (this environment will contain all the necessary dependencies)
|
||||
|
||||
```
|
||||
conda activate pyscriptjs/env/
|
||||
```
|
||||
**NOTE**: We are assuming you are in the root folder. If you are in the pyscriptjs you can run `conda activate env/` instead.
|
||||
|
||||
* Install pre-commit (you only need to do this once)
|
||||
|
||||
```
|
||||
pre-commit install
|
||||
```
|
||||
**NOTE**: On first run, pre-commit installs a bunch of hooks that will be run when you commit changes to your branch - this will make sure that your code is following our style (it will also lint your code automatically).
|
||||
|
||||
* Create a branch for the issue that you want to work on
|
||||
|
||||
```
|
||||
git checkout -b <your branch name>
|
||||
```
|
||||
|
||||
* Work on your change
|
||||
|
||||
**NOTE**: If you are working on a python file, you may encounter linting issues when pre-commit runs. Pyscript uses [black](https://black.readthedocs.io/en/stable/) to fix any linting problems automatically. All you need to do is add the changes again and commit using your previous commit message (the previous one that failed didn't complete due to black formatting files).
|
||||
|
||||
* Run tests before pushing the changes
|
||||
|
||||
```
|
||||
make tests
|
||||
```
|
||||
|
||||
* When you make changes locally, double check that your contribution follows the PyScript formatting rules by running `npm run lint`. Note that in this case you're looking for the errors, <u>**NOT**</u> the warnings (Unless the warning is created by a local change). If an error is found by lint you should fix it <u>**before**</u> creating a pull request
|
||||
|
||||
|
||||
## Rebasing changes
|
||||
|
||||
Sometimes you might be asked to rebase main into your branch. Please refer to this [section on git rebase from GitHub docs](https://docs.github.com/en/get-started/using-git/about-git-rebase).
|
||||
|
||||
If you need help with anything, feel free to reach out and ask for help!
|
||||
|
||||
|
||||
## pytest quick guide
|
||||
|
||||
We make a heavy usage of `pytest`. Here is a quick guide and collection of
|
||||
useful options:
|
||||
|
||||
- To run all tests in the current directory and subdirectories: `pytest`
|
||||
|
||||
- To run tests in a specific directory or file: `pytest path/to/dir/test_foo.py`
|
||||
|
||||
- `-s`: disables output capturing
|
||||
|
||||
- `--pdb`: in case of exception, enter a `(Pdb)` prompt so that you can
|
||||
inspect what went wrong.
|
||||
|
||||
- `-v`: verbose mode
|
||||
|
||||
- `-x`: stop the execution as soon as one test fails
|
||||
|
||||
- `-k foo`: run only the tests whose full name contains `foo`
|
||||
|
||||
- `-k 'foo and bar'`
|
||||
|
||||
- `-k 'foo and not bar'`
|
||||
|
||||
|
||||
## Running integration tests under pytest
|
||||
|
||||
`make test` is useful to run all the tests, but during the development is
|
||||
useful to have more control on how tests are run. The following guide assumes
|
||||
that you are in the directory `pyscriptjs/tests/integration/`.
|
||||
|
||||
#### To run all the integration tests, single or multi core
|
||||
|
||||
```
|
||||
$ pytest -xv
|
||||
...
|
||||
|
||||
test_00_support.py::TestSupport::test_basic[chromium] PASSED [ 0%]
|
||||
test_00_support.py::TestSupport::test_console[chromium] PASSED [ 1%]
|
||||
test_00_support.py::TestSupport::test_check_js_errors_simple[chromium] PASSED [ 2%]
|
||||
test_00_support.py::TestSupport::test_check_js_errors_expected[chromium] PASSED [ 3%]
|
||||
test_00_support.py::TestSupport::test_check_js_errors_expected_but_didnt_raise[chromium] PASSED [ 4%]
|
||||
test_00_support.py::TestSupport::test_check_js_errors_multiple[chromium] PASSED [ 5%]
|
||||
...
|
||||
```
|
||||
|
||||
`-x` means "stop at the first failure". `-v` means "verbose", so that you can
|
||||
see all the test names one by one. We try to keep tests in a reasonable order,
|
||||
from most basic to most complex. This way, if you introduced some bug in very
|
||||
basic things, you will notice immediately.
|
||||
|
||||
If you have the `pytest-xdist` plugin installed, you can run all the
|
||||
integration tests on 4 cores in parallel:
|
||||
```
|
||||
$ pytest -n 4
|
||||
```
|
||||
|
||||
#### To run a single test, headless
|
||||
```
|
||||
$ pytest test_01_basic.py -k test_pyscript_hello -s
|
||||
...
|
||||
[ 0.00 page.goto ] pyscript_hello.html
|
||||
[ 0.01 request ] 200 - fake_server - http://fake_server/pyscript_hello.html
|
||||
...
|
||||
[ 0.17 console.info ] [py-loader] Downloading pyodide-0.21.3...
|
||||
[ 0.18 request ] 200 - CACHED - https://cdn.jsdelivr.net/pyodide/v0.21.3/full/pyodide.js
|
||||
...
|
||||
[ 3.59 console.info ] [pyscript/main] PyScript page fully initialized
|
||||
[ 3.60 console.log ] hello pyscript
|
||||
```
|
||||
|
||||
`-k` selects tests by pattern matching as described above. `-s` instructs
|
||||
`pytest` to show the output to the terminal instead of capturing it. In the
|
||||
output you can see various useful things, including network requests and JS
|
||||
console messages.
|
||||
|
||||
#### To run a single test, headed
|
||||
```
|
||||
$ pytest test_01_basic.py -k test_pyscript_hello -s --headed
|
||||
...
|
||||
```
|
||||
|
||||
Same as above, but with `--headed` the browser is shown in a window, and you
|
||||
can interact with it. The browser uses a fake server, which means that HTTP
|
||||
requests are cached.
|
||||
|
||||
Unfortunately, in this mode source maps does not seem to work, and you cannot
|
||||
debug the original typescript source code. This seems to be a bug in
|
||||
playwright, for which we have a workaround:
|
||||
|
||||
```
|
||||
$ pytest test_01_basic.py -k test_pyscript_hello -s --headed --no-fake-server
|
||||
...
|
||||
```
|
||||
|
||||
As the name implies, `-no-fake-server` disables the fake server: HTTP requests
|
||||
are not cached, but source-level debugging works.
|
||||
|
||||
Finally:
|
||||
|
||||
```
|
||||
$ pytest test_01_basic.py -k test_pyscript_hello -s --dev
|
||||
...
|
||||
```
|
||||
|
||||
`--dev` implies `--headed --no-fake-server`. In addition, it also
|
||||
automatically open chrome dev tools.
|
||||
|
||||
|
||||
#### Fake server, HTTP cache
|
||||
|
||||
By default, our test machinery uses a playwright router which intercepts and
|
||||
cache HTTP requests, so that for example you don't have to download pyodide
|
||||
again and again. This also enables the possibility of running tests in
|
||||
parallel on multiple cores.
|
||||
|
||||
The cache is stored using the `pytest-cache` plugin, which means that it
|
||||
survives across sessions.
|
||||
|
||||
If you want to temporarily disable the cache, the easiest thing is to use
|
||||
`--no-fake-server`, which bypasses it completely.
|
||||
|
||||
If you want to clear the cache, you can use the special option
|
||||
`--clear-http-cache`:
|
||||
|
||||
```
|
||||
$ pytest --clear-http-cache
|
||||
...
|
||||
-------------------- SmartRouter HTTP cache --------------------
|
||||
Requests found in the cache:
|
||||
https://raw.githubusercontent.com/pyscript/pyscript/main/README.md
|
||||
https://cdn.jsdelivr.net/pyodide/v0.21.3/full/repodata.json
|
||||
https://cdn.jsdelivr.net/pyodide/v0.21.3/full/pyodide.asm.js
|
||||
https://cdn.jsdelivr.net/pyodide/v0.21.3/full/micropip-0.1-py3-none-any.whl
|
||||
https://cdn.jsdelivr.net/pyodide/v0.21.3/full/pyodide.asm.data
|
||||
https://cdn.jsdelivr.net/pyodide/v0.21.3/full/pyodide.js
|
||||
https://cdn.jsdelivr.net/pyodide/v0.21.3/full/pyodide.asm.wasm
|
||||
https://cdn.jsdelivr.net/pyodide/v0.21.3/full/pyodide_py.tar
|
||||
https://cdn.jsdelivr.net/pyodide/v0.21.3/full/pyparsing-3.0.9-py3-none-any.whl
|
||||
https://cdn.jsdelivr.net/pyodide/v0.21.3/full/distutils.tar
|
||||
https://cdn.jsdelivr.net/pyodide/v0.21.3/full/packaging-21.3-py3-none-any.whl
|
||||
Cache cleared
|
||||
```
|
||||
|
||||
**NOTE**: this works only if you are inside `tests/integration`, or if you
|
||||
explicitly specify `tests/integration` from the command line. This is due to
|
||||
how `pytest` decides to search for and load the various `conftest.py`.
|
||||
12
docs/development/index.md
Normal file
12
docs/development/index.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Development
|
||||
|
||||
This section contains various topics related to pyscript development.
|
||||
|
||||
```{toctree}
|
||||
---
|
||||
maxdepth: 1
|
||||
---
|
||||
setting-up-environment
|
||||
deprecation-cycle
|
||||
developing
|
||||
```
|
||||
69
docs/development/setting-up-environment.md
Normal file
69
docs/development/setting-up-environment.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# Setting up your development environment
|
||||
|
||||
* Fork the repository - [quicklink](https://github.com/pyscript/pyscript/fork)
|
||||
|
||||
* Clone your fork of the project
|
||||
|
||||
```
|
||||
git clone https://github.com/<your username>/pyscript
|
||||
```
|
||||
|
||||
* Add the original project as your upstream (this will allow you to pull the latest changes)
|
||||
|
||||
```sh
|
||||
git remote add upstream git@github.com:pyscript/pyscript.git
|
||||
```
|
||||
|
||||
* cd into the `pyscriptjs` folder using the line below in your terminal (if your terminal is already in pyscript then use **cd pyscriptjs** instead)
|
||||
|
||||
```
|
||||
cd pyscript/pyscriptjs
|
||||
```
|
||||
|
||||
* Install the dependencies with the command below (you must have node >=16)
|
||||
|
||||
```
|
||||
make setup
|
||||
```
|
||||
**NOTE**: If `make setup` gives a node/npm version required error then go to [troubleshooting](https://github.com/pyscript/pyscript/blob/main/TROUBLESHOOTING.md)
|
||||
|
||||
* You can also run the examples locally by running the command below in your terminal
|
||||
|
||||
```
|
||||
make examples
|
||||
```
|
||||
|
||||
* Run ***npm run dev*** to build and run the dev server. This will also watch for changes and rebuild when a file is saved.
|
||||
|
||||
```
|
||||
npm run dev
|
||||
```
|
||||
**NOTE**: To access your local build paste `http://localhost:8080` into your browser
|
||||
|
||||
|
||||
Now that node and npm have both been updated `make setup` should work, and you can continue [setting up your local environment](setting-up-environment.md) without problems (hopefully).
|
||||
|
||||
|
||||
## Setting up and building the docs
|
||||
|
||||
To build the documentation locally first make sure you are in the `docs` directory.
|
||||
|
||||
You'll need `make` and `conda` installed in your machine. The rest of the environment should be automatically download and created for you once you use the command:
|
||||
|
||||
```
|
||||
make setup
|
||||
```
|
||||
|
||||
Use `conda activate $environment_name` to activate your environment.
|
||||
|
||||
To add new information to the documentation make sure you conform with PyScript's code of conduct and with the general principles of Diataxis. Don't worry about reading too much on it, just do your best to keep your contributions on the correct axis.
|
||||
|
||||
Write your documentation files using [Markedly Structured Text](https://myst-parser.readthedocs.io/en/latest/syntax/optional.html), which is very similar to vanilla Markdown but with some addons to create the documentation infrastructure.
|
||||
|
||||
Once done, initialize a server to check your work:
|
||||
|
||||
```
|
||||
make livehtml
|
||||
```
|
||||
|
||||
Visible here: [http://127.0.0.1:8000](http://127.0.0.1:8000)
|
||||
3
docs/error.md
Normal file
3
docs/error.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Not found!
|
||||
|
||||
The page that you looked for could not be found. If you think this is a mistake, please [open an issue](https://github.com/pyscript/pyscript/issues/new/)
|
||||
35
docs/guides/asyncio.md
Normal file
35
docs/guides/asyncio.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Using Async/Await and Asyncio
|
||||
|
||||
## {bdg-warning-line}`Deprecated` Implicit Coroutine Scheduling / Top-Level Await
|
||||
|
||||
In PyScript versions 2022.09.1 and earlier, \<py-script\> tags could be written in a way that enabled "Implicit Coroutine Scheduling." The keywords `await`, `async for` and `await with` were permitted to be used outside of `async` functions. Any \<py-script\> tags with these keywords at the top level were compiled into coroutines and automatically scheuled to run in the browser's event loop. This functionality was deprecated, and these keywords are no longer allowed outside of `async` functions.
|
||||
|
||||
To transition code from using top-level await statements to the currently-acceptable syntax, wrap the code into a coroutine using `async def()` and schedule it to run in the browser's event looping using `asyncio.ensure_future()` or `asyncio.create_task()`.
|
||||
|
||||
The following two pieces of code are functionally equivalent - the first only works in versions 2022.09.1, the latter is the currently acceptable equivalent.
|
||||
|
||||
```python
|
||||
# This version is deprecated, since
|
||||
# it uses 'await' outside an async function
|
||||
<py-script>
|
||||
import asyncio
|
||||
|
||||
for i in range(3):
|
||||
print(i)
|
||||
await asyncio.sleep(1)
|
||||
</py-script>
|
||||
```
|
||||
|
||||
```python
|
||||
# This version is acceptable
|
||||
<py-script>
|
||||
import asyncio
|
||||
|
||||
async def main():
|
||||
for i in range(3):
|
||||
print(i)
|
||||
await asyncio.sleep(1)
|
||||
|
||||
asyncio.ensure_future(main())
|
||||
</py-script>
|
||||
```
|
||||
213
docs/guides/http-requests.md
Normal file
213
docs/guides/http-requests.md
Normal file
@@ -0,0 +1,213 @@
|
||||
# How to make HTTP requests using `PyScript`, in pure Python
|
||||
|
||||
[Pyodide](https://pyodide.org), the runtime that underlies `PyScript`, does not have the `requests` module
|
||||
(or other similar modules) available by default, which are traditionally used to make HTTP requests in Python.
|
||||
However, it is possible to make HTTP requests in Pyodide using the modern `JavaScript` `fetch` API
|
||||
([docs](https://developer.mozilla.org/en-US/docs/Web/API/fetch)). This example shows how to make common HTTP request
|
||||
(GET, POST, PUT, DELETE) to an API, using only Python code! We will use asynchronous functions with
|
||||
async/await syntax, as concurrent code is preferred for HTTP requests.
|
||||
|
||||
The purpose of this guide is not to teach the basics of HTTP requests, but to show how to make them
|
||||
from `PyScript` using Python, since currently, the common tools such as `requests` and `httpx` are not available.
|
||||
|
||||
## Fetch
|
||||
|
||||
The `fetch` API is a modern way to make HTTP requests. It is available in all modern browsers, and in Pyodide.
|
||||
|
||||
Although there are two ways to use `fetch`:
|
||||
1) using `JavaScript` from `PyScript`
|
||||
2) using Pyodide's Python wrapper,
|
||||
`pyodide.http.pyfetch`
|
||||
|
||||
This example will only show how to use the Python wrapper. Still, the
|
||||
[fetch documentation](https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters) is a useful reference, as its
|
||||
parameters can be called from Python using the `pyfetch` wrapper.
|
||||
|
||||
## Pyodide.http, pyfetch, and FetchResponse
|
||||
|
||||
The [pyodide.http module](https://pyodide.org/en/stable/usage/api/python-api/http.html#module-pyodide.http) is a Python API
|
||||
for dealing with HTTP requests. It provides the `pyfetch` function as a wrapper for the `fetch` API,
|
||||
which returns a `FetchResponse` object whenever a request is made. Extra keyword arguments can be passed to `pyfetch`
|
||||
which will be passed to the `fetch` API.
|
||||
|
||||
The returned object `FetchResponse` has familiar methods and properties
|
||||
for dealing with the response, such as `json()` or `status`. See the
|
||||
[FetchResponse documentation](https://pyodide.org/en/stable/usage/api/python-api/http.html#pyodide.http.FetchResponse)
|
||||
for more information.
|
||||
|
||||
# Example
|
||||
We will make async HTTP requests to [JSONPlaceholder](https://jsonplaceholder.typicode.com/)'s fake API using `pyfetch`.
|
||||
First we write a helper function in pure Python that makes a request and returns the response. This function
|
||||
makes it easier to make specific types of requests with the most common parameters.
|
||||
|
||||
## Python convenience function
|
||||
|
||||
```python
|
||||
from pyodide.http import pyfetch, FetchResponse
|
||||
from typing import Optional, Any
|
||||
|
||||
async def request(url: str, method: str = "GET", body: Optional[str] = None,
|
||||
headers: Optional[dict[str, str]] = None, **fetch_kwargs: Any) -> FetchResponse:
|
||||
"""
|
||||
Async request function. Pass in Method and make sure to await!
|
||||
Parameters:
|
||||
url: str = URL to make request to
|
||||
method: str = {"GET", "POST", "PUT", "DELETE"} from `JavaScript` global fetch())
|
||||
body: str = body as json string. Example, body=json.dumps(my_dict)
|
||||
headers: dict[str, str] = header as dict, will be converted to string...
|
||||
Example, headers=json.dumps({"Content-Type": "application/json"})
|
||||
fetch_kwargs: Any = any other keyword arguments to pass to `pyfetch` (will be passed to `fetch`)
|
||||
Return:
|
||||
response: pyodide.http.FetchResponse = use with .status or await.json(), etc.
|
||||
"""
|
||||
kwargs = {"method": method, "mode": "cors"} # CORS: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
|
||||
if body and method not in ["GET", "HEAD"]:
|
||||
kwargs["body"] = body
|
||||
if headers:
|
||||
kwargs["headers"] = headers
|
||||
kwargs.update(fetch_kwargs)
|
||||
|
||||
response = await pyfetch(url, **kwargs)
|
||||
return response
|
||||
```
|
||||
This function is a wrapper for `pyfetch`, which is a wrapper for the `fetch` API. It is a coroutine function,
|
||||
so it must be awaited. It also has type hints, which are not required, but are useful for IDEs and other tools.
|
||||
The basic idea is that the `PyScript` will import and call this function, then await the response. Therefore,
|
||||
the script containing this function must be importable by `PyScript`.
|
||||
|
||||
For this example, we will name the file containing the Python code `request.py` and place it in the same directory as the file
|
||||
containing the html code, which is described below.
|
||||
|
||||
## `PyScript` HTML code
|
||||
|
||||
In this How-to, the HTML code is split into separate code blocks to enable context highlighting (coloring of the Python
|
||||
code inside the html code block), but in reality it is all in the same file. The first part is a bare bones `PyScript`
|
||||
html page, using the [community examples](https://github.com/pyscript/pyscript-collective/) set-up. The second part is
|
||||
the actual Python code for HTTP requests, which is wrapped in `<py-script>` tags, while the third block has the
|
||||
concluding html code.
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>GET, POST, PUT, DELETE example</title>
|
||||
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
files = ["/request.py"]
|
||||
</py-config>
|
||||
</head>
|
||||
|
||||
<body><p>
|
||||
Hello world request example! <br>
|
||||
Here is the output of your request:
|
||||
</p>
|
||||
<py-script>
|
||||
import asyncio
|
||||
import json
|
||||
from request import request # import our request function.
|
||||
|
||||
async def main():
|
||||
baseurl = "https://jsonplaceholder.typicode.com"
|
||||
|
||||
# GET
|
||||
headers = {"Content-type": "application/json"}
|
||||
response = await request(f"{baseurl}/posts/2", method="GET", headers=headers)
|
||||
print(f"GET request=> status:{response.status}, json:{await response.json()}")
|
||||
|
||||
# POST
|
||||
body = json.dumps({"title": "test_title", "body": "test body", "userId": 1})
|
||||
new_post = await request(f"{baseurl}/posts", body=body, method="POST", headers=headers)
|
||||
print(f"POST request=> status:{new_post.status}, json:{await new_post.json()}")
|
||||
|
||||
# PUT
|
||||
body = json.dumps({"id": 1, "title": "test_title", "body": "test body", "userId": 2})
|
||||
new_post = await request(f"{baseurl}/posts/1", body=body, method="PUT", headers=headers)
|
||||
print(f"PUT request=> status:{new_post.status}, json:{await new_post.json()}")
|
||||
|
||||
# DELETE
|
||||
new_post = await request(f"{baseurl}/posts/1", method="DELETE", headers=headers)
|
||||
print(f"DELETE request=> status:{new_post.status}, json:{await new_post.json()}")
|
||||
|
||||
asyncio.ensure_future(main())
|
||||
</py-script>
|
||||
|
||||
<div>
|
||||
<p>
|
||||
You can also use other methods. See fetch documentation: <br>
|
||||
https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
See pyodide documentation for what to do with a FetchResponse object: <br>
|
||||
https://pyodide.org/en/stable/usage/api/python-api.html#pyodide.http.FetchResponse
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Explanation
|
||||
### `py-config` tag for importing our Python code
|
||||
The very first thing to notice is the `py-config` tag. This tag is used to import Python files into the `PyScript`.
|
||||
In this case, we are importing the `request.py` file, which contains the `request` function we wrote above.
|
||||
|
||||
### `py-script` tag for making async HTTP requests.
|
||||
Next, the `py-script` tag contains the actual Python code where we import `asyncio` and `json`,
|
||||
which are required or helpful for the `request` function.
|
||||
The `# GET`, `# POST`, `# PUT`, `# DELETE` blocks show examples of how to use the `request` function to make basic
|
||||
HTTP requests. The `await` keyword is required not only for the `request` function, but also for certain methods of the
|
||||
`FetchResponse` object, such as `json()`, meaning that the code is asynchronous and slower requests will not block the
|
||||
faster ones.
|
||||
|
||||
### HTTP Requests
|
||||
HTTP requests are a very common way to communicate with a server. They are used for everything from getting data from
|
||||
a database, to sending emails, to authorization, and more. Due to safety concerns, files loaded from the
|
||||
local file system are not accessible by `PyScript`. Therefore, the proper way to load data into `PyScript` is also
|
||||
through HTTP requests.
|
||||
|
||||
In our example, we show how to pass in a request `body`, `headers`, and specify the request `method`, in order to make
|
||||
`GET`, `POST`, `PUT`, and `DELETE` requests, although methods such as `PATCH` are also available. Additional
|
||||
parameters for the `fetch` API are also available, which can be specified as keyword arguments passed to our helper
|
||||
function or to `pyfetch`. See the
|
||||
[fetch documentation](https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters) for more information.
|
||||
HTTP requests are defined by standards-setting bodies in [RFC 1945](https://www.rfc-editor.org/info/rfc1945) and
|
||||
[RFC 9110](https://www.rfc-editor.org/info/rfc9110).
|
||||
|
||||
# Conclusion
|
||||
This tutorial demonstrates how to make HTTP requests using `pyfetch` and the `FetchResponse` objects. Importing Python
|
||||
code/files into the `PyScript` using the `py-config` tag is also covered.
|
||||
|
||||
Although a simple example, the principals here can be used to create complex web applications inside of `PyScript`,
|
||||
or load data into `PyScript` for use by an application, all served as a static HTML page, which is pretty amazing!
|
||||
|
||||
|
||||
# API Quick Reference
|
||||
## pyodide.http.pyfetch
|
||||
### Usage
|
||||
```python
|
||||
await pyodide.http.pyfetch(url: str, **kwargs: Any) -> FetchResponse
|
||||
```
|
||||
Use `pyfetch` to make HTTP requests in `PyScript`. This is a wrapper around the `fetch` API. Returns a `FetchResponse`.
|
||||
|
||||
- [`pyfetch` Docs.](https://pyodide.org/en/stable/usage/api/python-api/http.html#pyodide.http.pyfetch)
|
||||
|
||||
## pyodide.http.FetchResponse
|
||||
### Usage
|
||||
```python
|
||||
response: pyodide.http.FetchResponse = await <pyfetch call>
|
||||
status = response.status
|
||||
json = await response.json()
|
||||
```
|
||||
Class for handling HTTP responses. This is a wrapper around the `JavaScript` fetch `Response`. Contains common (async)
|
||||
methods and properties for handling HTTP responses, such as `json()`, `url`, `status`, `headers`, etc.
|
||||
|
||||
- [`FetchResponse` Docs.](https://pyodide.org/en/stable/usage/api/python-api/http.html#pyodide.http.FetchResponse)
|
||||
@@ -1,4 +1,4 @@
|
||||
# How-to guides
|
||||
# Guides
|
||||
|
||||
Welcome to the how-to documentation section for PyScript. If you've already
|
||||
gained some experience with PyScript before and just need practical guides
|
||||
@@ -14,5 +14,7 @@ maxdepth: 2
|
||||
glob:
|
||||
caption: 'Contents:'
|
||||
---
|
||||
*
|
||||
passing-objects
|
||||
http-requests
|
||||
asyncio
|
||||
```
|
||||
295
docs/guides/passing-objects.md
Normal file
295
docs/guides/passing-objects.md
Normal file
@@ -0,0 +1,295 @@
|
||||
# How to Pass Objects from PyScript to Javascript (and Vice Versa)
|
||||
|
||||
[Pyodide](https://pyodide.org), the runtime that underlies PyScript, does a lot of work under the hood to translate objects between Python and JavaScript. This allows code in one language to access objects defined in the other.
|
||||
|
||||
This guide discusses how to pass objects between JavaScript and Python within PyScript. For more details on how Pyodide handles translating and proxying objects between the two languages, see the [Pyodide Type Translations Page](https://pyodide.org/en/stable/usage/type-conversions.html).
|
||||
|
||||
For our purposes, an 'object' is anything that can be bound to a variable (a number, string, object, [function](https://developer.mozilla.org/en-US/docs/Glossary/First-class_Function), etc).
|
||||
|
||||
## JavaScript to PyScript
|
||||
|
||||
We can use the syntax `from js import ...` to import JavaScript objects directly into PyScript. Simple JavaScript objects are converted to equivalent Python types; these are called [implicit conversions](https://pyodide.org/en/stable/usage/type-conversions.html#implicit-conversions). More complicated objects are wrapped in [JSProxy](https://pyodide.org/en/stable/usage/type-conversions.html) objects to make them behave like Python objects.
|
||||
|
||||
`import js` and `from js import ...` [in Pyodide](https://pyodide.org/en/stable/usage/type-conversions.html#type-translations-using-js-obj-from-py) get objects from the [JavaScript globalThis scope](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis), so keep the[ rules of JavaScript variable scoping](https://www.freecodecamp.org/news/var-let-and-const-whats-the-difference/) in mind.
|
||||
|
||||
```html
|
||||
<script>
|
||||
name = "Guido" //A JS variable
|
||||
|
||||
// Define a JS Function
|
||||
function addTwoNumbers(x, y){
|
||||
return x + y;
|
||||
}
|
||||
</script>
|
||||
```
|
||||
```python
|
||||
<py-script>
|
||||
# Import and use JS function and variable into Python
|
||||
from js import name, addTwoNumbers
|
||||
|
||||
print(f"Hello {name}")
|
||||
print("Adding 1 and 2 in Javascript: " + str(addTwoNumbers(1, 2)))
|
||||
</py-script>
|
||||
```
|
||||
|
||||
## PyScript to JavaScript
|
||||
|
||||
### Using Pyodide's globals access
|
||||
|
||||
The [PyScript JavaScript module](../reference/modules/pyscript.md) exposes its underlying Pyodide runtime as `PyScript.runtime`, and maintains a reference to the [globals()](https://docs.python.org/3/library/functions.html#globals) dictionary of the Python namespace. Thus, any global variables in python are accessible in JavaScript at `PyScript.runtime.globals.get('my_variable_name')`
|
||||
|
||||
```html
|
||||
<body>
|
||||
<py-script>x = 42</py-script>
|
||||
|
||||
<button onclick="showX()">Click Me to Get 'x' from Python</button>
|
||||
<script>
|
||||
function showX(){
|
||||
console.log(`In Python right now, x = ${pyscript.runtime.globals.get('x')}`)
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
Since [everything is an object](https://docs.python.org/3/reference/datamodel.html) in Python, this applies not only to user created variables, but also to classes, functions, built-ins, etc. If we want, we can even apply Python functions to JavaScript data and variables:
|
||||
|
||||
```html
|
||||
<body>
|
||||
<!-- Click this button to log 'Apple', 'Banana', 'Candy', 'Donut' by sorting in Python-->
|
||||
<button onclick="sortInPython(['Candy', 'Donut', 'Apple', 'Banana'])">Sort In Python And Log</button>
|
||||
<script>
|
||||
function sortInPython(data){
|
||||
js_sorted = pyscript.runtime.globals.get('sorted') //grab python's 'sorted' function
|
||||
const sorted_data = js_sorted(data) //apply the function to the 'data' argument
|
||||
for (const item of sorted_data){
|
||||
console.log(item)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
### Using JavaScript's eval()
|
||||
|
||||
There may be some situations where it isn't possible or ideal to use `PyScript.runtime.globals.get()` to retrieve a variable from the Pyodide global dictionary. For example, some JavaScript frameworks may take a function/Callable as an html attribute in a context where code execution isn't allowed (i.e. `get()` fails). In these cases, you can create JavaScript proxies of Python objects more or less "manually" using [JavaScript's eval() function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval), which executes a string as code much like [Python's eval()](https://docs.python.org/3/library/functions.html#eval).
|
||||
|
||||
First, we create a JS function `createObject` which takes an object and a string, then uses `eval()` to create a variable named after the string and bind it to that object. By calling this function from PyScript (where we have access to the Pyodide global namespace), we can bind JavaScript variables to Python objects without having direct access to that global namespace.
|
||||
|
||||
Include the following script tag anywhere in your html document:
|
||||
|
||||
```html
|
||||
<script>
|
||||
function createObject(object, variableName){
|
||||
//Bind a variable whose name is the string variableName
|
||||
// to the object called 'object'
|
||||
let execString = variableName + " = object"
|
||||
console.log("Running '" + execString + "'");
|
||||
eval(execString)
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
This function takes a Python Object and creates a variable pointing to it in the JavaScript global scope.
|
||||
|
||||
### Exporting all Global Python Objects
|
||||
|
||||
We can use our new `createObject` function to "export" the entire Python global object dictionary as a JavaScript object:
|
||||
|
||||
```python
|
||||
<py-script>
|
||||
from js import createObject
|
||||
from pyodide.ffi import create_proxy
|
||||
createObject(create_proxy(globals()), "pyodideGlobals")
|
||||
</py-script>
|
||||
```
|
||||
This will make all Python global variables available in JavaScript with `pyodideGlobals.get('my_variable_name')`.
|
||||
|
||||
(Since PyScript tags evaluate _after_ all JavaScript on the page, we can't just dump a `console.log(...)` into a `<script>` tag, since that tag will evaluate before any PyScript has a chance to. We need to delay accessing the Python variable in JavaScript until after the Python code has a chance to run. The following example uses a button with `id="do-math"` to achieve this, but any method would be valid.)
|
||||
|
||||
```python
|
||||
<py-script>
|
||||
# create some Python objects:
|
||||
symbols = {'pi': 3.1415926, 'e': 2.7182818}
|
||||
|
||||
def rough_exponential(x):
|
||||
return symbols['e']**x
|
||||
|
||||
class Circle():
|
||||
def __init__(self, radius):
|
||||
self.radius = radius
|
||||
|
||||
@property
|
||||
def area(self):
|
||||
return symbols['pi'] * self.radius**2
|
||||
</py-script>
|
||||
```
|
||||
|
||||
```html
|
||||
<input type="button" value="Log Python Variables" id="do-math">
|
||||
<script>
|
||||
document.getElementById("do-math").addEventListener("click", () => {
|
||||
const exp = pyodideGlobals.get('rough_exponential');
|
||||
console.log(`e squared is about ${exp(2)}`);
|
||||
const c = pyodideGlobals.get('Circle')(4);
|
||||
console.log(`The area of c is ${c.area}`);
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
#### Full example
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>Exporting all Global Python Objects</title>
|
||||
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<input type="button" value="Log Python Variables" id="do-math">
|
||||
<py-script>
|
||||
from js import createObject
|
||||
from pyodide.ffi import create_proxy
|
||||
|
||||
createObject(create_proxy(globals()), "pyodideGlobals")
|
||||
|
||||
# create some Python objects:
|
||||
symbols = {'pi': 3.1415926, 'e': 2.7182818}
|
||||
|
||||
def rough_exponential(x):
|
||||
return symbols['e']**x
|
||||
|
||||
class Circle():
|
||||
def __init__(self, radius):
|
||||
self.radius = radius
|
||||
|
||||
@property
|
||||
def area(self):
|
||||
return symbols['pi'] * self.radius**2
|
||||
</py-script>
|
||||
<script>
|
||||
function createObject(object, variableName){
|
||||
//Bind a variable whose name is the string variableName
|
||||
// to the object called 'object'
|
||||
let execString = variableName + " = object"
|
||||
console.log("Running '" + execString + "'");
|
||||
eval(execString)
|
||||
}
|
||||
|
||||
document.getElementById("do-math").addEventListener("click", () => {
|
||||
const exp = pyodideGlobals.get('rough_exponential');
|
||||
console.log(`e squared is about ${exp(2)}`);
|
||||
const c = pyodideGlobals.get('Circle')(4);
|
||||
console.log(`The area of c is ${c.area}`);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
### Exporting Individual Python Objects
|
||||
|
||||
We can also export individual Python objects to the JavaScript global scope if we wish.
|
||||
|
||||
(As above, the following example uses a button to delay the execution of the `<script>` until after the PyScript has run.)
|
||||
|
||||
```python
|
||||
<py-script>
|
||||
import js
|
||||
from pyodide.ffi import create_proxy
|
||||
|
||||
# Create 3 python objects
|
||||
language = "Python 3"
|
||||
animals = ['dog', 'cat', 'bird']
|
||||
multiply3 = lambda a, b, c: a * b * c
|
||||
|
||||
# js object can be named the same as Python objects...
|
||||
js.createObject(language, "language")
|
||||
|
||||
# ...but don't have to be
|
||||
js.createObject(create_proxy(animals), "animals_from_py")
|
||||
|
||||
# functions are objects too, in both Python and Javascript
|
||||
js.createObject(create_proxy(multiply3), "multiply")
|
||||
</py-script>
|
||||
```
|
||||
```html
|
||||
<input type="button" value="Log Python Variables" id="log-python-variables">
|
||||
<script>
|
||||
document.getElementById("log-python-variables").addEventListener("click", () => {
|
||||
console.log(`Nice job using ${language}`);
|
||||
for (const animal of animals_from_py){
|
||||
console.log(`Do you like ${animal}s? `);
|
||||
}
|
||||
console.log(`2 times 3 times 4 is ${multiply(2,3,4)}`);
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
#### Full example
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>Exporting Individual Python Objects</title>
|
||||
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<py-script>
|
||||
import js
|
||||
from pyodide.ffi import create_proxy
|
||||
|
||||
# Create 3 python objects
|
||||
language = "Python 3"
|
||||
animals = ['dog', 'cat', 'bird']
|
||||
multiply3 = lambda a, b, c: a * b * c
|
||||
|
||||
# js object can be named the same as Python objects...
|
||||
js.createObject(language, "language")
|
||||
|
||||
# ...but don't have to be
|
||||
js.createObject(create_proxy(animals), "animals_from_py")
|
||||
|
||||
# functions are objects too, in both Python and Javascript
|
||||
js.createObject(create_proxy(multiply3), "multiply")
|
||||
</py-script>
|
||||
|
||||
<input type="button" value="Log Python Variables" id="log-python-variables">
|
||||
<script>
|
||||
function createObject(object, variableName){
|
||||
//Bind a variable whose name is the string variableName
|
||||
// to the object called 'object'
|
||||
let execString = variableName + " = object"
|
||||
console.log("Running '" + execString + "'");
|
||||
eval(execString)
|
||||
}
|
||||
|
||||
document.getElementById("log-python-variables").addEventListener("click", () => {
|
||||
console.log(`Nice job using ${language}`);
|
||||
for (const animal of animals_from_py){
|
||||
console.log(`Do you like ${animal}s? `);
|
||||
}
|
||||
console.log(`2 times 3 times 4 is ${multiply(2,3,4)}`);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
BIN
docs/img/diataxis.png
Normal file
BIN
docs/img/diataxis.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 108 KiB |
@@ -1,10 +1,5 @@
|
||||
# PyScript
|
||||
|
||||
```{warning}
|
||||
Please note, this documentation is just a placeholder and **should not be used
|
||||
in reference material**. Thank you!
|
||||
```
|
||||
|
||||
Welcome to the PyScript documentation!
|
||||
|
||||
PyScript provides a way for you to run Python code directly in your browser, giving
|
||||
@@ -22,9 +17,15 @@ Just getting started with PyScript?
|
||||
|
||||
Check out our [getting started guide](tutorials/getting-started.md)!
|
||||
:::
|
||||
:::{grid-item-card} [How-to guides](howtos/index.md)
|
||||
:::{grid-item-card} [Guides](guides/index.md)
|
||||
|
||||
**Coming soon!**
|
||||
You already know the basics and want to learn specifics!
|
||||
|
||||
[Passing Objects between JavaScript and Python](guides/passing-objects.md)
|
||||
|
||||
[Making async HTTP requests in pure Python](guides/http-requests.md)
|
||||
|
||||
[Async/Await and Asyncio](guides/asyncio.md)
|
||||
|
||||
:::
|
||||
:::{grid-item-card} [Concepts](concepts/index.md)
|
||||
@@ -36,6 +37,8 @@ Check out our [getting started guide](tutorials/getting-started.md)!
|
||||
|
||||
[Frequently asked questions](reference/faq.md)
|
||||
|
||||
[The PyScript JS Module](reference/modules/pyscript.md)
|
||||
|
||||
:::{toctree}
|
||||
:maxdepth: 1
|
||||
|
||||
@@ -48,7 +51,7 @@ maxdepth: 1
|
||||
hidden:
|
||||
---
|
||||
tutorials/index
|
||||
howtos/index
|
||||
guides/index
|
||||
concepts/index
|
||||
reference/index
|
||||
```
|
||||
|
||||
8
docs/reference/API/__version__.md
Normal file
8
docs/reference/API/__version__.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# `__version__`
|
||||
|
||||
`PyScript.__version__` is a `str` representing the current version of PyScript in a human-readable form. For a structured version more suitable to comparisons, and for details of what each part of the version number represents, see [`version_info`](version_info.md)
|
||||
|
||||
```shell
|
||||
>>> pyscript.__version__
|
||||
'2023.02.1.dev'
|
||||
```
|
||||
87
docs/reference/API/display.md
Normal file
87
docs/reference/API/display.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# `display(*values, target=None, append=True)`
|
||||
|
||||
## Parameters
|
||||
|
||||
`*values` - the objects to be displayed. String objects are output as-written. For non-string objects, the default content to display is the the object's `repr()`. Objects may implement the following methods to indicate that they should be displayed as a different MIME type. MIME types with a * indicate that the content will be wrapped in the appropriate html tags and attributes before output:
|
||||
|
||||
| Method | Inferred MIME type |
|
||||
|---------------------|------------------------|
|
||||
| `__repr__` | text/plain |
|
||||
| `_repr_html_` | text/html |
|
||||
| `_repr_svg_` | image/svg+xml |
|
||||
| `_repr_png_` | image/png* |
|
||||
| `_repr_pdf_` | application/pdf |
|
||||
| `_repr_jpeg_` | image/jpeg* |
|
||||
| `_repr_json_` | application/json |
|
||||
| `_repr_javascript_` | application/javascript*|
|
||||
| `savefig` | image/png |
|
||||
| | |
|
||||
|
||||
`target` - Element's ID. The default value for `target` is the current `py-script` tag ID, it's possible to specify different IDs for this parameter
|
||||
|
||||
`append` - `boolean` if the output is going to be appended or not to the `target`ed element. It creates a `<div>` tag if `True` and a `<py-script>` tag with a random ID if `False`. The default value for `append` is `True`.
|
||||
|
||||
### Description
|
||||
|
||||
Display is the default function to display objects on the screen. Functions like the Python `print()` or JavaScript `console.log()` are now defaulted to only appear on the terminal.
|
||||
|
||||
Display will throw an exception if the target is not clear. E.g. the following code is invalid:
|
||||
|
||||
```html
|
||||
<py-script>
|
||||
def display_hello():
|
||||
# this fails because we don't have any implicit target
|
||||
# from event handlers
|
||||
display('hello')
|
||||
</py-script>
|
||||
<button id="my-button" py-onClick="display_hello()">Click me</button>
|
||||
```
|
||||
|
||||
Because it's considered unclear if the `hello` string should be displayed underneath the `<py-script>` tag or the `<button>` tag.
|
||||
|
||||
To write compliant code, make sure to specify the target using the `target` parameter, for example:
|
||||
|
||||
```html
|
||||
<py-script>
|
||||
def display_hello():
|
||||
# this fails because we don't have any implicit target
|
||||
# from event handlers
|
||||
display('hello', target="helloDiv")
|
||||
</py-script>
|
||||
<div id="helloDiv"></div>
|
||||
<button id="my-button" py-onClick="display_hello()">Click me</button>
|
||||
```
|
||||
|
||||
#### Using matplotlib with display
|
||||
|
||||
`matplotlib` has two ways of plotting things as mentioned [here](https://matplotlib.org/matplotblog/posts/pyplot-vs-object-oriented-interface/)
|
||||
|
||||
- In case of using the `pyplot` interface, the graph can be shown using `display(plt)`.
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
# Data for plotting
|
||||
t = np.arange(0.0, 2.0, 0.01)
|
||||
s = 1 + np.sin(2 * np.pi * t)
|
||||
plt.plot(t,s)
|
||||
|
||||
display(plt)
|
||||
```
|
||||
|
||||
- In case of using the `object oriented` interface, the graph can be shown using `display(fig)` or `display(plt)` both.
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
# Data for plotting
|
||||
t = np.arange(0.0, 2.0, 0.01)
|
||||
s = 1 + np.sin(2 * np.pi * t)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(t, s)
|
||||
|
||||
display(fig) # but even display(plt) would have worked!
|
||||
```
|
||||
309
docs/reference/API/element.md
Normal file
309
docs/reference/API/element.md
Normal file
@@ -0,0 +1,309 @@
|
||||
# `Element`
|
||||
|
||||
The `Element` API is a helpful way to create and manipulate elements in the DOM. It is a wrapper around the native DOM API, and is designed to be as intuitive as possible.
|
||||
|
||||
## Methods and Properties
|
||||
|
||||
| Property | Description |
|
||||
|----------|-----------------------------------------|
|
||||
| `element` | Returns the element with the given ID. |
|
||||
| `id` | Returns the element's ID. |
|
||||
| `value` | Returns the element's value. |
|
||||
| `innerHtml` | Returns the element's inner HTML. |
|
||||
|
||||
|
||||
|
||||
| Method | Description |
|
||||
|----------------------|--------------------------------------------------------------|
|
||||
| `write` | Writes `value` to element and handles various mime types. `append` defaults to `False`, if set to true, it will create a child element. |
|
||||
| `clear` | Clears the element's value or content. |
|
||||
| `select` | Select element from `query` which uses [Document.querySelector()](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector). |
|
||||
| `clone` | Clones the with `new_id` if provided and `to` element if provided. |
|
||||
| `remove_class` | Removes one or more class name from the element. |
|
||||
| `add_class` | Adds one or more class name to the element. |
|
||||
|
||||
## Element.element
|
||||
|
||||
| Parameter | Default | Type |
|
||||
|-----------|---------|------|
|
||||
| | | |
|
||||
|
||||
The `element` property returns the DOM element with the given ID.
|
||||
|
||||
```html
|
||||
from pyscript import Element
|
||||
|
||||
my_div = Element('my-div')
|
||||
print(my_div.element)
|
||||
```
|
||||
|
||||
## Element.id
|
||||
|
||||
| Parameter | Default | Type |
|
||||
|-----------|---------|------|
|
||||
| | | |
|
||||
|
||||
Return the element's ID.
|
||||
|
||||
```html
|
||||
|
||||
<div id="my-div"></div>
|
||||
|
||||
<py-script>
|
||||
from pyscript import Element
|
||||
|
||||
my_div = Element('my-div')
|
||||
print(my_div.id) # prints 'my-div'
|
||||
</py-script>
|
||||
```
|
||||
|
||||
## Element.value
|
||||
|
||||
| Parameter | Default | Type |
|
||||
|-----------|---------|------|
|
||||
| | | |
|
||||
|
||||
Return the element's value.
|
||||
|
||||
```html
|
||||
<input id="my-input" value="hello world"></input>
|
||||
|
||||
<py-script>
|
||||
from pyscript import Element
|
||||
|
||||
my_input = Element('my-input')
|
||||
print(my_input.value) # prints 'hello world'
|
||||
</py-script>
|
||||
```
|
||||
|
||||
## Element.innerHtml
|
||||
|
||||
| Parameter | Default | Type |
|
||||
|-----------|---------|------|
|
||||
| | | |
|
||||
|
||||
Return the element's inner HTML.
|
||||
|
||||
```html
|
||||
<div id="my-innerHtml">
|
||||
<b>hello world</b>
|
||||
</div>
|
||||
|
||||
<py-script>
|
||||
from pyscript import Element
|
||||
|
||||
my_innerHtml = Element('my-innerHtml')
|
||||
print(my_innerHtml.innerHtml) # prints <b> hello world </b>
|
||||
</py-script>
|
||||
```
|
||||
|
||||
## Element.write
|
||||
|
||||
| Parameter | Default | Type |
|
||||
|-------------|---------|-----------------------------|
|
||||
| `value` | | `str` or `__mime_type__` |
|
||||
| `append` | False | `bool` |
|
||||
|
||||
Writes `value` to element and handles various mime types. This method also contains a `append` parameter, which defaults to `False`.
|
||||
|
||||
Currently, these are the MIME types that are supported when rendering content using this method
|
||||
|
||||
| Method | Inferred MIME type |
|
||||
|---------------------|------------------------|
|
||||
| `__repr__` | text/plain |
|
||||
| `_repr_html_` | text/html |
|
||||
| `_repr_svg_` | image/svg+xml |
|
||||
| `_repr_png_` | image/png* |
|
||||
| `_repr_pdf_` | application/pdf |
|
||||
| `_repr_jpeg_` | image/jpeg* |
|
||||
| `_repr_json_` | application/json |
|
||||
| `_repr_javascript_` | application/javascript*|
|
||||
| `savefig` | image/png |
|
||||
|
||||
```html
|
||||
<div id="foo"></div>
|
||||
|
||||
<py-script>
|
||||
from pyscript import Element
|
||||
el = Element("foo")
|
||||
el.write("Hello!")
|
||||
el.write("World!") # will replace the previous content
|
||||
</py-script>
|
||||
```
|
||||
|
||||
If we set `append` to `True`, it will create a child element using a `div`.
|
||||
|
||||
```html
|
||||
<div id="foo"></div>
|
||||
|
||||
<py-script>
|
||||
from pyscript import Element
|
||||
el = Element("foo")
|
||||
el.write("Hello!", append=True)
|
||||
|
||||
# This will create a child div with the id "foo-1"
|
||||
el.write("World!", append=True)
|
||||
</py-script>
|
||||
```
|
||||
|
||||
## Element.clear
|
||||
|
||||
| Parameter | Default | Type |
|
||||
|-----------|---------|------|
|
||||
| | | |
|
||||
|
||||
Clears the element's value or content. For example, we can clear the value of an input element.
|
||||
|
||||
```html
|
||||
<input id="foo" value="Hello!"></input>
|
||||
|
||||
<py-script>
|
||||
from pyscript import Element
|
||||
el = Element("foo")
|
||||
el.clear() # Removes value from input
|
||||
</py-script>
|
||||
```
|
||||
|
||||
Or we can clear the content of a div element.
|
||||
|
||||
```html
|
||||
<div id="foo">Hello!</div>
|
||||
|
||||
<py-script>
|
||||
from pyscript import Element
|
||||
el = Element("foo")
|
||||
el.clear() # Removes Hello from div content
|
||||
</py-script>
|
||||
```
|
||||
|
||||
## Element.select
|
||||
|
||||
Select element from `query`, it will look into the main Element if `from_content` is `True`. This method is a wrapper of [Document.querySelector()](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector).
|
||||
|
||||
```html
|
||||
<div id="foo">
|
||||
<div id="bar"></div>
|
||||
</div>
|
||||
|
||||
<py-script>
|
||||
from pyscript import Element
|
||||
el = Element("foo")
|
||||
bar = el.select("#bar")
|
||||
print(bar.id) # prints 'bar'
|
||||
</py-script>
|
||||
```
|
||||
|
||||
## Element.clone
|
||||
|
||||
| Parameter | Default | Type |
|
||||
|-------------|---------|-----------|
|
||||
| `new_id` | None | `str` |
|
||||
| `to` | None | `Element` |
|
||||
|
||||
Clones the element to a new element. You can provide `new_id` to set a different id to the cloned element. You can also use a `to` element to append the cloned element to.
|
||||
|
||||
```html
|
||||
<div id="foo">
|
||||
HI!
|
||||
</div>
|
||||
|
||||
<py-script>
|
||||
from pyscript import Element
|
||||
|
||||
el = Element("foo")
|
||||
# Creates two divs with the id "foo" and content "HI!"
|
||||
el.clone()
|
||||
</py-script>
|
||||
```
|
||||
|
||||
It's always a good idea to pass a new id to the element you are cloning to avoid confusion if you need to reference the element by id again.
|
||||
|
||||
```html
|
||||
<div id="foo">Hello!</div>
|
||||
|
||||
<py-script>
|
||||
from pyscript import Element
|
||||
el = Element("foo")
|
||||
# Clones foo and its contents, but uses the id 'bar'
|
||||
el.clone(new_id="bar")
|
||||
</py-script>
|
||||
```
|
||||
|
||||
You can also clone an element into another element.
|
||||
|
||||
```html
|
||||
<div id="bond">
|
||||
Bond
|
||||
</div>
|
||||
<div id="james">
|
||||
James
|
||||
</div>
|
||||
<py-script>
|
||||
from pyscript import Element
|
||||
|
||||
bond_div = Element("bond")
|
||||
james_div = Element("james")
|
||||
|
||||
bond_div.clone(new_id="bond-2", to=james_div)
|
||||
</py-script>
|
||||
```
|
||||
|
||||
## Element.remove_class
|
||||
|
||||
| Parameter | Default | Type |
|
||||
|-------------|---------|-----------------------|
|
||||
| `classname` | None | `str` or `List[str]` |
|
||||
|
||||
Removes one or more class names from the element.
|
||||
|
||||
```html
|
||||
<div id="foo" class="bar baz"></div>
|
||||
<py-script>
|
||||
from pyscript import Element
|
||||
|
||||
el = Element("foo")
|
||||
el.remove_class("bar")
|
||||
</py-script>
|
||||
```
|
||||
|
||||
You can also remove multiple classes by passing a list of strings.
|
||||
|
||||
```html
|
||||
<div id="foo" class="bar baz"></div>
|
||||
<py-script>
|
||||
from pyscript import Element
|
||||
|
||||
el = Element("foo")
|
||||
el.remove_class(["bar", "baz"]) # Remove all classes from element
|
||||
</py-script>
|
||||
```
|
||||
|
||||
## Element.add_class
|
||||
|
||||
| Parameter | Default | Type |
|
||||
|-------------|---------|-----------------------|
|
||||
| `classname` | None | `str` or `List[str]` |
|
||||
|
||||
Adds one or more class names to the element.
|
||||
|
||||
```html
|
||||
<style> .red { color: red; } </style>
|
||||
<div id="foo">Hi!</div>
|
||||
<py-script>
|
||||
from pyscript import Element
|
||||
el = Element("foo")
|
||||
el.add_class("red")
|
||||
</py-script>
|
||||
```
|
||||
|
||||
You can also add multiple classes at once by passing a list of strings.
|
||||
|
||||
```html
|
||||
<style> .red { color: red; } .bold { font-weight: bold; } </style>
|
||||
<div id="foo">Hi!</div>
|
||||
<py-script>
|
||||
from pyscript import Element
|
||||
el = Element("foo")
|
||||
el.add_class(["red", "bold"])
|
||||
</py-script>
|
||||
```
|
||||
16
docs/reference/API/version_info.md
Normal file
16
docs/reference/API/version_info.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# `version_info`
|
||||
|
||||
`PyScript.version_info` is a `namedtuple` representing the current version of PyScript. It can be used to compare whether current version precedes or follows a desired version. For a human-readable version of the same info, see [`__version__`](__version__.md)
|
||||
|
||||
```sh
|
||||
>>> pyscript.version_info
|
||||
version_info(year=2023, month=2, minor=1, releaselevel='dev')
|
||||
```
|
||||
|
||||
## Version Fields
|
||||
| **parameter** | **CalVer equivalent field** | **example value** | **description** |
|
||||
|-----------------|-----------------------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `year` | Full year (YYYY) | 2023 | The year of the release; when printed or represented as a string, always written with 4 digits |
|
||||
| `month` | Short Month (MM) | 2 | The month of the release; when printed or represented as a string, written with 1 or 2 digits as necessary |
|
||||
| `minor` | | 1 | The incremental number of the release for this month; when printed or represented as a string, written with 1 or two digits as necessary |
|
||||
| `releaselevel` | | 'dev' | A string representing the qualifications of this build |
|
||||
487
docs/reference/elements/py-config.md
Normal file
487
docs/reference/elements/py-config.md
Normal file
@@ -0,0 +1,487 @@
|
||||
# <py-config>
|
||||
|
||||
Use the `<py-config>` tag to set and configure general metadata along with declaring dependencies for your PyScript application. The configuration has to be set in either [TOML](https://toml.io/)(default) or [JSON](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/JSON) format.
|
||||
|
||||
If you are unfamiliar with TOML, consider [reading about it](https://learnxinyminutes.com/docs/toml/) or if you are unfamiliar with JSON, consider reading [freecodecamp's JSON for beginners](https://www.freecodecamp.org/news/what-is-json-a-json-file-example/) guide for more information.
|
||||
|
||||
The `<py-config>` element should be placed within the `<body>` element.
|
||||
|
||||
## Attributes
|
||||
|
||||
| attribute | type | default | description |
|
||||
|-----------|--------|---------|---------------------------------------------------------------------------------------------------------|
|
||||
| **type** | string | "toml" | Syntax type of the `<py-config>`. Value can be `json` or `toml`. Default: "toml" if type is unspecifed. |
|
||||
| **src** | url | | Source url to an external configuration file. |
|
||||
|
||||
## Examples
|
||||
|
||||
### Defining an inline config
|
||||
|
||||
- `<py-config>` using TOML (default)
|
||||
|
||||
```{note}
|
||||
Reminder: when using TOML, any Arrays of Tables defined with double-brackets (like `[[runtimes]]` and `[[fetch]]` must come after individual keys (like `plugins = ...` and `packages=...`)
|
||||
```
|
||||
|
||||
```html
|
||||
<py-config>
|
||||
[splashscreen]
|
||||
autoclose = true
|
||||
|
||||
[[runtimes]]
|
||||
src = "https://cdn.jsdelivr.net/pyodide/v0.21.2/full/pyodide.js"
|
||||
name = "pyodide-0.21.2"
|
||||
lang = "python"
|
||||
</py-config>
|
||||
```
|
||||
|
||||
- `<py-config>` using JSON via `type` attribute
|
||||
|
||||
```html
|
||||
<py-config type="json">
|
||||
{
|
||||
"splashscreen": {
|
||||
"autoclose": true
|
||||
},
|
||||
"runtimes": [{
|
||||
"src": "https://cdn.jsdelivr.net/pyodide/v0.21.2/full/pyodide.js",
|
||||
"name": "pyodide-0.21.2",
|
||||
"lang": "python"
|
||||
}]
|
||||
}
|
||||
</py-config>
|
||||
```
|
||||
|
||||
### Defining a file based config
|
||||
|
||||
- Use of the `src` attribute
|
||||
|
||||
```html
|
||||
<py-config src="./custom.toml"></py-config>
|
||||
```
|
||||
where `custom.toml` contains
|
||||
|
||||
```toml
|
||||
[splashscreen]
|
||||
autoclose = true
|
||||
|
||||
[[runtimes]]
|
||||
src = "https://cdn.jsdelivr.net/pyodide/v0.21.2/full/pyodide.js"
|
||||
name = "pyodide-0.21.2"
|
||||
lang = "python"
|
||||
```
|
||||
|
||||
- JSON using the `type` and `src` attribute
|
||||
|
||||
```html
|
||||
<py-config type="json" src="./custom.json"></py-config>
|
||||
```
|
||||
where `custom.json` contains
|
||||
|
||||
```json
|
||||
{
|
||||
"splashscreen": {
|
||||
"autoclose": true,
|
||||
},
|
||||
"runtimes": [{
|
||||
"src": "https://cdn.jsdelivr.net/pyodide/v0.21.2/full/pyodide.js",
|
||||
"name": "pyodide-0.21.2",
|
||||
"lang": "python"
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### Mixing inline and file based configs
|
||||
|
||||
One can also use both i.e pass the config from `src` attribute as well as specify it as `inline`. So the following snippet is also valid:
|
||||
|
||||
```html
|
||||
<py-config src="./custom.toml">
|
||||
[[fetch]]
|
||||
files = ["./utils.py"]
|
||||
</py-config>
|
||||
```
|
||||
|
||||
This can also be done via JSON using the `type` attribute.
|
||||
|
||||
```html
|
||||
<py-config type="json" src="./custom.json">
|
||||
{
|
||||
"fetch": [{
|
||||
"files": ["./utils.py"]
|
||||
}]
|
||||
}
|
||||
</py-config>
|
||||
```
|
||||
|
||||
Note: While the `<py-config>` tag supports both TOML and JSON, one cannot mix the type of config passed from 2 different sources i.e. the case when inline config is in TOML format while config from src is in JSON format is NOT allowed. Similarly for the opposite case.
|
||||
|
||||
---
|
||||
|
||||
This is helpful in cases where a number of applications share a common configuration (which can be supplied via `src`), but their specific keys need to be customised and overridden.
|
||||
|
||||
The keys supplied through `inline` override the values present in config supplied via `src`.
|
||||
|
||||
## Dependencies and Packages
|
||||
|
||||
One can also declare dependencies so as to get access to many 3rd party OSS packages that are supported by PyScript.
|
||||
You can also link to `.whl` files directly on disk like in our [toga example](https://github.com/pyscript/pyscript/blob/main/examples/toga/freedom.html).
|
||||
|
||||
Package dependencies in the `<py-config>` can be declared by using the direct link to the package URL (whl or any other format supported by the chosen runtime) or by just providing the package name [and version]. If only the name [and version] are provided, packages will be installed directly from what's provided by your runtime or from PyPI.
|
||||
|
||||
NOTICE that only pure python packages from PyPI will work and packages with C dependencies will not. These need to be built specifically for WASM (please, consult the Pyodide project for more information about what's supported and on how to build packages with C dependencies)
|
||||
|
||||
```html
|
||||
<py-config>
|
||||
packages = ["./static/wheels/travertino-0.1.3-py3-none-any.whl"]
|
||||
</py-config>
|
||||
```
|
||||
|
||||
OR in JSON like
|
||||
|
||||
```html
|
||||
<py-config type="json">
|
||||
{
|
||||
"packages": ["./static/wheels/travertino-0.1.3-py3-none-any.whl"]
|
||||
}
|
||||
</py-config>
|
||||
```
|
||||
|
||||
If your `.whl` is not a pure Python wheel, then open a PR or issue with [pyodide](https://github.com/pyodide/pyodide) to get it added [here](https://github.com/pyodide/pyodide/tree/main/packages).
|
||||
|
||||
If there's enough popular demand, the pyodide team will likely work on supporting your package. Regardless, things will likely move faster if you make the PR and consult with the team to get unblocked.
|
||||
|
||||
For example, NumPy and Matplotlib are available. Notice here we're using `display(fig, target="plot")`, which takes the graph and displays it in the element with the id `plot`.
|
||||
|
||||
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Let's plot random numbers</h1>
|
||||
<div id="plot"></div>
|
||||
<py-config type="json">
|
||||
{
|
||||
"packages": ["numpy", "matplotlib"]
|
||||
}
|
||||
</py-config>
|
||||
<py-script>
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
x = np.random.randn(1000)
|
||||
y = np.random.randn(1000)
|
||||
fig, ax = plt.subplots()
|
||||
ax.scatter(x, y)
|
||||
display(fig, target="plot")
|
||||
</py-script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Local modules
|
||||
|
||||
In addition to packages, you can declare local Python modules that will
|
||||
be imported in the `<py-script>` tag. For example, we can place the random
|
||||
number generation steps in a function in the file `data.py`.
|
||||
|
||||
```python
|
||||
# data.py
|
||||
import numpy as np
|
||||
def make_x_and_y(n):
|
||||
x = np.random.randn(n)
|
||||
y = np.random.randn(n)
|
||||
return x, y
|
||||
```
|
||||
|
||||
In the HTML tag `<py-config>`, paths to local modules are provided in the
|
||||
`files` key within the `fetch` section. Refer to the [fetch](#fetch) section for
|
||||
more details.
|
||||
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Let's plot random numbers</h1>
|
||||
<div id="plot"></div>
|
||||
<py-config type="toml">
|
||||
packages = ["numpy", "matplotlib"]
|
||||
|
||||
[[fetch]]
|
||||
files = ["./data.py"]
|
||||
</py-config>
|
||||
<py-script>
|
||||
import matplotlib.pyplot as plt
|
||||
from data import make_x_and_y
|
||||
x, y = make_x_and_y(n=1000)
|
||||
fig, ax = plt.subplots()
|
||||
ax.scatter(x, y)
|
||||
display(fig, target="plot")
|
||||
</py-script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Supported configuration values
|
||||
|
||||
The following optional values are supported by `<py-config>`:
|
||||
| Value | Type | Description |
|
||||
| ------ | ---- | ----------- |
|
||||
| `name` | string | Name of the user application. This field can be any string and is to be used by the application author for their own customization purposes. |
|
||||
| `description` | string | Description of the user application. This field can be any string and is to be used by the application author for their own customization purposes. |
|
||||
| `version` | string | Version of the user application. This field can be any string and is to be used by the application author for their own customization purposes. It is not related to the PyScript version. |
|
||||
| `schema_version` | number | The version of the config schema which determines what all keys are supported. This can be supplied by the user so PyScript knows what to expect in the config. If not supplied, the latest version for the schema is automatically used. |
|
||||
| `type` | string | Type of the project. The default is an "app" i.e. a user application |
|
||||
| `author_name` | string | Name of the author. |
|
||||
| `author_email` | string | Email of the author. |
|
||||
| `license` | string | License to be used for the user application. |
|
||||
| `autoclose_loader` | boolean | If false, PyScript will not close the loading splash screen when the startup operations finish. |
|
||||
| `packages` | List of Packages | Dependencies on 3rd party OSS packages are specified here. The default value is an empty list. |
|
||||
| `fetch` | List of Stuff to fetch | Local Python modules OR resources from the internet are to be specified here using a Fetch Configuration, described below. The default value is an empty list. |
|
||||
| `plugins` | List of Plugins | List of Plugins are to be specified here. The default value is an empty list. |
|
||||
| `runtimes` | List of Runtimes | List of runtime configurations, described below. The default value contains a single Pyodide based runtime. |
|
||||
|
||||
### <a name="fetch">Fetch</a>
|
||||
|
||||
A fetch configuration consists of the following:
|
||||
|
||||
| Value | Type | Description |
|
||||
|--------------|-----------------|-------------------------------------------------|
|
||||
| `from` | string | Base URL for the resource to be fetched. |
|
||||
| `to_folder` | string | Name of the folder to create in the filesystem. |
|
||||
| `to_file` | string | Name of the target to create in the filesystem. |
|
||||
| `files` | List of strings | List of files to be downloaded. |
|
||||
|
||||
The parameters `to_file` and `files` shouldn't be supplied together.
|
||||
|
||||
#### Mechanism
|
||||
|
||||
The `fetch` mechanism works in the following manner:
|
||||
|
||||
- If both `files` and `to_file` parameters are supplied: Error!
|
||||
- `from` defaults to an empty string i.e. `""` to denote relative URLs of the serving directory
|
||||
- `to_folder` defaults to `.` i.e. the current working directory of the filesystem
|
||||
- If `files` is specified
|
||||
- for each `file` present in the `files` array
|
||||
- the `sourcePath` is calculated as `from + file`
|
||||
- the `destination` is calculated as `to_folder + file`
|
||||
- thus, the object is downloaded from `sourcePath` to `destination`
|
||||
- Else i.e. `files` is NOT specified
|
||||
- If `to_file` is specified
|
||||
- the object is downloaded from `from` to `to_folder + to_file`
|
||||
- Otherwise, calculate the `filename` at the end of `from` i.e. the part after last `/`
|
||||
- the object is downloaded from `from` to `to_folder + filename at the end of 'from'`
|
||||
|
||||
Learn more about `fetch` on PyScript [here](https://jeff.glass/post/whats-new-pyscript-2022-12-1)
|
||||
|
||||
#### Use-Cases
|
||||
|
||||
Assumptions:
|
||||
|
||||
The directory being served has the following tree structure:
|
||||
|
||||
```
|
||||
content/
|
||||
├─ index.html <<< File with <py-config>
|
||||
├─ info.txt
|
||||
├─ data/
|
||||
│ ├─ sensordata.csv
|
||||
├─ packages/
|
||||
│ ├─ my_package/
|
||||
│ │ ├─ __init__.py
|
||||
│ │ ├─ helloworld/
|
||||
│ │ │ ├─ __init__.py
|
||||
│ │ │ ├─ greetings.py
|
||||
```
|
||||
|
||||
1. Fetching a single file
|
||||
|
||||
```html
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
files = ['info.txt']
|
||||
</py-config>
|
||||
```
|
||||
|
||||
```html
|
||||
<py-script>
|
||||
with open('info.txt', 'r') as fp:
|
||||
print(fp.read())
|
||||
</py-script>
|
||||
```
|
||||
|
||||
2. Single File with Renaming
|
||||
|
||||
```html
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
from = 'info.txt'
|
||||
to_file = 'info_loaded_from_web.txt'
|
||||
</py-config>
|
||||
```
|
||||
|
||||
```html
|
||||
<py-script>
|
||||
with open('info_loaded_from_web.txt', 'r') as fp:
|
||||
print(fp.read())
|
||||
</py-script>
|
||||
```
|
||||
|
||||
3. Single File to another Directory
|
||||
|
||||
```html
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
files = ['info.txt']
|
||||
to_folder = 'infofiles/loaded_info'
|
||||
</py-config>
|
||||
```
|
||||
|
||||
```html
|
||||
<py-script>
|
||||
with open('infofiles/loaded_info/info.txt', 'r') as fp:
|
||||
print(fp.read())
|
||||
</py-script>
|
||||
```
|
||||
|
||||
4. Single File to another Directory with Renaming
|
||||
|
||||
```html
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
from = 'info.txt'
|
||||
to_folder = 'infofiles/loaded_info'
|
||||
to_file = 'info_loaded_from_web.txt'
|
||||
</py-config>
|
||||
```
|
||||
|
||||
```html
|
||||
<py-script>
|
||||
with open('infofiles/loaded_info/info_loaded_from_web.txt', 'r') as fp:
|
||||
print(fp.read())
|
||||
</py-script>
|
||||
```
|
||||
|
||||
5. Single file from a folder to the current working directory
|
||||
|
||||
```html
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
from = 'data/'
|
||||
files = ['sensordata.csv']
|
||||
</py-config>
|
||||
```
|
||||
|
||||
```html
|
||||
<py-script>
|
||||
with open('./sensordata.csv', 'r') as fp:
|
||||
print(fp.read())
|
||||
</py-script>
|
||||
```
|
||||
|
||||
6. Single file from a folder to another folder (i.e. not the current working directory)
|
||||
|
||||
```html
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
from = 'data/'
|
||||
to_folder = './local_data'
|
||||
files = ['sensordata.csv']
|
||||
</py-config>
|
||||
```
|
||||
|
||||
```html
|
||||
<py-script>
|
||||
with open('./local_data/sensordata.csv', 'r') as fp:
|
||||
print(fp.read())
|
||||
</py-script>
|
||||
```
|
||||
|
||||
7. Multiple files preserving directory structure
|
||||
|
||||
```html
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
from = 'packages/my_package/'
|
||||
files = ['__init__.py', 'helloworld/greetings.py', 'helloworld/__init__.py']
|
||||
to_folder = 'custom_pkg'
|
||||
</py-config>
|
||||
```
|
||||
|
||||
```html
|
||||
<py-script>
|
||||
from custom_pkg.helloworld.greetings import say_hi
|
||||
print(say_hi())
|
||||
</py-script>
|
||||
```
|
||||
|
||||
8. From an API endpoint which doesn't end in a filename
|
||||
|
||||
```html
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
from = 'https://catfact.ninja/fact'
|
||||
to_file = './cat_fact.json'
|
||||
</py-config>
|
||||
```
|
||||
|
||||
```html
|
||||
<py-script>
|
||||
import json
|
||||
with open("cat_fact.json", "r") as fp:
|
||||
data = json.load(fp)
|
||||
</py-script>
|
||||
```
|
||||
|
||||
### Runtime
|
||||
|
||||
A runtime configuration consists of the following:
|
||||
| Value | Type | Description |
|
||||
|--------|-------------------|-------------|
|
||||
| `src` | string (Required) | URL to the runtime source. |
|
||||
| `name` | string | Name of the runtime. This field can be any string and is to be used by the application author for their own customization purposes |
|
||||
| `lang` | string | Programming language supported by the runtime. This field can be used by the application author to provide clarification. It currently has no implications on how PyScript behaves. |
|
||||
|
||||
#### Example
|
||||
|
||||
- The default runtime is `pyodide`, another version of which can be specified as following
|
||||
|
||||
```html
|
||||
<py-config>
|
||||
[[runtimes]]
|
||||
src = "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js"
|
||||
name = "pyodide-0.20.0"
|
||||
lang = "python"
|
||||
</py-config>
|
||||
```
|
||||
|
||||
## Supplying extra information (or metadata)
|
||||
|
||||
Besides the above schema, a user can also supply any extra keys and values that are relevant as metadata information or perhaps are being used within the application.
|
||||
|
||||
For example, a valid config could also be with the snippet below:
|
||||
|
||||
```html
|
||||
<py-config type="toml">
|
||||
magic = "unicorn"
|
||||
</py-config>
|
||||
```
|
||||
|
||||
OR in JSON like
|
||||
|
||||
```html
|
||||
<py-config type="json">
|
||||
{
|
||||
"magic": "unicorn"
|
||||
}
|
||||
</py-config>
|
||||
```
|
||||
|
||||
If this `"magic"` key is present in config supplied via `src` and also present in config supplied via `inline`, then the value in the inline config is given priority i.e. the overriding process also works for custom keys.
|
||||
30
docs/reference/elements/py-repl.md
Normal file
30
docs/reference/elements/py-repl.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# <py-repl>
|
||||
|
||||
The `<py-repl>` element provides a REPL(Read Eval Print Loop) to evaluate multi-line Python and display output.
|
||||
|
||||
## Attributes
|
||||
|
||||
| attribute | type | default | description |
|
||||
|-------------------|---------|---------|---------------------------------------|
|
||||
| **auto-generate** | boolean | | Auto-generates REPL after evaluation |
|
||||
| **output** | string | | The element to write output into |
|
||||
|
||||
### Examples
|
||||
|
||||
#### `<py-repl>` element set to auto-generate
|
||||
|
||||
```html
|
||||
<py-repl auto-generate="true"> </py-repl>
|
||||
```
|
||||
|
||||
#### `<py-repl>` element with output
|
||||
|
||||
```html
|
||||
<div id="replOutput"></div>
|
||||
<py-repl output="replOutput">
|
||||
hello = "Hello world!"
|
||||
hello
|
||||
</py-repl>
|
||||
```
|
||||
|
||||
Note that if we `print` any element in the repl, the output will be printed in the [`py-terminal`](../plugins/py-terminal.md) if is enabled.
|
||||
111
docs/reference/elements/py-script.md
Normal file
111
docs/reference/elements/py-script.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# <py-script>
|
||||
|
||||
The `<py-script>` element lets you execute multi-line Python scripts both inline and via a src attribute.
|
||||
|
||||
## Attributes
|
||||
|
||||
| attribute | type | default | description |
|
||||
|-----------|------|---------|------------------------------|
|
||||
| **src** | url | | Url of a python source file. |
|
||||
|
||||
## Examples
|
||||
|
||||
### Inline `<py-script>` element
|
||||
|
||||
Let's execute this multi-line Python script to compute π and print it back onto the page
|
||||
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<py-script>
|
||||
print("Let's compute π:")
|
||||
def compute_pi(n):
|
||||
pi = 2
|
||||
for i in range(1,n):
|
||||
pi *= 4 * i ** 2 / (4 * i ** 2 - 1)
|
||||
return pi
|
||||
|
||||
pi = compute_pi(100000)
|
||||
s = f"π is approximately {pi:.3f}"
|
||||
print(s)
|
||||
</py-script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Using `<py-script>` element with `src` attribute
|
||||
|
||||
we can also move our python code to its own file and reference it via the `src` attribute.
|
||||
|
||||
|
||||
```python
|
||||
# compute_pi.py
|
||||
print("Let's compute π:")
|
||||
def compute_pi(n):
|
||||
pi = 2
|
||||
for i in range(1,n):
|
||||
pi *= 4 * i ** 2 / (4 * i ** 2 - 1)
|
||||
return pi
|
||||
|
||||
pi = compute_pi(100000)
|
||||
s = f"π is approximately {pi:.3f}"
|
||||
print(s)
|
||||
```
|
||||
|
||||
Since both compute_pi.py and index.html are in the same directory, we can reference the python file with a relative path.
|
||||
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<py-script src="compute_pi.py"></py-script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Writing into labeled elements
|
||||
|
||||
In the example above, we had a single `<py-script>` tag printing
|
||||
one or more lines onto the page in order. Within the `<py-script>`, you can
|
||||
use the `Element` class to create a python object for interacting with
|
||||
page elements. Objects created from the `Element` class provide the `.write()` method
|
||||
which enables you to send strings into the page elements referenced by those objects.
|
||||
|
||||
For example, we'll add some style elements and provide placeholders for
|
||||
the `<py-script>` tag to write to.
|
||||
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<b><p>Today is <u><label id='today'></label></u></p></b>
|
||||
<br>
|
||||
<div id="pi" class="alert alert-primary"></div>
|
||||
<py-script>
|
||||
import datetime as dt
|
||||
Element('today').write(dt.date.today().strftime('%A %B %d, %Y'))
|
||||
|
||||
def compute_pi(n):
|
||||
pi = 2
|
||||
for i in range(1,n):
|
||||
pi *= 4 * i ** 2 / (4 * i ** 2 - 1)
|
||||
return pi
|
||||
|
||||
pi = compute_pi(100000)
|
||||
Element('pi').write(f'π is approximately {pi:.3f}')
|
||||
</py-script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
39
docs/reference/exceptions.md
Normal file
39
docs/reference/exceptions.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Exceptions and error codes
|
||||
|
||||
When creating pages with PyScript, you may encounter exceptions. Each handled exception will contain a specific code which will give you more information about it.
|
||||
This reference guide contains the error codes you might find and a description of each of them.
|
||||
|
||||
## User Errors
|
||||
|
||||
| Error code | Description | Recommendation |
|
||||
|------------|--------------------------------|--------------------|
|
||||
| PY1000 | Invalid configuration supplied | Confirm that your `py-config` tag is using a valid `TOML` or `JSON` syntax and is using the correct configuration type. |
|
||||
| PY1001 | Unable to install package(s) | Confirm that the package contains a pure Python 3 wheel or the name of the package is correct. |
|
||||
| PY9000 | Top level await is deprecated | Create a coroutine with your code and schedule it with `asyncio.ensure_future` or similar |
|
||||
|
||||
|
||||
|
||||
## Fetch Errors
|
||||
|
||||
These error codes are related to any exception raised when trying to fetch a resource. If, while trying to fetch a resource, we encounter a status code that is not 200, the error code will contain the HTTP status code and the `PY0` prefix. For example, if we encounter a 404 error, the error code will be `P02404`.
|
||||
|
||||
|
||||
| Error Code | Description |
|
||||
|------------|--------------------------------------------------------------|
|
||||
| PY0001 | Generic fetch error, failed to fetch page from the server |
|
||||
| PY0002 | Name supplied when trying to fetch resource is invalid |
|
||||
| PY0401 | You are not authorized to access this resource. |
|
||||
| PY0403 | You are not allowed to access this resource. |
|
||||
| PY0404 | The page you are trying to fetch does not exist. |
|
||||
| PY0500 | The server encountered an internal error. |
|
||||
| PY0503 | The server is currently unavailable. |
|
||||
|
||||
## PY1001
|
||||
|
||||
Pyscript cannot install the package(s) you specified in your `py-config` tag. This can happen for a few reasons:
|
||||
|
||||
- The package does not exist
|
||||
- The package does not contain a pure Python 3 wheel
|
||||
- An error occurred while trying to install the package
|
||||
|
||||
An error banner should appear on your page with the error code and a description of the error or a traceback. You can also check the developer console for more information.
|
||||
@@ -1,11 +1,54 @@
|
||||
# Reference
|
||||
|
||||
This reference section will have manually documented or fully
|
||||
automated code documentation. **Coming soon!**
|
||||
|
||||
```{toctree}
|
||||
---
|
||||
maxdepth: 1
|
||||
glob:
|
||||
caption: Elements
|
||||
---
|
||||
elements/*
|
||||
```
|
||||
|
||||
```{toctree}
|
||||
---
|
||||
maxdepth: 1
|
||||
glob:
|
||||
caption: Modules
|
||||
---
|
||||
modules/*
|
||||
```
|
||||
|
||||
```{toctree}
|
||||
---
|
||||
maxdepth: 1
|
||||
glob:
|
||||
caption: Plugins
|
||||
---
|
||||
plugins/*
|
||||
```
|
||||
|
||||
```{toctree}
|
||||
---
|
||||
maxdepth: 1
|
||||
glob:
|
||||
caption: API
|
||||
---
|
||||
API/*
|
||||
```
|
||||
|
||||
```{toctree}
|
||||
---
|
||||
maxdepth: 2
|
||||
glob:
|
||||
caption: Exceptions
|
||||
---
|
||||
exceptions
|
||||
```
|
||||
|
||||
```{toctree}
|
||||
---
|
||||
maxdepth: 1
|
||||
caption: Miscellaneous
|
||||
---
|
||||
faq
|
||||
```
|
||||
|
||||
77
docs/reference/modules/pyscript.md
Normal file
77
docs/reference/modules/pyscript.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# pyscript
|
||||
|
||||
The code underlying PyScript is a TypeScript/JavaScript module, which is loaded and executed by the browser. This is what loads when you include, for example, `<script defer src="https://pyscript.net/latest/pyscript.js">` in your HTML.
|
||||
|
||||
The module is exported to the browser as `pyscript`. The exports from this module are:
|
||||
|
||||
## pyscript.version
|
||||
|
||||
Once `pyscript.js` has loaded, the version of PyScript that is currently running can be accessed via `pyscript.version`.
|
||||
```html
|
||||
<script defer onload="console.log(`${pyscript.version}`)" src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
```
|
||||
```js
|
||||
//example result
|
||||
Object { year: 2022, month: 11, patch: 1, releaselevel: "dev" }
|
||||
```
|
||||
|
||||
## pyscript.runtime
|
||||
|
||||
The RunTime object which is responsible for executing Python code in the Browser. Currently, all runtimes are assumed to be Pyodide runtimes, but there is flexibility to expand this to other web-based Python runtimes in future versions.
|
||||
|
||||
The RunTime object has the following attributes
|
||||
|
||||
| attribute | type | description |
|
||||
|---------------------|---------------------|-----------------------------------------------------------------------------|
|
||||
| **src** | string | The URL from which the current runtime was fetched |
|
||||
| **interpreter** | RuntimeInterpreter | A reference to the runtime object itself |
|
||||
| **globals** | any | The globals dictionary of the runtime, if applicable/accessible |
|
||||
| **name (optional)** | string | A user-designated name for the runtime |
|
||||
| **lang (optional)** | string | A user-designation for the language the runtime runs ('Python', 'C++', etc) |
|
||||
|
||||
### pyscript.runtime.src
|
||||
|
||||
The URL from which the current runtime was fetched.
|
||||
|
||||
### pyscript.runtime.interpreter
|
||||
|
||||
A reference to the Runtime wrapper that PyScript uses to execute code. object itself. This allows other frameworks, modules etc to interact with the same [(Pyodide) runtime instance](https://pyodide.org/en/stable/usage/api/js-api.html) that PyScript uses.
|
||||
|
||||
For example, assuming we've loaded Pyodide, we can access the methods of the Pyodide runtime as follows:
|
||||
|
||||
```html
|
||||
<button onclick="logFromPython()">Click Me to Run Some Python</button>
|
||||
<script>
|
||||
function logFromPython(){
|
||||
pyscript.runtime.interpreter.runPython(`
|
||||
animal = "Python"
|
||||
sound = "sss"
|
||||
console.warn(f"{animal}s go " + sound * 5)
|
||||
`)
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### pyscript.runtime.globals
|
||||
|
||||
A proxy for the runtime's `globals()` dictionary. For example:
|
||||
|
||||
```html
|
||||
<body>
|
||||
<py-script>x = 42</py-script>
|
||||
|
||||
<button onclick="showX()">Click Me to Get 'x' from Python</button>
|
||||
<script>
|
||||
function showX(){
|
||||
console.log(`In Python right now, x = ${pyscript.runtime.globals.get('x')}`)
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
### pyscript.runtime.name
|
||||
|
||||
A user-supplied string for the runtime given at its creation. For user reference only - does not affect the operation of the runtime or PyScript.
|
||||
|
||||
### PyScript.runtime.lang
|
||||
|
||||
A user-supplied string for the language the runtime uses given at its creation. For user reference only - does not affect the operation of the runtime or PyScript.
|
||||
41
docs/reference/plugins/py-terminal.md
Normal file
41
docs/reference/plugins/py-terminal.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# <py-terminal>
|
||||
|
||||
This is one of the core plugins in PyScript, which is active by default. With it, you can print to `stdout` and `stderr` from your python code, and the output will be displayed on the page in `<py-terminal>`.
|
||||
|
||||
## Configuration
|
||||
|
||||
You can control how `<py-terminal>` behaves by setting the value of the `terminal` configuration in your `<py-config>`.
|
||||
|
||||
| value | description |
|
||||
|-------|-------------|
|
||||
| `false` | Don't add `<py-terminal>` to the page |
|
||||
| `true` | Automatically add a `<py-terminal>` to the page |
|
||||
| `"auto"` | This is the default. Automatically add a `<py-terminal auto>`, to the page. The terminal is initially hidden and automatically shown as soon as something writes to `stdout` and/or `stderr` |
|
||||
|
||||
### Examples
|
||||
|
||||
```html
|
||||
<py-config>
|
||||
terminal = true
|
||||
</py-config>
|
||||
|
||||
<py-script>
|
||||
print("Hello, world!")
|
||||
</py-script>
|
||||
```
|
||||
|
||||
This example will create a new `<py-terminal>`, the value "Hello, world!" that was printed will show in it.
|
||||
|
||||
You can also add one (or more) `<py-terminal>` to the page manually.
|
||||
|
||||
```html
|
||||
<py-script>
|
||||
print("Hello, world!")
|
||||
</py-script>
|
||||
|
||||
<py-terminal></py-terminal>
|
||||
```
|
||||
|
||||
```{note}
|
||||
If you include a `<py-terminal>` in the page, you can skip `terminal` from your `<py-config>`.
|
||||
```
|
||||
@@ -1,3 +0,0 @@
|
||||
# Deployment
|
||||
|
||||
**Coming soon!**
|
||||
@@ -5,7 +5,7 @@ This page will guide you through getting started with PyScript.
|
||||
## Development setup
|
||||
|
||||
PyScript does not require any development environment other
|
||||
than a web browser. We recommend using [Chrome](https://www.google.com/chrome/).
|
||||
then a web browser (we recommend using [Chrome](https://www.google.com/chrome/)) and a text editor, even though using your [IDE](https://en.wikipedia.org/wiki/Integrated_development_environment) of choice might be convenient.
|
||||
|
||||
If you're using [VSCode](https://code.visualstudio.com/), the
|
||||
[Live Server extension](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer)
|
||||
@@ -14,7 +14,7 @@ can be used to reload the page as you edit the HTML file.
|
||||
## Installation
|
||||
|
||||
There is no installation required. In this document, we'll use
|
||||
the PyScript assets served on https://pyscript.net.
|
||||
the PyScript assets served on [https://pyscript.net](https://pyscript.net).
|
||||
|
||||
If you want to download the source and build it yourself, follow
|
||||
the instructions in the [README.md](https://github.com/pyscript/pyscript/blob/main/README.md) file.
|
||||
@@ -31,237 +31,238 @@ open an HTML by double-clicking it in your file explorer.
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
</head>
|
||||
<body> <py-script> print('Hello, World!') </py-script> </body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Notice the use of the `<py-script>` tag in the HTML body. This
|
||||
is where you'll write your Python code. In the following sections, we'll
|
||||
introduce the eight tags provided by PyScript.
|
||||
|
||||
## The py-script tag
|
||||
|
||||
The `<py-script>` tag lets you execute multi-line Python scripts and
|
||||
print back onto the page. For example, we can compute π.
|
||||
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<py-script>
|
||||
print("Let's compute π:")
|
||||
def compute_pi(n):
|
||||
pi = 2
|
||||
for i in range(1,n):
|
||||
pi *= 4 * i ** 2 / (4 * i ** 2 - 1)
|
||||
return pi
|
||||
|
||||
pi = compute_pi(100000)
|
||||
s = f"π is approximately {pi:.3f}"
|
||||
print(s)
|
||||
</py-script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Writing into labeled elements
|
||||
|
||||
In the example above, we had a single `<py-script>` tag printing
|
||||
one or more lines onto the page in order. Within the `<py-script>`, you
|
||||
have access to the `pyscript` module, which provides a `.write()` method
|
||||
to send strings into labeled elements on the page.
|
||||
|
||||
For example, we'll add some style elements and provide placeholders for
|
||||
the `<py-script>` tag to write to.
|
||||
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<b><p>Today is <u><label id='today'></label></u></p></b>
|
||||
<br>
|
||||
<div id="pi" class="alert alert-primary"></div>
|
||||
<py-script>
|
||||
import datetime as dt
|
||||
pyscript.write('today', dt.date.today().strftime('%A %B %d, %Y'))
|
||||
|
||||
def compute_pi(n):
|
||||
pi = 2
|
||||
for i in range(1,n):
|
||||
pi *= 4 * i ** 2 / (4 * i ** 2 - 1)
|
||||
return pi
|
||||
|
||||
pi = compute_pi(100000)
|
||||
pyscript.write('pi', f'π is approximately {pi:.3f}')
|
||||
print('Hello, World!')
|
||||
</py-script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## The py-env tag
|
||||
## A more complex example
|
||||
|
||||
In addition to the [Python Standard Library](https://docs.python.org/3/library/) and
|
||||
the `pyscript` module, many 3rd-party OSS packages will work out-of-the-box with PyScript.
|
||||
Now that we know how you can create a simple 'Hello, World!' example, let's see a more complex example. This example will use the Demo created by [Cheuk Ting Ho](https://github.com/Cheukting). In this example, we will use more features from PyScript.
|
||||
|
||||
In order to use them, you will need to declare the dependencies using the `<py-env>` tag in the
|
||||
HTML head. You can also link to `.whl` files directly on disk like in our [toga example](https://github.com/pyscript/pyscript/blob/main/examples/toga/freedom.html).
|
||||
### Setting up the base index file
|
||||
|
||||
```
|
||||
<py-env>
|
||||
- './static/wheels/travertino-0.1.3-py3-none-any.whl'
|
||||
</py-env>
|
||||
```
|
||||
Let's create a new file called `index.html` and add the following content:
|
||||
|
||||
If your `.whl` is not a pure Python wheel, then open a PR or issue with [pyodide](https://github.com/pyodide/pyodide) to get it added [here](https://github.com/pyodide/pyodide/tree/main/packages).
|
||||
If there's enough popular demand, the pyodide team will likely work on supporting your package. Regardless, things will likely move faster if you make the PR and consult with the team to get unblocked.
|
||||
|
||||
For example, NumPy and Matplotlib are available. Notice here we're using `<py-script output="plot">`
|
||||
as a shortcut, which takes the expression on the last line of the script and runs `pyscript.write('plot', fig)`.
|
||||
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<py-env>
|
||||
- numpy
|
||||
- matplotlib
|
||||
</py-env>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Let's plot random numbers</h1>
|
||||
<div id="plot"></div>
|
||||
<py-script output="plot">
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
x = np.random.randn(1000)
|
||||
y = np.random.randn(1000)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.scatter(x, y)
|
||||
fig
|
||||
</py-script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Local modules
|
||||
|
||||
In addition to packages, you can declare local Python modules that will
|
||||
be imported in the `<py-script>` tag. For example, we can place the random
|
||||
number generation steps in a function in the file `data.py`.
|
||||
|
||||
```python
|
||||
# data.py
|
||||
import numpy as np
|
||||
|
||||
|
||||
def make_x_and_y(n):
|
||||
x = np.random.randn(n)
|
||||
y = np.random.randn(n)
|
||||
return x, y
|
||||
```
|
||||
|
||||
In the HTML tag `<py-env>`, paths to local modules are provided in the
|
||||
`paths:` key.
|
||||
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<py-env>
|
||||
- numpy
|
||||
- matplotlib
|
||||
- paths:
|
||||
- ./data.py
|
||||
</py-env>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Let's plot random numbers</h1>
|
||||
<div id="plot"></div>
|
||||
<py-script output="plot">
|
||||
import matplotlib.pyplot as plt
|
||||
from data import make_x_and_y
|
||||
|
||||
x, y = make_x_and_y(n=1000)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.scatter(x, y)
|
||||
fig
|
||||
</py-script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
## The py-repl tag
|
||||
|
||||
The `<py-repl>` tag creates a REPL component that is rendered to the page as a code editor, allowing you to write executable code inline.
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<title>Ice Cream Picker</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
<py-repl></py-repl>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## The py-config tag
|
||||
In this first step, we have created the index file, imported `pyscript.css` and `pyscript.js`. We are ready to start adding the elements we need for our application.
|
||||
|
||||
Use the `<py-config>` tag to set and configure general metadata about your PyScript application in YAML format. If you are unfamiliar with YAML, consider reading [Red Hat's YAML for beginners](https://www.redhat.com/sysadmin/yaml-beginners) guide for more information.
|
||||
### Importing the needed libraries
|
||||
|
||||
The `<py-config>` tag can be used as follows:
|
||||
For this example, we will need to install `pandas` and `matplotlib`. We can install libraries using the `<py-config>` tag so we can import them later. Please refer to the [`<py-config>`](../reference/elements/py-config.md) documentation for more information.
|
||||
|
||||
```html
|
||||
<py-config>
|
||||
autoclose_loader: false
|
||||
runtimes:
|
||||
- src: "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js"
|
||||
name: pyodide-0.20
|
||||
lang: python
|
||||
</py-config>
|
||||
<html>
|
||||
<head>
|
||||
<title>Ice Cream Picker</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<py-config>
|
||||
packages = ["matplotlib", "pandas"]
|
||||
</py-config>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
The following optional values are supported by `<py-config>`:
|
||||
| Value | Type | Description |
|
||||
| ------ | ---- | ----------- |
|
||||
| `autoclose_loader` | boolean | If false, PyScript will not close the loading splash screen when the startup operations finish. |
|
||||
| `name` | string | Name of the user application. This field can be any string and is to be used by the application author for their own customization purposes. |
|
||||
| `version` | string | Version of the user application. This field can be any string and is to be used by the application author for their own customization purposes. It is not related to the PyScript version. |
|
||||
| `runtimes` | List of Runtimes | List of runtime configurations, described below.
|
||||
### Importing the data and exploring
|
||||
|
||||
A runtime configuration consists of the following:
|
||||
| Value | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| `src` | string (Required) | URL to the runtime source. |
|
||||
| `name` | string | Name of the runtime. This field can be any string and is to be used by the application author for their own customization purposes |
|
||||
| `lang` | string | Programming language supported by the runtime. This field can be used by the application author to provide clarification. It currently has no implications on how PyScript behaves. |
|
||||
Now that we have installed the needed libraries, we can import and explore the data. In this step, we need to create a `<py-script>` tag to import our dependencies, read the data with pandas and then use `py-repl` to explore the data.
|
||||
|
||||
## Visual component tags
|
||||
You may want to read the [`<py-script>`](../reference/elements/py-script.md) and [`<py-repl>`](../reference/elements/py-repl.md) documentation for more information about these elements.
|
||||
|
||||
The following tags can be used to add visual attributes to your HTML page.
|
||||
|
||||
| Tag | Description |
|
||||
| --- | ----------- |
|
||||
| `<py-inputbox>` | Adds an input box that can be used to prompt users to enter input values. |
|
||||
| `<py-box>` | Creates a container object that can be used to host one or more visual components that define how elements of `<py-box>` should align and show on the page. |
|
||||
| `<py-button>` | Adds a button to which authors can add labels and event handlers for actions on the button, such as `on_focus` or `on_click`. |
|
||||
| `<py-title>` | Adds a static text title component that styles the text inside the tag as a page title. |
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<title>Ice Cream Picker</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
```{note}
|
||||
All the elements above are experimental and not implemented at their full functionality. Use them with the understanding that the APIs or full support might change or be removed until the visual components are more mature.
|
||||
<py-config>
|
||||
packages = ["matplotlib", "pandas"]
|
||||
</py-config>
|
||||
|
||||
<py-script>
|
||||
import pandas as pd
|
||||
|
||||
from pyodide.http import open_url
|
||||
|
||||
url = (
|
||||
"https://raw.githubusercontent.com/Cheukting/pyscript-ice-cream/main/bj-products.csv"
|
||||
)
|
||||
ice_data = pd.read_csv(open_url(url))
|
||||
</py-script>
|
||||
|
||||
<py-repl>
|
||||
ice_data
|
||||
</py-repl>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Note that we are adding `ice_data` to `py-repl` to pre-populate the REPL with this variable, so you don't have to type it yourself.
|
||||
|
||||
### Creating the plot
|
||||
|
||||
Now that we have the data, we can create the plot. We will use the `matplotlib` library to make the plot. We will use the `display` API to display the plot on the page. You may want to read the [`display`](../reference/API/display.md) documentation for more information.
|
||||
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<title>Ice Cream Picker</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<py-config>
|
||||
packages = ["matplotlib", "pandas"]
|
||||
</py-config>
|
||||
|
||||
<py-script>
|
||||
import pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from pyodide.http import open_url
|
||||
|
||||
url = (
|
||||
"https://raw.githubusercontent.com/Cheukting/pyscript-ice-cream/main/bj-products.csv"
|
||||
)
|
||||
ice_data = pd.read_csv(open_url(url))
|
||||
|
||||
def plot(data):
|
||||
plt.rcParams["figure.figsize"] = (22,20)
|
||||
fig, ax = plt.subplots()
|
||||
bars = ax.barh(data["name"], data["rating"], height=0.7)
|
||||
ax.bar_label(bars)
|
||||
plt.title("Rating of ice cream flavours of your choice")
|
||||
display(fig, target="graph-area", append=False)
|
||||
|
||||
plot(ice_data)
|
||||
</py-script>
|
||||
|
||||
<py-repl>
|
||||
ice_data
|
||||
</py-repl>
|
||||
|
||||
<div id="graph-area"></div>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Select specific flavours
|
||||
|
||||
Now that we have a way to explore the data using `py-repl` and a way to create the plot using all of the data, it's time for us to add a way to select specific flavours.
|
||||
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<title>Ice Cream Picker</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<py-config>
|
||||
packages = ["matplotlib", "pandas"]
|
||||
</py-config>
|
||||
|
||||
<py-script>
|
||||
import pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from pyodide.http import open_url
|
||||
from pyodide.ffi import create_proxy
|
||||
|
||||
url = (
|
||||
"https://raw.githubusercontent.com/Cheukting/pyscript-ice-cream/main/bj-products.csv"
|
||||
)
|
||||
ice_data = pd.read_csv(open_url(url))
|
||||
|
||||
current_selected = []
|
||||
flavour_elements = document.getElementsByName("flavour")
|
||||
|
||||
def plot(data):
|
||||
plt.rcParams["figure.figsize"] = (22,20)
|
||||
fig, ax = plt.subplots()
|
||||
bars = ax.barh(data["name"], data["rating"], height=0.7)
|
||||
ax.bar_label(bars)
|
||||
plt.title("Rating of ice cream flavours of your choice")
|
||||
display(fig, target="graph-area", append=False)
|
||||
|
||||
def select_flavour(event):
|
||||
for ele in flavour_elements:
|
||||
if ele.checked:
|
||||
current_selected = ele.value
|
||||
break
|
||||
if current_selected == "ALL":
|
||||
plot(ice_data)
|
||||
else:
|
||||
filter = ice_data.apply(lambda x: ele.value in x["ingredients"], axis=1)
|
||||
plot(ice_data[filter])
|
||||
|
||||
ele_proxy = create_proxy(select_flavour)
|
||||
|
||||
for ele in flavour_elements:
|
||||
if ele.value == "ALL":
|
||||
ele.checked = True
|
||||
current_selected = ele.value
|
||||
ele.addEventListener("change", ele_proxy)
|
||||
|
||||
plot(ice_data)
|
||||
|
||||
</py-script>
|
||||
|
||||
<div id="input" style="margin: 20px;">
|
||||
Select your 🍨 flavour: <br/>
|
||||
<input type="radio" id="all" name="flavour" value="ALL">
|
||||
<label for="all"> All 🍧</label>
|
||||
<input type="radio" id="chocolate" name="flavour" value="COCOA">
|
||||
<label for="chocolate"> Chocolate 🍫</label>
|
||||
<input type="radio" id="cherrie" name="flavour" value="CHERRIES">
|
||||
<label for="cherrie"> Cherries 🍒</label>
|
||||
<input type="radio" id="berries" name="flavour" value="BERRY">
|
||||
<label for="berries"> Berries 🍓</label>
|
||||
<input type="radio" id="cheese" name="flavour" value="CHEESE">
|
||||
<label for="cheese"> Cheese 🧀</label>
|
||||
<input type="radio" id="peanut" name="flavour" value="PEANUT">
|
||||
<label for="peanut"> Peanut 🥜</label>
|
||||
</div>
|
||||
|
||||
<py-repl>
|
||||
ice_data
|
||||
</py-repl>
|
||||
|
||||
<div id="graph-area"></div>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
@@ -7,6 +7,7 @@ This is the tutorials section for beginners.
|
||||
maxdepth: 2
|
||||
---
|
||||
getting-started
|
||||
deployment
|
||||
setup
|
||||
py-config-fetch
|
||||
py-config-runtime
|
||||
writing-to-page
|
||||
```
|
||||
|
||||
188
docs/tutorials/py-config-fetch.md
Normal file
188
docs/tutorials/py-config-fetch.md
Normal file
@@ -0,0 +1,188 @@
|
||||
# Using the fetch from py-config
|
||||
|
||||
This tutorial shows how to use the fetch configuration from `py-config` to fetch two files from a remote server, store them in a local directory, and verify their contents.
|
||||
|
||||
## Development setup
|
||||
|
||||
We will create a todo list application similar to the one in the [examples](https://pyscript.net/examples/todo.html). To do this, we need three things:
|
||||
|
||||
* An `index.html` file containing the HTML for the application.
|
||||
* A `todo.py` file containing the Python code for the application.
|
||||
* A `utils.py` file containing some utility functions for the application.
|
||||
|
||||
|
||||
We will use the `fetch` configuration from `py-config` to fetch these files from a remote server and store them in a local directory.
|
||||
|
||||
### Creating the html file
|
||||
|
||||
In this first step, we will create the `index.html` file and import both `pyscript.css` and `pyscript.js`. These are needed to run our Python code in the browser and style the application.
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>My Todo</title>
|
||||
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Fetching the files
|
||||
|
||||
### Using `fetch` to get the python files
|
||||
|
||||
Now we will use the `fetch` configuration from `py-config` to fetch the `todo.py` and `utils.py` files from a remote server and store them in a local directory called `todo`. Here we will fetch files from different URLs, using a `fetch` per item.
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>My Todo</title>
|
||||
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
from = "https://pyscript.net/examples/"
|
||||
files = ["utils.py"]
|
||||
[[fetch]]
|
||||
from = "https://gist.githubusercontent.com/FabioRosado/faba0b7f6ad4438b07c9ac567c73b864/raw/37603b76dc7ef7997bf36781ea0116150f727f44/"
|
||||
files = ["todo.py"]
|
||||
</py-config>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Creating a todo application
|
||||
|
||||
### Creating the todo elements
|
||||
|
||||
Now we will create the todo elements in the `body` of the `index.html` file.
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>My Todo</title>
|
||||
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
from = "https://pyscript.net/examples/"
|
||||
files = ["utils.py"]
|
||||
[[fetch]]
|
||||
from = "https://gist.githubusercontent.com/FabioRosado/faba0b7f6ad4438b07c9ac567c73b864/raw/37603b76dc7ef7997bf36781ea0116150f727f44/"
|
||||
files = ["todo.py"]
|
||||
</py-config>
|
||||
<section>
|
||||
<div class="text-center w-full mb-8">
|
||||
<h1 class="text-3xl font-bold text-gray-800 uppercase tracking-tight">To Do List</h1>
|
||||
</div>
|
||||
<div>
|
||||
<input id="new-task-content" class="py-input" type="text">
|
||||
<button id="new-task-btn" class="py-button" type="submit" py-click="add_task()">
|
||||
Add task
|
||||
</button>
|
||||
</div>
|
||||
<div id="list-tasks-container" class="flex flex-col-reverse mt-4"></div>
|
||||
<template id="task-template">
|
||||
<section class="task py-li-element">
|
||||
<label for="flex items-center p-2 ">
|
||||
<input class="mr-2" type="checkbox">
|
||||
<p class="m-0 inline"></p>
|
||||
</label>
|
||||
</section>
|
||||
</template>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Our todo application is starting to shape up, although if you try to add any tasks, you will notice that nothing happens. This is because we have not yet imported the `todo.py` file.
|
||||
|
||||
### Importing the needed functions from `todo.py`
|
||||
|
||||
This is where the magic happens. We can import the `todo.py` file by adding it as a source to the `py-script` tag. By specifying the file, pyscript will automatically import the file and run the code in it.
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>My Todo</title>
|
||||
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
from = "https://pyscript.net/examples/"
|
||||
files = ["utils.py"]
|
||||
[[fetch]]
|
||||
from = "https://gist.githubusercontent.com/FabioRosado/faba0b7f6ad4438b07c9ac567c73b864/raw/37603b76dc7ef7997bf36781ea0116150f727f44/"
|
||||
files = ["todo.py"]
|
||||
</py-config>
|
||||
<py-script>
|
||||
from todo import add_task, add_task_event
|
||||
</py-script>
|
||||
<section>
|
||||
<div class="text-center w-full mb-8">
|
||||
<h1 class="text-3xl font-bold text-gray-800 uppercase tracking-tight">To Do List</h1>
|
||||
</div>
|
||||
<div>
|
||||
<input id="new-task-content" class="py-input" type="text">
|
||||
<button id="new-task-btn" class="py-button" type="submit" py-click="add_task()">
|
||||
Add task
|
||||
</button>
|
||||
</div>
|
||||
<div id="list-tasks-container" class="flex flex-col-reverse mt-4"></div>
|
||||
<template id="task-template">
|
||||
<section class="task py-li-element">
|
||||
<label for="flex items-center p-2 ">
|
||||
<input class="mr-2" type="checkbox">
|
||||
<p class="m-0 inline"></p>
|
||||
</label>
|
||||
</section>
|
||||
</template>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
You can now save the file and refresh the page. You should now be able to add tasks to your todo list.
|
||||
|
||||
## That's it!
|
||||
|
||||
You have now created a todo application using pyscript. You can add tasks and mark them as done. Let's take a recap of what we have achieved:
|
||||
|
||||
* We have imported three separate files into our `index.html` file using the `py-config` tag.
|
||||
* We have created the necessary HTML code to create our todo's
|
||||
* We have imported functions from the `todo.py` file, using the `py-script` tag.
|
||||
|
||||
For reference, the code from [the gist](https://gist.githubusercontent.com/FabioRosado/faba0b7f6ad4438b07c9ac567c73b864/raw/37603b76dc7ef7997bf36781ea0116150f727f44/todo.py) is the same code that our [todo example](https://pyscript.net/examples/todo.html) uses with a slight change of importing `Element` from `pyscript`.
|
||||
88
docs/tutorials/py-config-runtime.md
Normal file
88
docs/tutorials/py-config-runtime.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# Setting a pyodide runtime
|
||||
|
||||
Pyscript will automatically set the runtime for you, but you can also set it manually. This is useful if you want to use a different version than the one set by default.
|
||||
|
||||
## Development setup
|
||||
|
||||
To get started, let's create a new `index.html` file and import `pyscript.js`.
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>Runtime</title>
|
||||
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
We are using the pyodide CDN to setup our runtime, but you can also download the files from [the pyodide GitHub release](https://github.com/pyodide/pyodide/releases/tag/0.22.0a3), unzip them and use the `pyodide.js` file as your runtime.
|
||||
|
||||
## Setting the runtime
|
||||
|
||||
To set the runtime, you can use the `runtime` configuration in the `py-config` element. In this tutorial, we will use the default `TOML` format, but know that you can also use `json` if you prefer by changing the `type` attribute of the `py-config` element.
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>Runtime</title>
|
||||
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<py-config>
|
||||
[[runtimes]]
|
||||
src = "https://cdn.jsdelivr.net/pyodide/v0.22.0a3/full/pyodide.js"
|
||||
name = "pyodide-0.22.0a3"
|
||||
lang = "python"
|
||||
</py-config>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Confirming the runtime version
|
||||
|
||||
To confirm that the runtime is set correctly, you can open the DevTools and check the version from the console. But for the sake of this tutorial, let's create a `py-script` tag and print pyodide's version.
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>Runtime</title>
|
||||
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<py-config>
|
||||
[[runtimes]]
|
||||
src = "https://cdn.jsdelivr.net/pyodide/v0.22.0a3/full/pyodide.js"
|
||||
name = "pyodide-0.22.0a3"
|
||||
lang = "python"
|
||||
</py-config>
|
||||
<py-script>
|
||||
import pyodide
|
||||
print(pyodide.__version__)
|
||||
</py-script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
@@ -1,3 +0,0 @@
|
||||
# Setup
|
||||
|
||||
**Coming soon!**
|
||||
212
docs/tutorials/writing-to-page.md
Normal file
212
docs/tutorials/writing-to-page.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# How to write content to the page
|
||||
|
||||
When creating your PyScript application, you will want to write content to the page. This tutorial will explore the different methods you can use to write content to the page and their differences.
|
||||
|
||||
## Development setup
|
||||
|
||||
To get started, we will create an `index.html` file, import PyScript and start building on top of it.
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>Writing to the page</title>
|
||||
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Writing content to an element
|
||||
|
||||
Let's first see how we can write content to an element on the page. We will start by creating a `div` element with an `id` of `manual-write`, then create a `py-script` tag that, upon a click of a button, will write 'Hello World' to the `div` element.
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>Writing to the page</title>
|
||||
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="manual-write"></div>
|
||||
<button py-click="write_to_page()" id="manual">Say Hello</button>
|
||||
|
||||
<py-script>
|
||||
def write_to_page():
|
||||
manual_div = Element("manual-write")
|
||||
manual_div.element.innerText = "Hello World"
|
||||
</py-script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```{note}
|
||||
When using `py-click` you must supply an `id` to the element you want to use as the trigger.
|
||||
```
|
||||
|
||||
We can now open our `index.html` file and click the button. You will see that "Hello World" will appear in the `div` element. You could also write HTML using `manual_div.element.innerHTML` instead of `innerText`. For example:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>Writing to the page</title>
|
||||
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="manual-write"></div>
|
||||
<button py-click="write_to_page()" id="manual">Say Hello</button>
|
||||
|
||||
<py-script>
|
||||
def write_to_page():
|
||||
manual_div = Element("manual-write")
|
||||
manual_div.element.innerHTML = "<p><b>Hello World</b></p>"
|
||||
</py-script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Writing content with the `display` API
|
||||
|
||||
The `display` API is a simple way to write content to the page. Not only does it allow you to write content to the page, but it also allows you to display a range of different content types such as images, markdown, svgs, json, etc.
|
||||
|
||||
Using the' display' API, let's reuse our previous example and write "Hello World" to the page.
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>Writing to the page</title>
|
||||
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="manual-write"></div>
|
||||
<button py-click="write_to_page()" id="manual">Say Hello</button>
|
||||
<div id="display-write"></div>
|
||||
<button py-click="display_to_div()" id="display">Say Things!</button>
|
||||
|
||||
<py-script>
|
||||
def write_to_page():
|
||||
manual_div = Element("manual-write")
|
||||
manual_div.element.innerHTML = "<p><b>Hello World</b></p>"
|
||||
|
||||
def display_to_div():
|
||||
display("I display things!", target="display-write")
|
||||
</py-script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```{note}
|
||||
When using the `display` API, you must specify the `target` parameter to tell PyScript where to write the content. If you do not use this parameter, an error will be thrown.
|
||||
```
|
||||
|
||||
You may be interested in reading more about the `display` API in the [Display API](../reference/api/display) section of the documentation.
|
||||
|
||||
## Printing to the page
|
||||
|
||||
We couldn't have a tutorial on writing to the page without mentioning the `print` function. The `print` function is a simple way to write content to the page, that any Python developer will be familiar with. When you use the `print` function, the content will be written to the page in a `py-terminal` element.
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>Writing to the page</title>
|
||||
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="manual-write"></div>
|
||||
<button py-click="write_to_page()" id="manual">Say Hello</button>
|
||||
<div id="display-write"></div>
|
||||
<button py-click="display_to_div()" id="display">Say Things!</button>
|
||||
<button py-click="print_to_page()" id="print">Print Things!</button>
|
||||
|
||||
<py-script>
|
||||
def write_to_page():
|
||||
manual_div = Element("manual-write")
|
||||
manual_div.element.innerHTML = "<p><b>Hello World</b></p>"
|
||||
|
||||
def display_to_div():
|
||||
display("I display things!", target="display-write")
|
||||
|
||||
def print_to_page():
|
||||
print("I print things!")
|
||||
</py-script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
You may be surprised to see that when you click the "Print Things!" button, the content is written below the rest of the elements on the page in a black canvas. This is because the `print` function writes content to the page in a `py-terminal` element. You can read more about the `py-terminal` element in the [Terminal Element](../reference/plugins/py-terminal) section of the documentation.
|
||||
|
||||
PyScript comes with the `py-terminal` plugin by default and any `stdout` or `stderr` content will be shown in this element. We can be explicit about where we want the terminal to be shown by adding the `<py-terminal>` tag to our HTML.
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>Writing to the page</title>
|
||||
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="manual-write"></div>
|
||||
<button py-click="write_to_page()" id="manual">Say Hello</button>
|
||||
<div id="display-write"></div>
|
||||
<button py-click="display_to_div()" id="display">Say Things!</button>
|
||||
<div>
|
||||
<py-terminal>
|
||||
</div>
|
||||
<button py-click="print_to_page()" id="print">Print Things!</button>
|
||||
|
||||
<py-script>
|
||||
def write_to_page():
|
||||
manual_div = Element("manual-write")
|
||||
manual_div.element.innerHTML = "<p><b>Hello World</b></p>"
|
||||
|
||||
def display_to_div():
|
||||
display("I display things!", target="display-write")
|
||||
|
||||
def print_to_page():
|
||||
print("I print things!")
|
||||
</py-script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
@@ -3,58 +3,152 @@
|
||||
<title>Altair</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="icon" type="image/x-icon" href="./favicon.png">
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<py-env>
|
||||
- altair
|
||||
- pandas
|
||||
- vega_datasets
|
||||
</py-env>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<link rel="stylesheet" href="./assets/prism/prism.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<script defer src="./assets/prism/prism.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="altair" style="width: 100%; height: 100%"></div>
|
||||
<py-script output="altair">
|
||||
import altair as alt
|
||||
from vega_datasets import data
|
||||
<nav class="navbar" style="background-color: #000000;">
|
||||
<div class="app-header">
|
||||
<a href="/">
|
||||
<img src="./logo.png" class="logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">Altair</a>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<div id="altair"></div>
|
||||
<py-config>
|
||||
packages = [
|
||||
"altair",
|
||||
"pandas",
|
||||
"vega_datasets"
|
||||
]
|
||||
</py-config>
|
||||
<py-script>
|
||||
import altair as alt
|
||||
from vega_datasets import data
|
||||
|
||||
source = data.movies.url
|
||||
source = data.movies.url
|
||||
|
||||
pts = alt.selection(type="single", encodings=['x'])
|
||||
pts = alt.selection(type="single", encodings=['x'])
|
||||
|
||||
rect = alt.Chart(data.movies.url).mark_rect().encode(
|
||||
alt.X('IMDB_Rating:Q', bin=True),
|
||||
alt.Y('Rotten_Tomatoes_Rating:Q', bin=True),
|
||||
alt.Color('count()',
|
||||
scale=alt.Scale(scheme='greenblue'),
|
||||
legend=alt.Legend(title='Total Records')
|
||||
)
|
||||
)
|
||||
rect = alt.Chart(data.movies.url).mark_rect().encode(
|
||||
alt.X('IMDB_Rating:Q', bin=True),
|
||||
alt.Y('Rotten_Tomatoes_Rating:Q', bin=True),
|
||||
alt.Color('count()',
|
||||
scale=alt.Scale(scheme='greenblue'),
|
||||
legend=alt.Legend(title='Total Records')
|
||||
)
|
||||
)
|
||||
|
||||
circ = rect.mark_point().encode(
|
||||
alt.ColorValue('grey'),
|
||||
alt.Size('count()',
|
||||
legend=alt.Legend(title='Records in Selection')
|
||||
)
|
||||
).transform_filter(
|
||||
pts
|
||||
)
|
||||
circ = rect.mark_point().encode(
|
||||
alt.ColorValue('grey'),
|
||||
alt.Size('count()',
|
||||
legend=alt.Legend(title='Records in Selection')
|
||||
)
|
||||
).transform_filter(
|
||||
pts
|
||||
)
|
||||
|
||||
bar = alt.Chart(source).mark_bar().encode(
|
||||
x='Major_Genre:N',
|
||||
y='count()',
|
||||
color=alt.condition(pts, alt.ColorValue("steelblue"), alt.ColorValue("grey"))
|
||||
).properties(
|
||||
width=550,
|
||||
height=200
|
||||
).add_selection(pts)
|
||||
bar = alt.Chart(source).mark_bar().encode(
|
||||
x='Major_Genre:N',
|
||||
y='count()',
|
||||
color=alt.condition(pts, alt.ColorValue("steelblue"), alt.ColorValue("grey"))
|
||||
).properties(
|
||||
width=550,
|
||||
height=200
|
||||
).add_selection(pts)
|
||||
|
||||
alt.vconcat(
|
||||
rect + circ,
|
||||
bar
|
||||
).resolve_legend(
|
||||
color="independent",
|
||||
size="independent"
|
||||
)
|
||||
</py-script>
|
||||
</body>
|
||||
display(alt.vconcat(
|
||||
rect + circ,
|
||||
bar
|
||||
).resolve_legend(
|
||||
color="independent",
|
||||
size="independent"
|
||||
), target="altair")
|
||||
</py-script>
|
||||
</section>
|
||||
<section class="code">
|
||||
<div id="view-code-button" role="button" aria-pressed="false" tabindex="0">View Code</div>
|
||||
<div id="code-section" class="code-section-hidden">
|
||||
<p>index.html</p>
|
||||
<pre class="prism-code language-html">
|
||||
<code class="language-html">
|
||||
<py-config>
|
||||
packages = [
|
||||
"altair",
|
||||
"pandas",
|
||||
"vega_datasets"
|
||||
]
|
||||
</py-config>
|
||||
<py-script>
|
||||
import altair as alt
|
||||
from vega_datasets import data
|
||||
|
||||
source = data.movies.url
|
||||
|
||||
pts = alt.selection(type="single", encodings=['x'])
|
||||
|
||||
rect = alt.Chart(data.movies.url).mark_rect().encode(
|
||||
alt.X('IMDB_Rating:Q', bin=True),
|
||||
alt.Y('Rotten_Tomatoes_Rating:Q', bin=True),
|
||||
alt.Color('count()',
|
||||
scale=alt.Scale(scheme='greenblue'),
|
||||
legend=alt.Legend(title='Total Records')
|
||||
)
|
||||
)
|
||||
|
||||
circ = rect.mark_point().encode(
|
||||
alt.ColorValue('grey'),
|
||||
alt.Size('count()',
|
||||
legend=alt.Legend(title='Records in Selection')
|
||||
)
|
||||
).transform_filter(
|
||||
pts
|
||||
)
|
||||
|
||||
bar = alt.Chart(source).mark_bar().encode(
|
||||
x='Major_Genre:N',
|
||||
y='count()',
|
||||
color=alt.condition(pts, alt.ColorValue("steelblue"), alt.ColorValue("grey"))
|
||||
).properties(
|
||||
width=550,
|
||||
height=200
|
||||
).add_selection(pts)
|
||||
|
||||
display(alt.vconcat(
|
||||
rect + circ,
|
||||
bar
|
||||
).resolve_legend(
|
||||
color="independent",
|
||||
size="independent"
|
||||
), target="altair")
|
||||
</py-script>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
<script>
|
||||
const viewCodeButton = document.getElementById("view-code-button");
|
||||
const codeSection = document.getElementById("code-section");
|
||||
const handleClick = () => {
|
||||
if (codeSection.classList.contains("code-section-hidden")) {
|
||||
codeSection.classList.remove("code-section-hidden");
|
||||
codeSection.classList.add("code-section-visible");
|
||||
} else {
|
||||
codeSection.classList.remove("code-section-visible");
|
||||
codeSection.classList.add("code-section-hidden");
|
||||
}
|
||||
}
|
||||
viewCodeButton.addEventListener("click", handleClick)
|
||||
viewCodeButton.addEventListener("keydown", (e) => {
|
||||
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
||||
handleClick();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -3,18 +3,116 @@
|
||||
<title>Antigravity</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="icon" type="image/x-icon" href="./favicon.png">
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<link rel="stylesheet" href="./assets/prism/prism.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<script defer src="./assets/prism/prism.js"></script>
|
||||
</head>
|
||||
<py-env>
|
||||
- paths:
|
||||
- ./antigravity.py
|
||||
</py-env>
|
||||
<body>
|
||||
<nav class="navbar" style="background-color: #000000;">
|
||||
<div class="app-header">
|
||||
<a href="/">
|
||||
<img src="./logo.png" class="logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">Antigravity</a>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
files = ["./antigravity.py"]
|
||||
</py-config>
|
||||
<b>Based on xkcd: antigravity https://xkcd.com/353/.</b>
|
||||
<py-script>
|
||||
import antigravity
|
||||
antigravity.fly()
|
||||
</py-script>
|
||||
</section>
|
||||
<section class="code">
|
||||
<div id="view-code-button" role="button" aria-pressed="false" tabindex="0">View Code</div>
|
||||
<div id="code-section" class="code-section-hidden">
|
||||
<p>index.html</p>
|
||||
<pre class="prism-code language-html">
|
||||
<code class="language-html">
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
files = ["./antigravity.py"]
|
||||
</py-config>
|
||||
<py-script>
|
||||
import antigravity
|
||||
antigravity.fly()
|
||||
</py-script>
|
||||
</code>
|
||||
</pre>
|
||||
<p>antigravity.py</p>
|
||||
<pre class="prism-code language-python">
|
||||
<code class="language-python">
|
||||
import random
|
||||
import sys
|
||||
|
||||
from js import DOMParser, document, setInterval
|
||||
from pyodide.ffi import create_proxy
|
||||
from pyodide.http import open_url
|
||||
|
||||
|
||||
class Antigravity:
|
||||
|
||||
url = "./antigravity.svg"
|
||||
|
||||
def __init__(self, target=None, interval=10, append=True, fly=False):
|
||||
target = target or sys.stdout._out
|
||||
self.target = (
|
||||
document.getElementById(target) if isinstance(target, str) else target
|
||||
)
|
||||
doc = DOMParser.new().parseFromString(
|
||||
open_url(self.url).read(), "image/svg+xml"
|
||||
)
|
||||
self.node = doc.documentElement
|
||||
if append:
|
||||
self.target.append(self.node)
|
||||
else:
|
||||
self.target.replaceChildren(self.node)
|
||||
self.xoffset, self.yoffset = 0, 0
|
||||
self.interval = interval
|
||||
if fly:
|
||||
self.fly()
|
||||
|
||||
def fly(self):
|
||||
setInterval(create_proxy(self.move), self.interval)
|
||||
|
||||
def move(self):
|
||||
char = self.node.getElementsByTagName("g")[1]
|
||||
char.setAttribute("transform", f"translate({self.xoffset}, {-self.yoffset})")
|
||||
self.xoffset += random.normalvariate(0, 1) / 20
|
||||
if self.yoffset < 50:
|
||||
self.yoffset += 0.1
|
||||
else:
|
||||
self.yoffset += random.normalvariate(0, 1) / 20
|
||||
|
||||
_auto = Antigravity(append=True)
|
||||
fly = _auto.fly
|
||||
</code>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
<script>
|
||||
const viewCodeButton = document.getElementById("view-code-button");
|
||||
const codeSection = document.getElementById("code-section");
|
||||
const handleClick = () => {
|
||||
if (codeSection.classList.contains("code-section-hidden")) {
|
||||
codeSection.classList.remove("code-section-hidden");
|
||||
codeSection.classList.add("code-section-visible");
|
||||
} else {
|
||||
codeSection.classList.remove("code-section-visible");
|
||||
codeSection.classList.add("code-section-hidden");
|
||||
}
|
||||
}
|
||||
viewCodeButton.addEventListener("click", handleClick)
|
||||
viewCodeButton.addEventListener("keydown", (e) => {
|
||||
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
||||
handleClick();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import random
|
||||
import sys
|
||||
|
||||
from js import DOMParser, document, setInterval
|
||||
from pyodide import create_proxy
|
||||
from pyodide.ffi import create_proxy
|
||||
from pyodide.http import open_url
|
||||
|
||||
|
||||
@@ -11,9 +10,10 @@ class Antigravity:
|
||||
url = "./antigravity.svg"
|
||||
|
||||
def __init__(self, target=None, interval=10, append=True, fly=False):
|
||||
target = target or sys.stdout._out
|
||||
self.target = (
|
||||
document.getElementById(target) if isinstance(target, str) else target
|
||||
document.getElementById(target)
|
||||
if isinstance(target, str)
|
||||
else document.body
|
||||
)
|
||||
doc = DOMParser.new().parseFromString(
|
||||
open_url(self.url).read(), "image/svg+xml"
|
||||
|
||||
90
examples/assets/css/examples.css
Normal file
90
examples/assets/css/examples.css
Normal file
@@ -0,0 +1,90 @@
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.pyscript {
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
.code {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
z-index: 9998;
|
||||
top: 7rem;
|
||||
}
|
||||
|
||||
@media (max-width: 1300px) {
|
||||
.code:has(> .code-section-visible) {
|
||||
width: 90%;
|
||||
/* Absolute position is messing up the layout on small screens */
|
||||
right: 70px;
|
||||
}
|
||||
}
|
||||
|
||||
.code-section-hidden {
|
||||
width: 0px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.code-section-visible {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
background-color: rgb(45 46 53 / 90%);
|
||||
padding: 1rem;
|
||||
border-radius: 10px 0px 0px 10px;
|
||||
color: #c6c6c8;
|
||||
}
|
||||
.code-section-visible p{
|
||||
margin: 0;
|
||||
font-style: italic;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.language-html, .language-python {
|
||||
float: left;
|
||||
}
|
||||
|
||||
#view-code-button {
|
||||
writing-mode: tb-rl;
|
||||
text-orientation: sideways-right;
|
||||
background-color: #1D1D22;
|
||||
color: white;
|
||||
padding: 0.5rem;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
height: 81px;
|
||||
}
|
||||
|
||||
nav {
|
||||
position: sticky;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.logo {
|
||||
padding-right: 10px;
|
||||
font-size: 28px;
|
||||
height: 30px;
|
||||
max-width: inherit;
|
||||
}
|
||||
|
||||
.title {
|
||||
text-decoration: none;
|
||||
text-decoration-line: none;
|
||||
text-decoration-style: initial;
|
||||
text-decoration-color: initial;
|
||||
font-weight: 400;
|
||||
font-size: 1.5em;
|
||||
line-height: 2em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.app-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
}
|
||||
|
||||
.example h2{
|
||||
color: #000000;
|
||||
/* color: #000000; */
|
||||
font-family: "Inconsolata", monospace;
|
||||
font-size: 2.25rem;
|
||||
margin-bottom: 1rem;
|
||||
@@ -11,10 +11,14 @@
|
||||
|
||||
.card {
|
||||
height: 15rem;
|
||||
background-color: #FFFFFF;
|
||||
background-color: var(--color-secondary);
|
||||
padding: 1rem;
|
||||
-webkit-box-shadow: var(--card-shadow);
|
||||
box-shadow: var(--card-shadow);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.card:hover, .card:hover a, .card:hover a:visited, .card:hover h2 {
|
||||
background-color: var(--color-primary);
|
||||
color: #1D1D22
|
||||
}
|
||||
|
||||
.card a h2 {
|
||||
@@ -24,6 +28,17 @@
|
||||
font-size: 2.25rem;
|
||||
}
|
||||
|
||||
.card a p {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
a .card {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
@@ -31,6 +46,10 @@
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.card-content a, .card-content a:visited {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.container-card {
|
||||
max-width: 1500px;
|
||||
margin: 0 auto;
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
@import "./variables.css";
|
||||
@import "./reset.css";
|
||||
|
||||
|
||||
body {
|
||||
background: #2D2E35 url('https://assets.anaconda.com/production/Content/1650828148240.png?w=3240&auto=compress%2Cformat&fit=crop&dm=1650828161&s=c558dc55e0ed1f8419a892e842a5728f') repeat-x center bottom / 250px;
|
||||
background-attachment: fixed;
|
||||
overflow-x: hidden;
|
||||
color: var(--text-color)
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1510px;
|
||||
margin: auto;
|
||||
@@ -9,7 +17,6 @@
|
||||
}
|
||||
|
||||
.title-main {
|
||||
color: #000000;
|
||||
font-size: 4.25rem;
|
||||
font-family: "Inconsolata", monospace;
|
||||
text-align: center;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
:root {
|
||||
--color-primary: #2563eb;
|
||||
--color-primary: #FDA703;
|
||||
--color-secondary: #1D1D22;
|
||||
--text-color: white;
|
||||
--card-shadow: 0px 5px 11px 0px rgb(0 0 0 / 15%);
|
||||
}
|
||||
|
||||
3
examples/assets/prism/prism.css
Normal file
3
examples/assets/prism/prism.css
Normal file
@@ -0,0 +1,3 @@
|
||||
/* PrismJS 1.29.0
|
||||
https://prismjs.com/download.html#themes=prism-okaidia&languages=markup+clike+javascript+python */
|
||||
code[class*=language-],pre[class*=language-]{color:#f8f8f2;background:0 0;text-shadow:0 1px rgba(0,0,0,.3);font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto;border-radius:.3em}:not(pre)>code[class*=language-],pre[class*=language-]{background:#272822}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#8292a2}.token.punctuation{color:#f8f8f2}.token.namespace{opacity:.7}.token.constant,.token.deleted,.token.property,.token.symbol,.token.tag{color:#f92672}.token.boolean,.token.number{color:#ae81ff}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#a6e22e}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url,.token.variable{color:#f8f8f2}.token.atrule,.token.attr-value,.token.class-name,.token.function{color:#e6db74}.token.keyword{color:#66d9ef}.token.important,.token.regex{color:#fd971f}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}
|
||||
7
examples/assets/prism/prism.js
Normal file
7
examples/assets/prism/prism.js
Normal file
File diff suppressed because one or more lines are too long
26
examples/await/await0.html
Normal file
26
examples/await/await0.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>Async Await BLOCKING LOOP Pyscript Twice</title>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<py-script>
|
||||
import js
|
||||
import asyncio
|
||||
for i in range(3):
|
||||
js.console.log('A', i)
|
||||
await asyncio.sleep(0.1)
|
||||
</py-script>
|
||||
<py-script>
|
||||
import js
|
||||
import asyncio
|
||||
for i in range(3):
|
||||
js.console.log('B', i)
|
||||
await asyncio.sleep(0.1)
|
||||
</py-script>
|
||||
</body>
|
||||
</html>
|
||||
40
examples/await/await1.html
Normal file
40
examples/await/await1.html
Normal file
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>Async Await BLOCKING LOOP Pyscript Twice</title>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
Pyscript - FIRST ASYNC WITH INVOKED LOOP BLOCKING AWAIT AT SAME LEVEL AS LOOP Pyscript writing to console.log:
|
||||
<py-script>
|
||||
import js
|
||||
import asyncio
|
||||
|
||||
async def asyncCallLoop1():
|
||||
for i in range(3):
|
||||
js.console.log('A', i)
|
||||
await asyncio.sleep(2)
|
||||
|
||||
asyncCallLoop1()
|
||||
</py-script>
|
||||
</div>
|
||||
<div>
|
||||
Pyscript - SECOND ASYNC WITH INVOKED LOOP BLOCKING AWAIT AT SAME LEVEL AS LOOP Pyscript writing to console.log:
|
||||
<py-script>
|
||||
import js
|
||||
import asyncio
|
||||
|
||||
async def asyncCallLoop2():
|
||||
for i in range(3):
|
||||
js.console.log('B', i)
|
||||
await asyncio.sleep(2)
|
||||
|
||||
asyncCallLoop2()
|
||||
</py-script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
37
examples/await/await2.html
Normal file
37
examples/await/await2.html
Normal file
@@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>Async Await BLOCKING LOOP Pyscript Twice</title>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
Pyscript - FIRST ASYNC WITH INVOKED LOOP BLOCKING AWAIT AT SAME LEVEL AS LOOP Pyscript writing to console.log:
|
||||
<py-script>
|
||||
import js
|
||||
import asyncio
|
||||
|
||||
async def asyncCallLoop1():
|
||||
for i in range(3):
|
||||
js.console.log('A', i)
|
||||
await asyncio.sleep(2)
|
||||
|
||||
asyncCallLoop1()
|
||||
</py-script>
|
||||
</div>
|
||||
<div>
|
||||
Pyscript - SECOND ASYNC WITH TOP-LEVEL LOOP BLOCKING AWAIT AT SAME LEVEL AS LOOP Pyscript writing to console.log:
|
||||
<py-script>
|
||||
import js
|
||||
import asyncio
|
||||
|
||||
for i in range(3):
|
||||
js.console.log('B', i)
|
||||
await asyncio.sleep(2)
|
||||
</py-script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
40
examples/await/await3.html
Normal file
40
examples/await/await3.html
Normal file
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>Async Await NON-BLOCKING Pyscript Twice</title>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
Pyscript - FIRST ASYNC WITH NON-BLOCKING AWAIT AT ONE LEVEL LOWER THAN LOOP Pyscript writing to console.log:
|
||||
<py-script>
|
||||
import js
|
||||
import asyncio
|
||||
|
||||
async def asyncCall1():
|
||||
await asyncio.sleep(2)
|
||||
|
||||
for i in range(3):
|
||||
js.console.log('A', i)
|
||||
asyncCall1()
|
||||
</py-script>
|
||||
</div>
|
||||
<div>
|
||||
Pyscript - SECOND ASYNC WITH NON-BLOCKING AWAIT AT ONE LEVEL LOWER THAN LOOP Pyscript writing to console.log:
|
||||
<py-script>
|
||||
import js
|
||||
import asyncio
|
||||
|
||||
async def asyncCall2():
|
||||
await asyncio.sleep(2)
|
||||
|
||||
for i in range(3):
|
||||
js.console.log('B', i)
|
||||
asyncCall2()
|
||||
</py-script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
34
examples/await/await4.html
Normal file
34
examples/await/await4.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>Async Await BLOCKING LOOP Pyscript Twice</title>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
Pyscript - FIRST ASYNC WITH TOP-LEVEL LOOP BLOCKING AWAIT AT SAME LEVEL AS LOOP Pyscript writing to console.log:
|
||||
<py-script>
|
||||
import js
|
||||
import asyncio
|
||||
|
||||
for i in range(3):
|
||||
js.console.log('A', i)
|
||||
await asyncio.sleep(2)
|
||||
</py-script>
|
||||
</div>
|
||||
<div>
|
||||
Pyscript - SECOND ASYNC WITH TOP-LEVEL LOOP BLOCKING AWAIT AT SAME LEVEL AS LOOP Pyscript writing to console.log:
|
||||
<py-script>
|
||||
import js
|
||||
import asyncio
|
||||
|
||||
for i in range(3):
|
||||
js.console.log('B', i)
|
||||
await asyncio.sleep(2)
|
||||
</py-script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
19
examples/await/await5.html
Normal file
19
examples/await/await5.html
Normal file
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>Async Await BLOCKING LOOP Pyscript Twice</title>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<py-script>
|
||||
import asyncio
|
||||
from itertools import count
|
||||
for i in count():
|
||||
print(f"Count: {i}")
|
||||
await asyncio.sleep(1)
|
||||
</py-script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -11,19 +11,32 @@
|
||||
<script type="text/javascript">
|
||||
Bokeh.set_log_level("info");
|
||||
</script>
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<link rel="stylesheet" href="./assets/prism/prism.css" />
|
||||
<script defer src="./assets/prism/prism.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<py-env>
|
||||
- bokeh
|
||||
- numpy
|
||||
</py-env>
|
||||
<h1>Bokeh Example</h1>
|
||||
<nav class="navbar" style="background-color: #000000;">
|
||||
<div class="app-header">
|
||||
<a href="/">
|
||||
<img src="./logo.png" class="logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">Bokeh Example</a>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<div id="myplot"></div>
|
||||
|
||||
<py-config>
|
||||
packages = [
|
||||
"bokeh",
|
||||
"numpy"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
<py-script id="main">
|
||||
import json
|
||||
import pyodide
|
||||
@@ -43,6 +56,60 @@ p_json = json.dumps(json_item(p, "myplot"))
|
||||
|
||||
Bokeh.embed.embed_item(JSON.parse(p_json))
|
||||
</py-script>
|
||||
</section>
|
||||
<section class="code">
|
||||
<div id="view-code-button" role="button" aria-pressed="false" tabindex="0">View Code</div>
|
||||
<div id="code-section" class="code-section-hidden">
|
||||
<p>index.html</p>
|
||||
<pre class="prism-code language-html">
|
||||
<code class="language-html">
|
||||
<py-config>
|
||||
packages = [
|
||||
"bokeh",
|
||||
"numpy"
|
||||
]
|
||||
</py-config>
|
||||
<py-script id="main">
|
||||
import json
|
||||
import pyodide
|
||||
|
||||
</body>
|
||||
from js import Bokeh, console, JSON
|
||||
|
||||
from bokeh.embed import json_item
|
||||
from bokeh.plotting import figure
|
||||
from bokeh.resources import CDN
|
||||
|
||||
# create a new plot with default tools, using figure
|
||||
p = figure(plot_width=400, plot_height=400)
|
||||
|
||||
# add a circle renderer with x and y coordinates, size, color, and alpha
|
||||
p.circle([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], size=15, line_color="navy", fill_color="orange", fill_alpha=0.5)
|
||||
p_json = json.dumps(json_item(p, "myplot"))
|
||||
|
||||
Bokeh.embed.embed_item(JSON.parse(p_json))
|
||||
</py-script>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
<script>
|
||||
const viewCodeButton = document.getElementById("view-code-button");
|
||||
const codeSection = document.getElementById("code-section");
|
||||
const handleClick = () => {
|
||||
if (codeSection.classList.contains("code-section-hidden")) {
|
||||
codeSection.classList.remove("code-section-hidden");
|
||||
codeSection.classList.add("code-section-visible");
|
||||
} else {
|
||||
codeSection.classList.remove("code-section-visible");
|
||||
codeSection.classList.add("code-section-hidden");
|
||||
}
|
||||
}
|
||||
viewCodeButton.addEventListener("click", handleClick)
|
||||
viewCodeButton.addEventListener("keydown", (e) => {
|
||||
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
||||
handleClick();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -7,23 +7,37 @@
|
||||
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.2.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.2.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-2.4.2.min.js"></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<link rel="stylesheet" href="./assets/prism/prism.css" />
|
||||
<script defer src="./assets/prism/prism.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
Bokeh.set_log_level("info");
|
||||
</script>
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<py-env>
|
||||
- bokeh
|
||||
- numpy
|
||||
</py-env>
|
||||
<nav class="navbar" style="background-color: #000000;">
|
||||
<div class="app-header">
|
||||
<a href="/">
|
||||
<img src="./logo.png" class="logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">Bokeh Example</a>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<h1>Bokeh Example</h1>
|
||||
<div id="myplot"></div>
|
||||
|
||||
<py-config>
|
||||
packages = [
|
||||
"bokeh",
|
||||
"numpy"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
<py-script id="main">
|
||||
import asyncio
|
||||
import json
|
||||
@@ -71,17 +85,17 @@ def _link_docs(pydoc, jsdoc):
|
||||
if getattr(event, 'setter_id', None) is not None:
|
||||
return
|
||||
events = [event]
|
||||
json_patch = jsdoc.create_json_patch_string(pyodide.to_js(events))
|
||||
json_patch = jsdoc.create_json_patch_string(pyodide.ffi.to_js(events))
|
||||
pydoc.apply_json_patch(json.loads(json_patch))
|
||||
|
||||
jsdoc.on_change(pyodide.create_proxy(jssync), pyodide.to_js(False))
|
||||
jsdoc.on_change(pyodide.ffi.create_proxy(jssync), pyodide.ffi.to_js(False))
|
||||
|
||||
def pysync(event):
|
||||
json_patch, buffers = process_document_events([event], use_buffers=True)
|
||||
buffer_map = {}
|
||||
for (ref, buffer) in buffers:
|
||||
buffer_map[ref['id']] = buffer
|
||||
jsdoc.apply_json_patch(JSON.parse(json_patch), pyodide.to_js(buffer_map), setter_id='js')
|
||||
jsdoc.apply_json_patch(JSON.parse(json_patch), pyodide.ffi.to_js(buffer_map), setter_id='js')
|
||||
|
||||
pydoc.on_change(pysync)
|
||||
|
||||
@@ -91,8 +105,112 @@ async def show(plot, target):
|
||||
jsdoc = views[0].model.document
|
||||
_link_docs(pydoc, jsdoc)
|
||||
|
||||
await show(row, 'myplot')
|
||||
asyncio.ensure_future(show(row, 'myplot'))
|
||||
</py-script>
|
||||
</section>
|
||||
<section class="code">
|
||||
<div id="view-code-button" role="button" aria-pressed="false" tabindex="0">View Code</div>
|
||||
<div id="code-section" class="code-section-hidden">
|
||||
<p>index.html</p>
|
||||
<pre class="prism-code language-html">
|
||||
<code class="language-html">
|
||||
<py-config>
|
||||
packages = [
|
||||
"bokeh",
|
||||
"numpy"
|
||||
]
|
||||
</py-config>
|
||||
<py-script id="main">
|
||||
import asyncio
|
||||
import json
|
||||
import pyodide
|
||||
|
||||
</body>
|
||||
from js import Bokeh, console, JSON
|
||||
|
||||
from bokeh import __version__
|
||||
from bokeh.document import Document
|
||||
from bokeh.embed.util import OutputDocumentFor, standalone_docs_json_and_render_items
|
||||
from bokeh.models import Slider, Div
|
||||
from bokeh.layouts import Row
|
||||
from bokeh.protocol.messages.patch_doc import process_document_events
|
||||
|
||||
# create a new plot with default tools, using figure
|
||||
p = Slider(start=0.1, end=10, value=1, step=.1, title="Amplitude")
|
||||
div = Div(text=f'Amplitude is: {p.value}')
|
||||
|
||||
def callback(attr, old, new):
|
||||
div.text = f'Amplitude is: {new}'
|
||||
|
||||
p.on_change('value', callback)
|
||||
|
||||
row = Row(children=[p, div])
|
||||
|
||||
def doc_json(model, target):
|
||||
with OutputDocumentFor([model]) as doc:
|
||||
doc.title = ""
|
||||
docs_json, _ = standalone_docs_json_and_render_items(
|
||||
[model], suppress_callback_warning=True
|
||||
)
|
||||
|
||||
doc_json = list(docs_json.values())[0]
|
||||
root_id = doc_json['roots']['root_ids'][0]
|
||||
|
||||
return doc, json.dumps(dict(
|
||||
target_id = target,
|
||||
root_id = root_id,
|
||||
doc = doc_json,
|
||||
version = __version__,
|
||||
))
|
||||
|
||||
def _link_docs(pydoc, jsdoc):
|
||||
def jssync(event):
|
||||
if getattr(event, 'setter_id', None) is not None:
|
||||
return
|
||||
events = [event]
|
||||
json_patch = jsdoc.create_json_patch_string(pyodide.ffi.to_js(events))
|
||||
pydoc.apply_json_patch(json.loads(json_patch))
|
||||
|
||||
jsdoc.on_change(pyodide.ffi.create_proxy(jssync), pyodide.ffi.to_js(False))
|
||||
|
||||
def pysync(event):
|
||||
json_patch, buffers = process_document_events([event], use_buffers=True)
|
||||
buffer_map = {}
|
||||
for (ref, buffer) in buffers:
|
||||
buffer_map[ref['id']] = buffer
|
||||
jsdoc.apply_json_patch(JSON.parse(json_patch), pyodide.ffi.to_js(buffer_map), setter_id='js')
|
||||
|
||||
pydoc.on_change(pysync)
|
||||
|
||||
async def show(plot, target):
|
||||
pydoc, model_json = doc_json(plot, target)
|
||||
views = await Bokeh.embed.embed_item(JSON.parse(model_json))
|
||||
jsdoc = views[0].model.document
|
||||
_link_docs(pydoc, jsdoc)
|
||||
|
||||
asyncio.ensure_future(show(row, 'myplot'))
|
||||
</py-script>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
<script>
|
||||
const viewCodeButton = document.getElementById("view-code-button");
|
||||
const codeSection = document.getElementById("code-section");
|
||||
const handleClick = () => {
|
||||
if (codeSection.classList.contains("code-section-hidden")) {
|
||||
codeSection.classList.remove("code-section-hidden");
|
||||
codeSection.classList.add("code-section-visible");
|
||||
} else {
|
||||
codeSection.classList.remove("code-section-visible");
|
||||
codeSection.classList.add("code-section-hidden");
|
||||
}
|
||||
}
|
||||
viewCodeButton.addEventListener("click", handleClick)
|
||||
viewCodeButton.addEventListener("keydown", (e) => {
|
||||
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
||||
handleClick();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
|
||||
174
examples/d3.html
174
examples/d3.html
@@ -4,9 +4,12 @@
|
||||
<meta charset="utf-8">
|
||||
<link rel="icon" type="image/x-icon" href="./favicon.png">
|
||||
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<link rel="stylesheet" href="./assets/prism/prism.css" />
|
||||
<script defer src="./assets/prism/prism.js"></script>
|
||||
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||||
<style>
|
||||
.loading {
|
||||
display: inline-block;
|
||||
@@ -27,6 +30,15 @@
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar" style="background-color: #000000;">
|
||||
<div class="app-header">
|
||||
<a href="/">
|
||||
<img src="./logo.png" class="logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">Simple d3 visualization</a>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<b>
|
||||
Based on <i><a href="https://observablehq.com/@d3/learn-d3-shapes?collection=@d3/learn-d3>">Learn D3: Shapes</a></i> tutorial.
|
||||
</b>
|
||||
@@ -45,16 +57,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"d3": "https://cdn.skypack.dev/d3@7"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="module">
|
||||
import * as d3 from "https://cdn.skypack.dev/d3@7";
|
||||
|
||||
const fruits = [
|
||||
{name: "🍊", count: 21},
|
||||
@@ -109,8 +112,9 @@ for (const d of data) {
|
||||
</script>
|
||||
|
||||
<py-script>
|
||||
from pyodide import create_proxy, to_js
|
||||
import d3
|
||||
import js
|
||||
from pyodide.ffi import create_proxy, to_js
|
||||
d3 = js.d3
|
||||
|
||||
fruits = [
|
||||
dict(name="🍊", count=21),
|
||||
@@ -165,6 +169,146 @@ for d in data:
|
||||
.attr("dy", "1.3em")
|
||||
.text(d_py["value"]))
|
||||
</py-script>
|
||||
</section>
|
||||
<section class="code">
|
||||
<div id="view-code-button" role="button" aria-pressed="false" tabindex="0">View Code</div>
|
||||
<div id="code-section" class="code-section-hidden">
|
||||
<p>index.html</p>
|
||||
<pre class="prism-code language-html">
|
||||
<code class="language-html">
|
||||
<script>
|
||||
import * as d3 from "https://cdn.skypack.dev/pin/d3@v7.6.1-1Q0NZ0WZnbYeSjDusJT3/mode=imports,min/optimized/d3.js";
|
||||
|
||||
</body>
|
||||
const fruits = [
|
||||
{name: "🍊", count: 21},
|
||||
{name: "🍇", count: 13},
|
||||
{name: "🍏", count: 8},
|
||||
{name: "🍌", count: 5},
|
||||
{name: "🍐", count: 3},
|
||||
{name: "🍋", count: 2},
|
||||
{name: "🍎", count: 1},
|
||||
{name: "🍉", count: 1},
|
||||
]
|
||||
|
||||
const fn = (d) => d.count
|
||||
const data = d3.pie().value(fn)(fruits)
|
||||
|
||||
const arc = d3.arc()
|
||||
.innerRadius(210)
|
||||
.outerRadius(310)
|
||||
.padRadius(300)
|
||||
.padAngle(2 / 300)
|
||||
.cornerRadius(8)
|
||||
|
||||
const js = d3.select("#js")
|
||||
js.select(".loading").remove()
|
||||
|
||||
const svg = js
|
||||
.append("svg")
|
||||
.attr("viewBox", "-320 -320 640 640")
|
||||
.attr("width", "400")
|
||||
.attr("height", "400")
|
||||
|
||||
for (const d of data) {
|
||||
svg.append("path")
|
||||
.style("fill", "steelblue")
|
||||
.attr("d", arc(d))
|
||||
|
||||
const text = svg.append("text")
|
||||
.style("fill", "white")
|
||||
.attr("transform", `translate(${arc.centroid(d).join(",")})`)
|
||||
.attr("text-anchor", "middle")
|
||||
|
||||
text.append("tspan")
|
||||
.style("font-size", "24")
|
||||
.attr("x", "0").text(d.data.name)
|
||||
|
||||
text.append("tspan")
|
||||
.style("font-size", "18")
|
||||
.attr("x", "0")
|
||||
.attr("dy", "1.3em")
|
||||
.text(d.value)
|
||||
}
|
||||
</script>
|
||||
<py-script>
|
||||
from pyodide.ffi import create_proxy, to_js
|
||||
import d3
|
||||
|
||||
fruits = [
|
||||
dict(name="🍊", count=21),
|
||||
dict(name="🍇", count=13),
|
||||
dict(name="🍏", count=8),
|
||||
dict(name="🍌", count=5),
|
||||
dict(name="🍐", count=3),
|
||||
dict(name="🍋", count=2),
|
||||
dict(name="🍎", count=1),
|
||||
dict(name="🍉", count=1),
|
||||
]
|
||||
|
||||
fn = create_proxy(lambda d, *_: d["count"])
|
||||
data = d3.pie().value(fn)(to_js(fruits))
|
||||
|
||||
arc = (d3.arc()
|
||||
.innerRadius(210)
|
||||
.outerRadius(310)
|
||||
.padRadius(300)
|
||||
.padAngle(2 / 300)
|
||||
.cornerRadius(8))
|
||||
|
||||
py = d3.select("#py")
|
||||
py.select(".loading").remove()
|
||||
|
||||
svg = (py
|
||||
.append("svg")
|
||||
.attr("viewBox", "-320 -320 640 640")
|
||||
.attr("width", "400")
|
||||
.attr("height", "400"))
|
||||
|
||||
for d in data:
|
||||
d_py = d.to_py()
|
||||
|
||||
(svg.append("path")
|
||||
.style("fill", "steelblue")
|
||||
.attr("d", arc(d)))
|
||||
|
||||
text = (svg.append("text")
|
||||
.style("fill", "white")
|
||||
.attr("transform", f"translate({arc.centroid(d).join(',')})")
|
||||
.attr("text-anchor", "middle"))
|
||||
|
||||
(text.append("tspan")
|
||||
.style("font-size", "24")
|
||||
.attr("x", "0")
|
||||
.text(d_py["data"]["name"]))
|
||||
|
||||
(text.append("tspan")
|
||||
.style("font-size", "18")
|
||||
.attr("x", "0")
|
||||
.attr("dy", "1.3em")
|
||||
.text(d_py["value"]))
|
||||
</py-script>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
<script>
|
||||
const viewCodeButton = document.getElementById("view-code-button");
|
||||
const codeSection = document.getElementById("code-section");
|
||||
const handleClick = () => {
|
||||
if (codeSection.classList.contains("code-section-hidden")) {
|
||||
codeSection.classList.remove("code-section-hidden");
|
||||
codeSection.classList.add("code-section-visible");
|
||||
} else {
|
||||
codeSection.classList.remove("code-section-visible");
|
||||
codeSection.classList.add("code-section-hidden");
|
||||
}
|
||||
}
|
||||
viewCodeButton.addEventListener("click", handleClick)
|
||||
viewCodeButton.addEventListener("keydown", (e) => {
|
||||
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
||||
handleClick();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -3,16 +3,32 @@
|
||||
<title>Folium</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="icon" type="image/x-icon" href="./favicon.png">
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<py-env>
|
||||
- folium
|
||||
- pandas
|
||||
</py-env>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<link rel="stylesheet" href="./assets/prism/prism.css" />
|
||||
<script defer src="./assets/prism/prism.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="folium" style="width: 100%; height: 100%"></div>
|
||||
<py-script output="folium">
|
||||
<nav class="navbar" style="background-color: #000000;">
|
||||
<div class="app-header">
|
||||
<a href="/">
|
||||
<img src="./logo.png" class="logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">Folium</a>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<div id="folium"></div>
|
||||
|
||||
<py-config>
|
||||
packages = [
|
||||
"folium",
|
||||
"pandas"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
<py-script>
|
||||
import folium
|
||||
import json
|
||||
import pandas as pd
|
||||
@@ -43,7 +59,78 @@ folium.Choropleth(
|
||||
|
||||
folium.LayerControl().add_to(m)
|
||||
|
||||
m
|
||||
display(m, target="folium")
|
||||
</py-script>
|
||||
</body>
|
||||
</section>
|
||||
<section class="code">
|
||||
<div id="view-code-button" role="button" aria-pressed="false" tabindex="0">View Code</div>
|
||||
<div id="code-section" class="code-section-hidden">
|
||||
<p>index.html</p>
|
||||
<pre class="prism-code language-html">
|
||||
<code class="language-html">
|
||||
|
||||
<div id="folium"></div>
|
||||
<py-config>
|
||||
packages = [
|
||||
"folium",
|
||||
"pandas"
|
||||
]
|
||||
</py-config>
|
||||
<py-script>
|
||||
import folium
|
||||
import json
|
||||
import pandas as pd
|
||||
|
||||
from pyodide.http import open_url
|
||||
|
||||
url = (
|
||||
"https://raw.githubusercontent.com/python-visualization/folium/master/examples/data"
|
||||
)
|
||||
state_geo = f"{url}/us-states.json"
|
||||
state_unemployment = f"{url}/US_Unemployment_Oct2012.csv"
|
||||
state_data = pd.read_csv(open_url(state_unemployment))
|
||||
geo_json = json.loads(open_url(state_geo).read())
|
||||
|
||||
m = folium.Map(location=[48, -102], zoom_start=3)
|
||||
|
||||
folium.Choropleth(
|
||||
geo_data=geo_json,
|
||||
name="choropleth",
|
||||
data=state_data,
|
||||
columns=["State", "Unemployment"],
|
||||
key_on="feature.id",
|
||||
fill_color="YlGn",
|
||||
fill_opacity=0.7,
|
||||
line_opacity=0.2,
|
||||
legend_name="Unemployment Rate (%)",
|
||||
).add_to(m)
|
||||
|
||||
folium.LayerControl().add_to(m)
|
||||
|
||||
display(m, target="folium")
|
||||
</py-script>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
<script>
|
||||
const viewCodeButton = document.getElementById("view-code-button");
|
||||
const codeSection = document.getElementById("code-section");
|
||||
const handleClick = () => {
|
||||
if (codeSection.classList.contains("code-section-hidden")) {
|
||||
codeSection.classList.remove("code-section-hidden");
|
||||
codeSection.classList.add("code-section-visible");
|
||||
} else {
|
||||
codeSection.classList.remove("code-section-visible");
|
||||
codeSection.classList.add("code-section-hidden");
|
||||
}
|
||||
}
|
||||
viewCodeButton.addEventListener("click", handleClick)
|
||||
viewCodeButton.addEventListener("keydown", (e) => {
|
||||
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
||||
handleClick();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>Svelte app</title>
|
||||
<title>Say Hello</title>
|
||||
|
||||
<link rel="icon" type="image/png" href="../favicon.png" />
|
||||
<link rel="stylesheet" href="../https://pyscript.net/alpha/pyscript.css" />
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
|
||||
<script defer src="../https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -106,7 +106,7 @@ pyscript.run_until_complete(start())
|
||||
</py-script>
|
||||
|
||||
<div class="mb10">
|
||||
<button id="trackbutton" class="bx--btn bx--btn--secondary" type="button" pys-onClick="toggle_video">
|
||||
<button id="trackbutton" class="bx--btn bx--btn--secondary" type="button" py-onClick="toggle_video()">
|
||||
Toggle Video
|
||||
</button>
|
||||
<button id="nextimagebutton" class="mt10 bx--btn bx--btn--secondary" type="button" disabled>
|
||||
|
||||
@@ -7,18 +7,65 @@
|
||||
<title>PyScript Hello World</title>
|
||||
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<link rel="stylesheet" href="./assets/prism/prism.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<script defer src="./assets/prism/prism.js"></script>
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
Hello world! <br>
|
||||
This is the current date and time, as computed by Python:
|
||||
<py-script>
|
||||
from datetime import datetime
|
||||
now = datetime.now()
|
||||
now.strftime("%m/%d/%Y, %H:%M:%S")
|
||||
</py-script>
|
||||
<nav class="navbar" style="background-color: #000000;">
|
||||
<div class="app-header">
|
||||
<a href="/">
|
||||
<img src="./logo.png" class="logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">Hello World</a>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
Hello world! <br>
|
||||
This is the current date and time, as computed by Python:
|
||||
<py-script>
|
||||
from datetime import datetime
|
||||
now = datetime.now()
|
||||
display(now.strftime("%m/%d/%Y, %H:%M:%S"))
|
||||
</py-script>
|
||||
</section>
|
||||
<section class="code">
|
||||
<div id="view-code-button" role="button" aria-pressed="false" tabindex="0">View Code</div>
|
||||
<div id="code-section" class="code-section-hidden">
|
||||
<p>index.html</p>
|
||||
<pre class="prism-code language-html">
|
||||
<code class="language-html">
|
||||
<py-script>
|
||||
from datetime import datetime
|
||||
now = datetime.now()
|
||||
display(now.strftime("%m/%d/%Y, %H:%M:%S"))
|
||||
</py-script>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
<script>
|
||||
const viewCodeButton = document.getElementById("view-code-button");
|
||||
const codeSection = document.getElementById("code-section");
|
||||
const handleClick = () => {
|
||||
if (codeSection.classList.contains("code-section-hidden")) {
|
||||
codeSection.classList.remove("code-section-hidden");
|
||||
codeSection.classList.add("code-section-visible");
|
||||
} else {
|
||||
codeSection.classList.remove("code-section-visible");
|
||||
codeSection.classList.add("code-section-hidden");
|
||||
}
|
||||
}
|
||||
viewCodeButton.addEventListener("click", handleClick)
|
||||
viewCodeButton.addEventListener("keydown", (e) => {
|
||||
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
||||
handleClick();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<h2>REPL</h2>
|
||||
</a>
|
||||
<p>
|
||||
A Python REPL (Read Eval Print Loop).
|
||||
A Python REPL (Read Eval Print Loop)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -53,7 +53,7 @@
|
||||
<h2>REPL2</h2>
|
||||
</a>
|
||||
<p>
|
||||
A Python REPL (Read Eval Print Loop) with slightly better formatting..
|
||||
A Python REPL (Read Eval Print Loop) with slightly better formatting
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -64,7 +64,7 @@
|
||||
<h2>TODO App</h2>
|
||||
</a>
|
||||
<p>
|
||||
Demo showing how would a Simple TODO App would look like in PyScript</code> tag
|
||||
Simple TODO App
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -75,7 +75,7 @@
|
||||
<h2>PyScript Native TODO App</h2>
|
||||
</a>
|
||||
<p>
|
||||
Demo showing how would a Simple TODO App would look like in PyScript</code> tag
|
||||
Simple TODO App using <code><py-list></code>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -91,7 +91,7 @@
|
||||
<h2>Matplotlib</h2>
|
||||
</a>
|
||||
<p>
|
||||
Demonstrates rendering <a href="https://matplotlib.org/" target="_blank">Matplotlib</a> figure as output of the py-script tag
|
||||
Demonstrates rendering a <a href="https://matplotlib.org/" target="_blank">Matplotlib</a> figure as output of the py-script tag
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -104,7 +104,7 @@
|
||||
</h2>
|
||||
</a>
|
||||
<p>
|
||||
Demonstrates rendering <a href="https://altair-viz.github.io/" target="_blank">Altair</a> plot as output of the py-script tag
|
||||
Demonstrates rendering a <a href="https://altair-viz.github.io/" target="_blank">Altair</a> plot as output of the py-script tag
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -117,7 +117,7 @@
|
||||
</h2>
|
||||
</a>
|
||||
<p>
|
||||
Demonstrates rendering
|
||||
Demonstrates rendering a
|
||||
<a href="https://python-visualization.github.io/folium/" target="_blank">Folium</a>
|
||||
map as output of the py-script tag
|
||||
</p>
|
||||
@@ -254,20 +254,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<a href="./toga/freedom.html" target="_blank">
|
||||
<h2 class="text-2xl font-bold text-blue-600">
|
||||
Freedom Units!
|
||||
</h2>
|
||||
</a>
|
||||
<p>
|
||||
A <a href="https://beeware.org/project/projects/libraries/toga/" target="_blank">Toga</a>
|
||||
application (a Fahrenheit to Celsius converter), rendered as a Single Page App
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<a href="./numpy_canvas_fractals.html" target="_blank">
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<title>Svelte app</title>
|
||||
<title>Mario</title>
|
||||
|
||||
<link rel="icon" type="image/png" href="../favicon.png" />
|
||||
<link rel="stylesheet" href="../https://pyscript.net/alpha/pyscript.css" />
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
|
||||
<script defer src="../https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -72,7 +72,7 @@ def toggle_video(evt):
|
||||
|
||||
async def start_video():
|
||||
global isVideo
|
||||
update_note.write("Inside sstart video")
|
||||
update_note.write("Inside start video")
|
||||
status = await handTrack.startVideo(video.element)
|
||||
console.log("video started", status)
|
||||
if status:
|
||||
@@ -131,7 +131,7 @@ pyscript.run_until_complete(start())
|
||||
|
||||
<div class="mb10">
|
||||
<p>Use < > to move, ↓ to crouch and x to jump. If video is enabled, say hi to jump as well! </p>
|
||||
<button id="trackbutton" class="bx--btn bx--btn--secondary" type="button" pys-onClick="toggle_video">
|
||||
<button id="trackbutton" class="bx--btn bx--btn--secondary" type="button" py-onClick="toggle_video()">
|
||||
Start Video
|
||||
</button>
|
||||
<div id="update-note" py-mount class="updatenote mt10">loading model ..</div>
|
||||
|
||||
26
examples/markdown-plugin.html
Normal file
26
examples/markdown-plugin.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<title>PyMarkdown</title>
|
||||
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<link rel="stylesheet" href="../build/pyscript.css" />
|
||||
|
||||
<script defer src="../build/pyscript.js"></script>
|
||||
|
||||
<py-config>
|
||||
packages = [
|
||||
"markdown"
|
||||
]
|
||||
plugins = [
|
||||
"../build/plugins/python/py_markdown.py"
|
||||
]
|
||||
</py-config>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<py-md>#Hello world!</py-md>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,15 +3,31 @@
|
||||
<title>Matplotlib</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="icon" type="image/x-icon" href="./favicon.png">
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<py-env>
|
||||
- matplotlib
|
||||
</py-env>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<link rel="stylesheet" href="./assets/prism/prism.css" />
|
||||
<script defer src="./assets/prism/prism.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar" style="background-color: #000000;">
|
||||
<div class="app-header">
|
||||
<a href="/">
|
||||
<img src="./logo.png" class="logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">Matplotlib</a>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<div id="mpl"></div>
|
||||
<py-script output="mpl">
|
||||
|
||||
<py-config>
|
||||
packages = [
|
||||
"matplotlib"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
<py-script>
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.tri as tri
|
||||
import numpy as np
|
||||
@@ -44,7 +60,79 @@ tpc = ax1.tripcolor(triang, z, shading='flat')
|
||||
fig1.colorbar(tpc)
|
||||
ax1.set_title('tripcolor of Delaunay triangulation, flat shading')
|
||||
|
||||
fig1
|
||||
display(fig1, target="mpl")
|
||||
</py-script>
|
||||
</section>
|
||||
<section class="code">
|
||||
<div id="view-code-button" role="button" aria-pressed="false" tabindex="0">View Code</div>
|
||||
<div id="code-section" class="code-section-hidden">
|
||||
<p>index.html</p>
|
||||
<pre class="prism-code language-html">
|
||||
<code class="language-html">
|
||||
<div id="mpl"></div>
|
||||
<py-config>
|
||||
packages = [
|
||||
"matplotlib",
|
||||
]
|
||||
</py-config>
|
||||
<py-script>
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.tri as tri
|
||||
import numpy as np
|
||||
|
||||
# First create the x and y coordinates of the points.
|
||||
n_angles = 36
|
||||
n_radii = 8
|
||||
min_radius = 0.25
|
||||
radii = np.linspace(min_radius, 0.95, n_radii)
|
||||
|
||||
angles = np.linspace(0, 2 * np.pi, n_angles, endpoint=False)
|
||||
angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
|
||||
angles[:, 1::2] += np.pi / n_angles
|
||||
|
||||
x = (radii * np.cos(angles)).flatten()
|
||||
y = (radii * np.sin(angles)).flatten()
|
||||
z = (np.cos(radii) * np.cos(3 * angles)).flatten()
|
||||
|
||||
# Create the Triangulation; no triangles so Delaunay triangulation created.
|
||||
triang = tri.Triangulation(x, y)
|
||||
|
||||
# Mask off unwanted triangles.
|
||||
triang.set_mask(np.hypot(x[triang.triangles].mean(axis=1),
|
||||
y[triang.triangles].mean(axis=1))
|
||||
< min_radius)
|
||||
|
||||
fig1, ax1 = plt.subplots()
|
||||
ax1.set_aspect('equal')
|
||||
tpc = ax1.tripcolor(triang, z, shading='flat')
|
||||
fig1.colorbar(tpc)
|
||||
ax1.set_title('tripcolor of Delaunay triangulation, flat shading')
|
||||
|
||||
display(fig1, target="mpl")
|
||||
</py-script>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
<script>
|
||||
const viewCodeButton = document.getElementById("view-code-button");
|
||||
const codeSection = document.getElementById("code-section");
|
||||
const handleClick = () => {
|
||||
if (codeSection.classList.contains("code-section-hidden")) {
|
||||
codeSection.classList.remove("code-section-hidden");
|
||||
codeSection.classList.add("code-section-visible");
|
||||
} else {
|
||||
codeSection.classList.remove("code-section-visible");
|
||||
codeSection.classList.add("code-section-hidden");
|
||||
}
|
||||
}
|
||||
viewCodeButton.addEventListener("click", handleClick)
|
||||
viewCodeButton.addEventListener("keydown", (e) => {
|
||||
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
||||
handleClick();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -2,16 +2,20 @@
|
||||
|
||||
<head>
|
||||
<link rel="icon" type="image/x-icon" href="./favicon.png">
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<py-env>
|
||||
- numpy
|
||||
- networkx
|
||||
- matplotlib
|
||||
</py-env>
|
||||
|
||||
<py-config>
|
||||
packages = [
|
||||
"numpy",
|
||||
"networkx",
|
||||
"matplotlib"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
<py-script>
|
||||
import numpy as np
|
||||
import networkx as nx
|
||||
|
||||
@@ -6,20 +6,23 @@
|
||||
|
||||
<title>micrograd</title>
|
||||
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
|
||||
<py-env>
|
||||
- micrograd
|
||||
- numpy
|
||||
- matplotlib
|
||||
</py-env>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
|
||||
</head>
|
||||
|
||||
<body style="padding-top: 20px; padding-right: 20px; padding-bottom: 20px; padding-left: 20px">
|
||||
<h1>Micrograd - A tiny Autograd engine (with a bite! :))</h1><br>
|
||||
|
||||
<py-config>
|
||||
packages = [
|
||||
"micrograd",
|
||||
"numpy",
|
||||
"matplotlib"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
<div>
|
||||
<p>
|
||||
<a href="https://github.com/karpathy/micrograd">Micrograd</a> is a tiny Autograd engine created
|
||||
@@ -49,7 +52,7 @@
|
||||
<div id="python-status">Python is currently starting. Please wait...</div>
|
||||
</p>
|
||||
<p>
|
||||
<button id="run-all-button" class="btn btn-primary" type="submit" pys-onClick="run_all_micrograd_demo">Run All</button><br>
|
||||
<button id="run-all-button" class="btn btn-primary" type="submit" py-onClick="run_all_micrograd_demo()">Run All</button><br>
|
||||
<py-script src="/micrograd_ai.py"></py-script>
|
||||
<div id="micrograd-run-all-print-div"></div><br>
|
||||
<div id="micrograd-run-all-fig1-div"></div>
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
<title>Visualization of Mandelbrot, Julia and Newton sets with NumPy and HTML5 canvas</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="icon" type="image/x-icon" href="./favicon.png">
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<link rel="stylesheet" href="./assets/prism/prism.css" />
|
||||
<script defer src="./assets/prism/prism.js"></script>
|
||||
<style>
|
||||
.loading {
|
||||
display: inline-block;
|
||||
@@ -30,8 +32,15 @@
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<b>
|
||||
</b>
|
||||
<nav class="navbar" style="background-color: #000000;">
|
||||
<div class="app-header">
|
||||
<a href="/">
|
||||
<img src="./logo.png" class="logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">Fractals with NumPy and canvas</a>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<div style="display: flex; flex-direction: column; gap: 1em; width: 600px">
|
||||
<div id="mandelbrot">
|
||||
<div style="text-align: center">Mandelbrot set</div>
|
||||
@@ -78,16 +87,25 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<py-env>
|
||||
- numpy
|
||||
- sympy
|
||||
- paths:
|
||||
- ./palettes.py
|
||||
- ./fractals.py
|
||||
</py-env>
|
||||
<py-config type="json">
|
||||
{
|
||||
"packages": [
|
||||
"numpy",
|
||||
"sympy"
|
||||
],
|
||||
"fetch": [
|
||||
{
|
||||
"files": [
|
||||
"./palettes.py",
|
||||
"./fractals.py"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
</py-config>
|
||||
|
||||
<py-script>
|
||||
from pyodide import to_js, create_proxy
|
||||
from pyodide.ffi import to_js, create_proxy
|
||||
|
||||
import numpy as np
|
||||
import sympy
|
||||
@@ -307,12 +325,290 @@ canvas.addEventListener("mousemove", create_proxy(mousemove))
|
||||
|
||||
import asyncio
|
||||
|
||||
_ = await asyncio.gather(
|
||||
draw_mandelbrot(),
|
||||
draw_julia(),
|
||||
draw_newton(),
|
||||
)
|
||||
</py-script>
|
||||
async def main():
|
||||
_ = await asyncio.gather(
|
||||
draw_mandelbrot(),
|
||||
draw_julia(),
|
||||
draw_newton(),
|
||||
)
|
||||
|
||||
</body>
|
||||
asyncio.ensure_future(main())
|
||||
</py-script>
|
||||
</section>
|
||||
<section class="code">
|
||||
<div id="view-code-button" role="button" aria-pressed="false" tabindex="0">View Code</div>
|
||||
<div id="code-section" class="code-section-hidden">
|
||||
<p>index.html</p>
|
||||
<pre class="prism-code language-html">
|
||||
<code class="language-html">
|
||||
<py-config type="json">
|
||||
{
|
||||
"packages": [
|
||||
"numpy",
|
||||
"sympy"
|
||||
],
|
||||
"fetch": [
|
||||
{
|
||||
"files": [
|
||||
"./palettes.py",
|
||||
"./fractals.py"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
</py-config>
|
||||
<py-script>
|
||||
from pyodide.ffi import to_js, create_proxy
|
||||
|
||||
import numpy as np
|
||||
import sympy
|
||||
|
||||
from palettes import Magma256
|
||||
from fractals import mandelbrot, julia, newton
|
||||
|
||||
from js import (
|
||||
console,
|
||||
document,
|
||||
devicePixelRatio,
|
||||
ImageData,
|
||||
Uint8ClampedArray,
|
||||
CanvasRenderingContext2D as Context2d,
|
||||
requestAnimationFrame,
|
||||
)
|
||||
|
||||
def prepare_canvas(width: int, height: int, canvas: Element) -> Context2d:
|
||||
ctx = canvas.getContext("2d")
|
||||
|
||||
canvas.style.width = f"{width}px"
|
||||
canvas.style.height = f"{height}px"
|
||||
|
||||
canvas.width = width
|
||||
canvas.height = height
|
||||
|
||||
ctx.clearRect(0, 0, width, height)
|
||||
|
||||
return ctx
|
||||
|
||||
def color_map(array: np.array, palette: np.array) -> np.array:
|
||||
size, _ = palette.shape
|
||||
index = (array/array.max()*(size - 1)).round().astype("uint8")
|
||||
|
||||
width, height = array.shape
|
||||
image = np.full((width, height, 4), 0xff, dtype=np.uint8)
|
||||
image[:, :, :3] = palette[index]
|
||||
|
||||
return image
|
||||
|
||||
def draw_image(ctx: Context2d, image: np.array) -> None:
|
||||
data = Uint8ClampedArray.new(to_js(image.tobytes()))
|
||||
width, height, _ = image.shape
|
||||
image_data = ImageData.new(data, width, height)
|
||||
ctx.putImageData(image_data, 0, 0)
|
||||
|
||||
width, height = 600, 600
|
||||
|
||||
async def draw_mandelbrot() -> None:
|
||||
spinner = document.querySelector("#mandelbrot .loading")
|
||||
canvas = document.querySelector("#mandelbrot canvas")
|
||||
|
||||
spinner.style.display = ""
|
||||
canvas.style.display = "none"
|
||||
|
||||
ctx = prepare_canvas(width, height, canvas)
|
||||
|
||||
console.log("Computing Mandelbrot set ...")
|
||||
console.time("mandelbrot")
|
||||
iters = mandelbrot(width, height)
|
||||
console.timeEnd("mandelbrot")
|
||||
|
||||
image = color_map(iters, Magma256)
|
||||
draw_image(ctx, image)
|
||||
|
||||
spinner.style.display = "none"
|
||||
canvas.style.display = "block"
|
||||
|
||||
async def draw_julia() -> None:
|
||||
spinner = document.querySelector("#julia .loading")
|
||||
canvas = document.querySelector("#julia canvas")
|
||||
|
||||
spinner.style.display = ""
|
||||
canvas.style.display = "none"
|
||||
|
||||
ctx = prepare_canvas(width, height, canvas)
|
||||
|
||||
console.log("Computing Julia set ...")
|
||||
console.time("julia")
|
||||
iters = julia(width, height)
|
||||
console.timeEnd("julia")
|
||||
|
||||
image = color_map(iters, Magma256)
|
||||
draw_image(ctx, image)
|
||||
|
||||
spinner.style.display = "none"
|
||||
canvas.style.display = "block"
|
||||
|
||||
def ranges():
|
||||
x0_in = document.querySelector("#x0")
|
||||
x1_in = document.querySelector("#x1")
|
||||
y0_in = document.querySelector("#y0")
|
||||
y1_in = document.querySelector("#y1")
|
||||
|
||||
xr = (float(x0_in.value), float(x1_in.value))
|
||||
yr = (float(y0_in.value), float(y1_in.value))
|
||||
|
||||
return xr, yr
|
||||
|
||||
current_image = None
|
||||
async def draw_newton() -> None:
|
||||
spinner = document.querySelector("#newton .loading")
|
||||
canvas = document.querySelector("#newton canvas")
|
||||
|
||||
spinner.style.display = ""
|
||||
canvas.style.display = "none"
|
||||
|
||||
ctx = prepare_canvas(width, height, canvas)
|
||||
|
||||
console.log("Computing Newton set ...")
|
||||
|
||||
poly_in = document.querySelector("#poly")
|
||||
coef_in = document.querySelector("#coef")
|
||||
conv_in = document.querySelector("#conv")
|
||||
iter_in = document.querySelector("#iter")
|
||||
|
||||
xr, yr = ranges()
|
||||
|
||||
# z**3 - 1
|
||||
# z**8 + 15*z**4 - 16
|
||||
# z**3 - 2*z + 2
|
||||
|
||||
expr = sympy.parse_expr(poly_in.value)
|
||||
coeffs = [ complex(c) for c in reversed(sympy.Poly(expr, sympy.Symbol("z")).all_coeffs()) ]
|
||||
poly = np.polynomial.Polynomial(coeffs)
|
||||
|
||||
coef = complex(sympy.parse_expr(coef_in.value))
|
||||
|
||||
console.time("newton")
|
||||
iters, roots = newton(width, height, p=poly, a=coef, xr=xr, yr=yr)
|
||||
console.timeEnd("newton")
|
||||
|
||||
if conv_in.checked:
|
||||
n = poly.degree() + 1
|
||||
k = int(len(Magma256)/n)
|
||||
|
||||
colors = Magma256[::k, :][:n]
|
||||
colors[0, :] = [255, 0, 0] # red: no convergence
|
||||
|
||||
image = color_map(roots, colors)
|
||||
else:
|
||||
image = color_map(iters, Magma256)
|
||||
|
||||
global current_image
|
||||
current_image = image
|
||||
draw_image(ctx, image)
|
||||
|
||||
spinner.style.display = "none"
|
||||
canvas.style.display = "block"
|
||||
|
||||
handler = create_proxy(lambda _event: draw_newton())
|
||||
document.querySelector("#newton fieldset").addEventListener("change", handler)
|
||||
|
||||
canvas = document.querySelector("#newton canvas")
|
||||
|
||||
is_selecting = False
|
||||
init_sx, init_sy = None, None
|
||||
sx, sy = None, None
|
||||
async def mousemove(event):
|
||||
global is_selecting
|
||||
global init_sx
|
||||
global init_sy
|
||||
global sx
|
||||
global sy
|
||||
|
||||
def invert(sx, source_range, target_range):
|
||||
source_start, source_end = source_range
|
||||
target_start, target_end = target_range
|
||||
factor = (target_end - target_start)/(source_end - source_start)
|
||||
offset = -(factor * source_start) + target_start
|
||||
return (sx - offset) / factor
|
||||
|
||||
bds = canvas.getBoundingClientRect()
|
||||
event_sx, event_sy = event.clientX - bds.x, event.clientY - bds.y
|
||||
|
||||
ctx = canvas.getContext("2d")
|
||||
|
||||
pressed = event.buttons == 1
|
||||
if is_selecting:
|
||||
if not pressed:
|
||||
xr, yr = ranges()
|
||||
|
||||
x0 = invert(init_sx, xr, (0, width))
|
||||
x1 = invert(sx, xr, (0, width))
|
||||
y0 = invert(init_sy, yr, (0, height))
|
||||
y1 = invert(sy, yr, (0, height))
|
||||
|
||||
document.querySelector("#x0").value = x0
|
||||
document.querySelector("#x1").value = x1
|
||||
document.querySelector("#y0").value = y0
|
||||
document.querySelector("#y1").value = y1
|
||||
|
||||
is_selecting = False
|
||||
init_sx, init_sy = None, None
|
||||
sx, sy = init_sx, init_sy
|
||||
|
||||
await draw_newton()
|
||||
else:
|
||||
ctx.save()
|
||||
ctx.clearRect(0, 0, width, height)
|
||||
draw_image(ctx, current_image)
|
||||
sx, sy = event_sx, event_sy
|
||||
ctx.beginPath()
|
||||
ctx.rect(init_sx, init_sy, sx - init_sx, sy - init_sy)
|
||||
ctx.fillStyle = "rgba(255, 255, 255, 0.4)"
|
||||
ctx.strokeStyle = "rgba(255, 255, 255, 1.0)"
|
||||
ctx.fill()
|
||||
ctx.stroke()
|
||||
ctx.restore()
|
||||
else:
|
||||
if pressed:
|
||||
is_selecting = True
|
||||
init_sx, init_sy = event_sx, event_sy
|
||||
sx, sy = init_sx, init_sy
|
||||
|
||||
canvas.addEventListener("mousemove", create_proxy(mousemove))
|
||||
|
||||
import asyncio
|
||||
|
||||
async def main():
|
||||
_ = await asyncio.gather(
|
||||
draw_mandelbrot(),
|
||||
draw_julia(),
|
||||
draw_newton(),
|
||||
)
|
||||
|
||||
asyncio.ensure_future(main())
|
||||
</py-script>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
<script>
|
||||
const viewCodeButton = document.getElementById("view-code-button");
|
||||
const codeSection = document.getElementById("code-section");
|
||||
const handleClick = () => {
|
||||
if (codeSection.classList.contains("code-section-hidden")) {
|
||||
codeSection.classList.remove("code-section-hidden");
|
||||
codeSection.classList.add("code-section-visible");
|
||||
} else {
|
||||
codeSection.classList.remove("code-section-visible");
|
||||
codeSection.classList.add("code-section-hidden");
|
||||
}
|
||||
}
|
||||
viewCodeButton.addEventListener("click", handleClick)
|
||||
viewCodeButton.addEventListener("keydown", (e) => {
|
||||
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
||||
handleClick();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -3,21 +3,36 @@
|
||||
<title>Panel Example</title>
|
||||
<meta charset="iso-8859-1">
|
||||
<link rel="icon" type="image/x-icon" href="./favicon.png">
|
||||
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.4.2.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.2.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.2.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@holoviz/panel@0.13.1/dist/panel.min.js"></script>
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.4.3.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.3.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.3.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@holoviz/panel@0.14.1/dist/panel.min.js"></script>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<link rel="stylesheet" href="./assets/prism/prism.css" />
|
||||
<script defer src="./assets/prism/prism.js"></script>
|
||||
</head>
|
||||
<py-env>
|
||||
- bokeh
|
||||
- numpy
|
||||
- panel==0.13.1
|
||||
</py-env>
|
||||
<body>
|
||||
<h1>Panel Example</h1>
|
||||
<nav class="navbar" style="background-color: #000000;">
|
||||
<div class="app-header">
|
||||
<a href="/">
|
||||
<img src="./logo.png" class="logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">Panel Example</a>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<div id="simple_app"></div>
|
||||
|
||||
<py-config>
|
||||
packages = [
|
||||
"bokeh",
|
||||
"numpy",
|
||||
"panel==0.14.1"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
<py-script>
|
||||
import panel as pn
|
||||
|
||||
@@ -28,5 +43,52 @@
|
||||
|
||||
pn.Row(slider, pn.bind(callback, slider)).servable(target='simple_app');
|
||||
</py-script>
|
||||
</body>
|
||||
</section>
|
||||
<section class="code">
|
||||
<div id="view-code-button" role="button" aria-pressed="false" tabindex="0">View Code</div>
|
||||
<div id="code-section" class="code-section-hidden">
|
||||
<p>index.html</p>
|
||||
<pre class="prism-code language-html">
|
||||
<code class="language-html">
|
||||
<py-config>
|
||||
packages = [
|
||||
"bokeh",
|
||||
"numpy",
|
||||
"panel==0.13.1"
|
||||
]
|
||||
</py-config>
|
||||
<py-script>
|
||||
import panel as pn
|
||||
|
||||
slider = pn.widgets.FloatSlider(start=0, end=10, name='Amplitude')
|
||||
|
||||
def callback(new):
|
||||
return f'Amplitude is: {new}'
|
||||
|
||||
pn.Row(slider, pn.bind(callback, slider)).servable(target='simple_app');
|
||||
</py-script>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
<script>
|
||||
const viewCodeButton = document.getElementById("view-code-button");
|
||||
const codeSection = document.getElementById("code-section");
|
||||
const handleClick = () => {
|
||||
if (codeSection.classList.contains("code-section-hidden")) {
|
||||
codeSection.classList.remove("code-section-hidden");
|
||||
codeSection.classList.add("code-section-visible");
|
||||
} else {
|
||||
codeSection.classList.remove("code-section-visible");
|
||||
codeSection.classList.add("code-section-hidden");
|
||||
}
|
||||
}
|
||||
viewCodeButton.addEventListener("click", handleClick)
|
||||
viewCodeButton.addEventListener("keydown", (e) => {
|
||||
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
||||
handleClick();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -38,44 +38,45 @@
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<link rel="stylesheet" href="./assets/prism/prism.css" />
|
||||
<script defer src="./assets/prism/prism.js"></script>
|
||||
</head>
|
||||
|
||||
<py-env>
|
||||
- bokeh
|
||||
- numpy
|
||||
- pandas
|
||||
- panel==0.13.1
|
||||
</py-env>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid d-flex flex-column vh-100 overflow-hidden" id="container">
|
||||
<nav class="navbar navbar-expand-md navbar-dark sticky-top shadow" id="header" style="background-color: #000000;">
|
||||
<button type="button" class="navbar-toggle collapsed" id="sidebarCollapse">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="app-header">
|
||||
<a class="navbar-brand app-logo" href="/">
|
||||
<img src="./logo.png" class="app-logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">Panel DeckGL NYC Taxi Demo</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<nav class="navbar" style="background-color: #000000;">
|
||||
<div class="app-header">
|
||||
<a href="/">
|
||||
<img src="./logo.png" class="logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">Panel DeckGL NYC Taxi Demo</a>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<div class="d-flex flex-nowrap" id="content">
|
||||
<div class="sidenav" id="sidebar">
|
||||
<ul class="nav flex-column" >
|
||||
<div class="bk-root" id="widgets"></div>
|
||||
<py-repl id="my-repl" auto-generate="true"> </py-repl>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col mh-100" style="padding: 0">
|
||||
<div class="bk-root" id="plot"></div>
|
||||
</div>
|
||||
<div class="sidenav" id="sidebar">
|
||||
<ul class="nav flex-column" >
|
||||
<div class="bk-root" id="widgets"></div>
|
||||
<py-repl id="my-repl" auto-generate="true"> </py-repl>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col mh-100" style="padding: 0">
|
||||
<div class="bk-root" id="plot"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<py-config>
|
||||
packages = [
|
||||
"bokeh",
|
||||
"numpy",
|
||||
"pandas",
|
||||
"panel==0.13.1"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
<py-script>
|
||||
import panel as pn
|
||||
import pandas as pd
|
||||
@@ -217,5 +218,185 @@
|
||||
app.deck_gl.servable(target='plot')
|
||||
controls.servable(target='widgets');
|
||||
</py-script>
|
||||
</section>
|
||||
<section class="code">
|
||||
<div id="view-code-button" role="button" aria-pressed="false" tabindex="0" style="height: 95px;">View Code</div>
|
||||
<div id="code-section" class="code-section-hidden">
|
||||
<p>index.html</p>
|
||||
<pre class="prism-code language-html">
|
||||
<code class="language-html">
|
||||
<py-config>
|
||||
packages = [
|
||||
"bokeh",
|
||||
"numpy",
|
||||
"pandas",
|
||||
"panel==0.13.1"
|
||||
]
|
||||
</py-config>
|
||||
<py-script>
|
||||
import panel as pn
|
||||
import pandas as pd
|
||||
import param
|
||||
|
||||
from pyodide.http import open_url
|
||||
|
||||
MAPBOX_KEY = "pk.eyJ1IjoicGFuZWxvcmciLCJhIjoiY2s1enA3ejhyMWhmZjNobjM1NXhtbWRrMyJ9.B_frQsAVepGIe-HiOJeqvQ"
|
||||
|
||||
class App(param.Parameterized):
|
||||
|
||||
data = param.DataFrame(precedence=-1)
|
||||
|
||||
view = param.DataFrame(precedence=-1)
|
||||
|
||||
arc_view = param.DataFrame(precedence=-1)
|
||||
|
||||
radius = param.Integer(default=50, bounds=(20, 1000))
|
||||
|
||||
elevation = param.Integer(default=10, bounds=(0, 50))
|
||||
|
||||
hour = param.Integer(default=0, bounds=(0, 23))
|
||||
|
||||
speed = param.Integer(default=1, bounds=(0, 10), precedence=-1)
|
||||
|
||||
play = param.Event(label='▷')
|
||||
|
||||
def __init__(self, **params):
|
||||
self.deck_gl = None
|
||||
super().__init__(**params)
|
||||
self.deck_gl = pn.pane.DeckGL(
|
||||
dict(self.spec),
|
||||
mapbox_api_key=MAPBOX_KEY,
|
||||
throttle={'click': 10},
|
||||
sizing_mode='stretch_both',
|
||||
margin=0
|
||||
)
|
||||
self.deck_gl.param.watch(self._update_arc_view, 'click_state')
|
||||
self._playing = False
|
||||
self._cb = pn.state.add_periodic_callback(
|
||||
self._update_hour, 1000//self.speed, start=False
|
||||
)
|
||||
|
||||
@property
|
||||
def spec(self):
|
||||
return {
|
||||
"initialViewState": {
|
||||
"bearing": 0,
|
||||
"latitude": 40.7,
|
||||
"longitude": -73.9,
|
||||
"maxZoom": 15,
|
||||
"minZoom": 5,
|
||||
"pitch": 40.5,
|
||||
"zoom": 11
|
||||
},
|
||||
"layers": [self.hex_layer, self.arc_layer],
|
||||
"mapStyle": "mapbox://styles/mapbox/dark-v9",
|
||||
"views": [
|
||||
{"@@type": "MapView", "controller": True}
|
||||
]
|
||||
}
|
||||
|
||||
@property
|
||||
def hex_layer(self):
|
||||
return {
|
||||
"@@type": "HexagonLayer",
|
||||
"autoHighlight": True,
|
||||
"coverage": 1,
|
||||
"data": self.data if self.view is None else self.view,
|
||||
"elevationRange": [0, 100],
|
||||
"elevationScale": self.elevation,
|
||||
"radius": self.radius,
|
||||
"extruded": True,
|
||||
"getPosition": "@@=[pickup_x, pickup_y]",
|
||||
"id": "8a553b25-ef3a-489c-bbe2-e102d18a3211"
|
||||
}
|
||||
|
||||
@property
|
||||
def arc_layer(self):
|
||||
return {
|
||||
"@@type": "ArcLayer",
|
||||
"id": 'arc-layer',
|
||||
"data": self.arc_view,
|
||||
"pickable": True,
|
||||
"getWidth": 1,
|
||||
"getSourcePosition": "@@=[pickup_x, pickup_y]",
|
||||
"getTargetPosition": "@@=[dropoff_x, dropoff_y]",
|
||||
"getSourceColor": [0, 255, 0, 180],
|
||||
"getTargetColor": [240, 100, 0, 180]
|
||||
}
|
||||
|
||||
def _update_hour(self):
|
||||
self.hour = (self.hour+1) % 24
|
||||
|
||||
@param.depends('view', watch=True)
|
||||
def _update_arc_view(self, event=None):
|
||||
data = self.data if self.view is None else self.view
|
||||
if not self.deck_gl or not self.deck_gl.click_state:
|
||||
self.arc_view = data.iloc[:0]
|
||||
return
|
||||
lon, lat = self.deck_gl.click_state['coordinate']
|
||||
tol = 0.001
|
||||
self.arc_view = data[
|
||||
(df.pickup_x>=float(lon-tol)) &
|
||||
(df.pickup_x<=float(lon+tol)) &
|
||||
(df.pickup_y>=float(lat-tol)) &
|
||||
(df.pickup_y<=float(lat+tol))
|
||||
]
|
||||
|
||||
@param.depends('hour', watch=True, on_init=True)
|
||||
def _update_hourly_view(self):
|
||||
self.view = self.data[self.data.hour==self.hour]
|
||||
|
||||
@param.depends('speed', watch=True)
|
||||
def _update_speed(self):
|
||||
self._cb.period = 1000//self.speed
|
||||
|
||||
@param.depends('play', watch=True)
|
||||
def _play_pause(self):
|
||||
if self._playing:
|
||||
self._cb.stop()
|
||||
self.param.play.label = '▷'
|
||||
self.param.speed.precedence = -1
|
||||
else:
|
||||
self._cb.start()
|
||||
self.param.play.label = '❚❚'
|
||||
self.param.speed.precedence = 1
|
||||
self._playing = not self._playing
|
||||
|
||||
@param.depends('view', 'radius', 'elevation', 'arc_view', watch=True)
|
||||
def update_spec(self):
|
||||
self.deck_gl.object = dict(self.spec)
|
||||
|
||||
url = 'https://s3.eu-west-1.amazonaws.com/assets.holoviews.org/data/nyc_taxi_wide.csv'
|
||||
df = pd.read_csv(open_url(url))
|
||||
app = App(data=df)
|
||||
controls = pn.Param(app.param, sizing_mode='stretch_width', show_name=False)
|
||||
|
||||
app.deck_gl.servable(target='plot')
|
||||
controls.servable(target='widgets')
|
||||
</py-script>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
<script>
|
||||
const viewCodeButton = document.getElementById("view-code-button");
|
||||
const codeSection = document.getElementById("code-section");
|
||||
const handleClick = () => {
|
||||
if (codeSection.classList.contains("code-section-hidden")) {
|
||||
codeSection.classList.remove("code-section-hidden");
|
||||
codeSection.classList.add("code-section-visible");
|
||||
} else {
|
||||
codeSection.classList.remove("code-section-visible");
|
||||
codeSection.classList.add("code-section-hidden");
|
||||
}
|
||||
}
|
||||
viewCodeButton.addEventListener("click", handleClick)
|
||||
viewCodeButton.addEventListener("keydown", (e) => {
|
||||
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
||||
handleClick();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -39,48 +39,49 @@
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<link rel="stylesheet" href="./assets/prism/prism.css" />
|
||||
<script defer src="./assets/prism/prism.js"></script>
|
||||
</head>
|
||||
|
||||
<py-env>
|
||||
- altair
|
||||
- numpy
|
||||
- pandas
|
||||
- scikit-learn
|
||||
- panel==0.13.1
|
||||
</py-env>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid d-flex flex-column vh-100 overflow-hidden" id="container">
|
||||
<nav class="navbar navbar-expand-md navbar-dark sticky-top shadow" id="header" style="background-color: #000000;">
|
||||
<button type="button" class="navbar-toggle collapsed" id="sidebarCollapse">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="app-header">
|
||||
<a class="navbar-brand app-logo" href="/">
|
||||
<img src="./logo.png" class="app-logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">Panel KMeans Clustering Demo</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<nav class="navbar" style="background-color: #000000;">
|
||||
<div class="app-header">
|
||||
<a href="/">
|
||||
<img src="./logo.png" class="logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">Panel KMeans Clustering Demo</a>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<div class="row overflow-hidden" id="content">
|
||||
<div class="sidenav" id="sidebar">
|
||||
<ul class="nav flex-column">
|
||||
<div class="bk-root" id="x-widget"></div>
|
||||
<div class="bk-root" id="y-widget"></div>
|
||||
<div class="bk-root" id="n-widget"></div>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col mh-100 float-left" id="main">
|
||||
<div class="bk-root" id="intro"></div>
|
||||
<div class="bk-root" id="cluster-plot"></div>
|
||||
<div class="bk-root" id="table"></div>
|
||||
</div>
|
||||
<div class="sidenav" id="sidebar">
|
||||
<ul class="nav flex-column">
|
||||
<div class="bk-root" id="x-widget"></div>
|
||||
<div class="bk-root" id="y-widget"></div>
|
||||
<div class="bk-root" id="n-widget"></div>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col mh-100 float-left" id="main">
|
||||
<div class="bk-root" id="intro"></div>
|
||||
<div class="bk-root" id="cluster-plot"></div>
|
||||
<div class="bk-root" id="table"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<py-config>
|
||||
packages = [
|
||||
"altair",
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn",
|
||||
"panel==0.13.1"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
<py-script>
|
||||
import altair as alt
|
||||
import panel as pn
|
||||
@@ -157,6 +158,122 @@
|
||||
update_table()
|
||||
update_chart()
|
||||
</py-script>
|
||||
</section>
|
||||
<section class="code">
|
||||
<div id="view-code-button" role="button" aria-pressed="false" tabindex="0" style="height: 95px;">View Code</div>
|
||||
<div id="code-section" class="code-section-hidden">
|
||||
<p>index.html</p>
|
||||
<pre class="prism-code language-html">
|
||||
<code class="language-html">
|
||||
<py-config>
|
||||
packages = [
|
||||
"altair",
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn",
|
||||
"panel==0.13.1"
|
||||
]
|
||||
</py-config>
|
||||
<py-script>
|
||||
import altair as alt
|
||||
import panel as pn
|
||||
import pandas as pd
|
||||
|
||||
from sklearn.cluster import KMeans
|
||||
from pyodide.http import open_url
|
||||
|
||||
pn.config.sizing_mode = 'stretch_width'
|
||||
|
||||
url = 'https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-07-28/penguins.csv'
|
||||
penguins = pd.read_csv(open_url(url)).dropna()
|
||||
cols = list(penguins.columns)[2:6]
|
||||
|
||||
x = pn.widgets.Select(name='x', options=cols, value='bill_depth_mm').servable(target='x-widget')
|
||||
y = pn.widgets.Select(name='y', options=cols, value='bill_length_mm').servable(target='y-widget')
|
||||
n_clusters = pn.widgets.IntSlider(name='n_clusters', start=1, end=5, value=3).servable(target='n-widget')
|
||||
|
||||
brush = alt.selection_interval(name='brush') # selection of type "interval"
|
||||
|
||||
def get_clusters(n_clusters):
|
||||
kmeans = KMeans(n_clusters=n_clusters)
|
||||
est = kmeans.fit(penguins[cols].values)
|
||||
df = penguins.copy()
|
||||
df['labels'] = est.labels_.astype('str')
|
||||
return df
|
||||
|
||||
def get_chart(x, y, df):
|
||||
centers = df.groupby('labels').mean()
|
||||
return (
|
||||
alt.Chart(df)
|
||||
.mark_point(size=100)
|
||||
.encode(
|
||||
x=alt.X(x, scale=alt.Scale(zero=False)),
|
||||
y=alt.Y(y, scale=alt.Scale(zero=False)),
|
||||
shape='labels',
|
||||
color='species'
|
||||
).add_selection(brush).properties(width=800) +
|
||||
alt.Chart(centers)
|
||||
.mark_point(size=250, shape='cross', color='black')
|
||||
.encode(x=x+':Q', y=y+':Q')
|
||||
)
|
||||
|
||||
intro = pn.pane.Markdown("""
|
||||
This app provides an example of **building a simple dashboard using
|
||||
Panel**.\n\nIt demonstrates how to take the output of **k-means
|
||||
clustering on the Penguins dataset** using scikit-learn,
|
||||
parameterizing the number of clusters and the variables to
|
||||
plot.\n\nThe plot and the table are linked, i.e. selecting on the plot
|
||||
will filter the data in the table.\n\n The **`x` marks the center** of
|
||||
the cluster.
|
||||
""").servable(target='intro')
|
||||
|
||||
chart = pn.pane.Vega().servable(target='cluster-plot')
|
||||
table = pn.widgets.Tabulator(pagination='remote', page_size=10).servable(target='table')
|
||||
|
||||
def update_table(event=None):
|
||||
table.value = get_clusters(n_clusters.value)
|
||||
|
||||
n_clusters.param.watch(update_table, 'value')
|
||||
|
||||
@pn.depends(x, y, n_clusters, watch=True)
|
||||
def update_chart(*events):
|
||||
chart.object = get_chart(x.value, y.value, table.value)
|
||||
chart.selection.param.watch(update_filters, 'brush')
|
||||
|
||||
def update_filters(event=None):
|
||||
filters = []
|
||||
for k, v in (getattr(event, 'new') or {}).items():
|
||||
filters.append(dict(field=k, type='>=', value=v[0]))
|
||||
filters.append(dict(field=k, type='<=', value=v[1]))
|
||||
table.filters = filters
|
||||
|
||||
update_table()
|
||||
update_chart()
|
||||
</py-script>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
<script>
|
||||
const viewCodeButton = document.getElementById("view-code-button");
|
||||
const codeSection = document.getElementById("code-section");
|
||||
const handleClick = () => {
|
||||
if (codeSection.classList.contains("code-section-hidden")) {
|
||||
codeSection.classList.remove("code-section-hidden");
|
||||
codeSection.classList.add("code-section-visible");
|
||||
} else {
|
||||
codeSection.classList.remove("code-section-visible");
|
||||
codeSection.classList.add("code-section-hidden");
|
||||
}
|
||||
}
|
||||
viewCodeButton.addEventListener("click", handleClick)
|
||||
viewCodeButton.addEventListener("keydown", (e) => {
|
||||
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
||||
handleClick();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
|
||||
@@ -30,31 +30,23 @@
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<link rel="stylesheet" href="./assets/prism/prism.css" />
|
||||
<script defer src="./assets/prism/prism.js"></script>
|
||||
</head>
|
||||
|
||||
<py-env>
|
||||
- bokeh
|
||||
- numpy
|
||||
- pandas
|
||||
- panel==0.13.1
|
||||
</py-env>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid d-flex flex-column vh-100 overflow-hidden" id="container">
|
||||
<nav class="navbar navbar-expand-md navbar-dark sticky-top shadow" id="header" style="background-color: #000000;">
|
||||
<button type="button" class="navbar-toggle collapsed" id="sidebarCollapse">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="app-header">
|
||||
<a class="navbar-brand app-logo" href="/">
|
||||
<img src="./logo.png" class="app-logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">Panel Streaming Demo</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<nav class="navbar" style="background-color: #000000;">
|
||||
<div class="app-header">
|
||||
<a href="/">
|
||||
<img src="./logo.png" class="logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">Panel Streaming Demo</a>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<div class="row overflow-hidden" id="content">
|
||||
<div class="col mh-100 float-left" id="main">
|
||||
<div class="bk-root" id="controls"></div>
|
||||
@@ -66,6 +58,15 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<py-config>
|
||||
packages = [
|
||||
"bokeh",
|
||||
"numpy",
|
||||
"pandas",
|
||||
"panel==0.13.1"
|
||||
]
|
||||
</py-config>
|
||||
|
||||
<py-script>
|
||||
import panel as pn
|
||||
import numpy as np
|
||||
@@ -113,5 +114,90 @@
|
||||
pn.pane.Bokeh(p).servable(target='plot')
|
||||
pn.Row(cb.param.period, rollover, follow, width=400).servable(target='controls')
|
||||
</py-script>
|
||||
</section>
|
||||
<section class="code">
|
||||
<div id="view-code-button" role="button" aria-pressed="false" tabindex="0" style="height: 95px;">View Code</div>
|
||||
<div id="code-section" class="code-section-hidden">
|
||||
<p>index.html</p>
|
||||
<pre class="prism-code language-html">
|
||||
<code class="language-html">
|
||||
<py-config>
|
||||
packages = [
|
||||
"bokeh",
|
||||
"numpy",
|
||||
"pandas",
|
||||
"panel==0.13.1"
|
||||
]
|
||||
</py-config>
|
||||
<py-script>
|
||||
import panel as pn
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
from bokeh.models import ColumnDataSource
|
||||
from bokeh.plotting import figure
|
||||
|
||||
df = pd.DataFrame(np.random.randn(10, 4), columns=list('ABCD')).cumsum()
|
||||
|
||||
rollover = pn.widgets.IntInput(name='Rollover', value=15)
|
||||
follow = pn.widgets.Checkbox(name='Follow', value=True, align='end')
|
||||
|
||||
tabulator = pn.widgets.Tabulator(df, height=450, width=400).servable(target='table')
|
||||
|
||||
def color_negative_red(val):
|
||||
"""
|
||||
Takes a scalar and returns a string with
|
||||
the css property `'color: red'` for negative
|
||||
strings, black otherwise.
|
||||
"""
|
||||
color = 'red' if val < 0 else 'green'
|
||||
return 'color: %s' % color
|
||||
|
||||
tabulator.style.applymap(color_negative_red)
|
||||
|
||||
p = figure(height=450, width=600)
|
||||
|
||||
cds = ColumnDataSource(data=ColumnDataSource.from_df(df))
|
||||
|
||||
p.line('index', 'A', source=cds, line_color='red')
|
||||
p.line('index', 'B', source=cds, line_color='green')
|
||||
p.line('index', 'C', source=cds, line_color='blue')
|
||||
p.line('index', 'D', source=cds, line_color='purple')
|
||||
|
||||
def stream():
|
||||
data = df.iloc[-1] + np.random.randn(4)
|
||||
tabulator.stream(data, rollover=rollover.value, follow=follow.value)
|
||||
value = {k: [v] for k, v in tabulator.value.iloc[-1].to_dict().items()}
|
||||
value['index'] = [tabulator.value.index[-1]]
|
||||
cds.stream(value)
|
||||
|
||||
cb = pn.state.add_periodic_callback(stream, 200)
|
||||
|
||||
pn.pane.Bokeh(p).servable(target='plot')
|
||||
pn.Row(cb.param.period, rollover, follow, width=400).servable(target='controls')
|
||||
</py-script>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
<script>
|
||||
const viewCodeButton = document.getElementById("view-code-button");
|
||||
const codeSection = document.getElementById("code-section");
|
||||
const handleClick = () => {
|
||||
if (codeSection.classList.contains("code-section-hidden")) {
|
||||
codeSection.classList.remove("code-section-hidden");
|
||||
codeSection.classList.add("code-section-visible");
|
||||
} else {
|
||||
codeSection.classList.remove("code-section-visible");
|
||||
codeSection.classList.add("code-section-hidden");
|
||||
}
|
||||
}
|
||||
viewCodeButton.addEventListener("click", handleClick)
|
||||
viewCodeButton.addEventListener("keydown", (e) => {
|
||||
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
||||
handleClick();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
from datetime import datetime as dt
|
||||
|
||||
import pyscript
|
||||
|
||||
class PyItem(PyItemTemplate):
|
||||
|
||||
class PyItem(pyscript.PyItemTemplate):
|
||||
def on_click(self, evt=None):
|
||||
self.data["done"] = not self.data["done"]
|
||||
self.strike(self.data["done"])
|
||||
@@ -9,7 +11,7 @@ class PyItem(PyItemTemplate):
|
||||
self.select("input").element.checked = self.data["done"]
|
||||
|
||||
|
||||
class PyList(PyListTemplate):
|
||||
class PyList(pyscript.PyListTemplate):
|
||||
item_class = PyItem
|
||||
|
||||
def add(self, item):
|
||||
|
||||
@@ -7,21 +7,117 @@
|
||||
<title>REPL</title>
|
||||
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<link rel="stylesheet" href="./assets/prism/prism.css" />
|
||||
<script defer src="./assets/prism/prism.js"></script>
|
||||
</head>
|
||||
|
||||
<py-env>
|
||||
- paths:
|
||||
- ./antigravity.py
|
||||
</py-env>
|
||||
|
||||
<body>
|
||||
<h1><b>pyscript REPL</b></h1>
|
||||
<nav class="navbar" style="background-color: #000000;">
|
||||
<div class="app-header">
|
||||
<a href="/">
|
||||
<img src="./logo.png" class="logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">PyScript REPL</a>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<h1><b>PyScript REPL</b></h1>
|
||||
Tip: press Shift-ENTER to evaluate a cell
|
||||
<br>
|
||||
<div>
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
files = ["./antigravity.py"]
|
||||
</py-config>
|
||||
<div style="margin-right: 3rem;">
|
||||
<py-repl id="my-repl" auto-generate="true"> </py-repl>
|
||||
</div>
|
||||
</section>
|
||||
<section class="code">
|
||||
<div id="view-code-button" role="button" aria-pressed="false" tabindex="0">View Code</div>
|
||||
<div id="code-section" class="code-section-hidden">
|
||||
<p>index.html</p>
|
||||
<pre class="prism-code language-html">
|
||||
<code class="language-html">
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
files = ["./antigravity.py"]
|
||||
</py-config>
|
||||
<div>
|
||||
<py-repl id="my-repl" auto-generate="true"> </py-repl>
|
||||
</div>
|
||||
</code>
|
||||
</pre>
|
||||
<p>antigravity.py</p>
|
||||
<pre class="prism-code language-python">
|
||||
<code class="language-python">
|
||||
import random
|
||||
import sys
|
||||
|
||||
from js import DOMParser, document, setInterval
|
||||
from pyodide.ffi import create_proxy
|
||||
from pyodide.http import open_url
|
||||
|
||||
|
||||
class Antigravity:
|
||||
|
||||
url = "./antigravity.svg"
|
||||
|
||||
def __init__(self, target=None, interval=10, append=True, fly=False):
|
||||
target = target or sys.stdout._out
|
||||
self.target = (
|
||||
document.getElementById(target) if isinstance(target, str) else target
|
||||
)
|
||||
doc = DOMParser.new().parseFromString(
|
||||
open_url(self.url).read(), "image/svg+xml"
|
||||
)
|
||||
self.node = doc.documentElement
|
||||
if append:
|
||||
self.target.append(self.node)
|
||||
else:
|
||||
self.target.replaceChildren(self.node)
|
||||
self.xoffset, self.yoffset = 0, 0
|
||||
self.interval = interval
|
||||
if fly:
|
||||
self.fly()
|
||||
|
||||
def fly(self):
|
||||
setInterval(create_proxy(self.move), self.interval)
|
||||
|
||||
def move(self):
|
||||
char = self.node.getElementsByTagName("g")[1]
|
||||
char.setAttribute("transform", f"translate({self.xoffset}, {-self.yoffset})")
|
||||
self.xoffset += random.normalvariate(0, 1) / 20
|
||||
if self.yoffset < 50:
|
||||
self.yoffset += 0.1
|
||||
else:
|
||||
self.yoffset += random.normalvariate(0, 1) / 20
|
||||
|
||||
_auto = Antigravity(append=True)
|
||||
fly = _auto.fly
|
||||
</code>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
<script>
|
||||
const viewCodeButton = document.getElementById("view-code-button");
|
||||
const codeSection = document.getElementById("code-section");
|
||||
const handleClick = () => {
|
||||
if (codeSection.classList.contains("code-section-hidden")) {
|
||||
codeSection.classList.remove("code-section-hidden");
|
||||
codeSection.classList.add("code-section-visible");
|
||||
} else {
|
||||
codeSection.classList.remove("code-section-visible");
|
||||
codeSection.classList.add("code-section-hidden");
|
||||
}
|
||||
}
|
||||
viewCodeButton.addEventListener("click", handleClick)
|
||||
viewCodeButton.addEventListener("keydown", (e) => {
|
||||
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
||||
handleClick();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -7,27 +7,142 @@
|
||||
<title>Custom REPL Example</title>
|
||||
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<link rel="stylesheet" href="repl.css" />
|
||||
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<link rel="stylesheet" href="./assets/prism/prism.css" />
|
||||
<script defer src="./assets/prism/prism.js"></script>
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
|
||||
<py-env>
|
||||
- bokeh
|
||||
- numpy
|
||||
- paths:
|
||||
- ./utils.py
|
||||
- ./antigravity.py
|
||||
</py-env>
|
||||
|
||||
<body>
|
||||
<nav class="navbar" style="background-color: #000000;">
|
||||
<div class="app-header">
|
||||
<a href="/">
|
||||
<img src="./logo.png" class="logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">Custom REPL</a>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<h1 class="font-semibold text-2xl ml-5">Custom REPL</h1>
|
||||
<py-box widths="1/2;1/2">
|
||||
<py-repl id="my-repl" auto-generate="true" std-out="output" std-err="err-div"> </py-repl>
|
||||
<py-config>
|
||||
packages = [
|
||||
"bokeh",
|
||||
"numpy"
|
||||
]
|
||||
|
||||
[[fetch]]
|
||||
files = ["./utils.py", "./antigravity.py"]
|
||||
</py-config>
|
||||
<div style="margin-right: 3rem;">
|
||||
<py-repl id="my-repl" auto-generate="true"> </py-repl>
|
||||
<div id="output" class="p-4"></div>
|
||||
</py-box>
|
||||
<footer id="err-div" class="bg-red-700 text-white text-center border-t-4 border-green-500 fixed inset-x-0 bottom-0 p-4 hidden">
|
||||
</footer>
|
||||
</div>
|
||||
</section>
|
||||
<section class="code">
|
||||
<div id="view-code-button" role="button" aria-pressed="false" tabindex="0">View Code</div>
|
||||
<div id="code-section" class="code-section-hidden">
|
||||
<p>index.html</p>
|
||||
<pre class="prism-code language-html">
|
||||
<code class="language-html">
|
||||
<div>
|
||||
<py-repl id="my-repl" auto-generate="true"> </py-repl>
|
||||
<div id="output" class="p-4"></div>
|
||||
</div>
|
||||
</code>
|
||||
</pre>
|
||||
<p>utils.py</p>
|
||||
<pre class="prism-code language-python">
|
||||
<code class="language-python">
|
||||
from datetime import datetime as dt
|
||||
|
||||
|
||||
def format_date(dt_, fmt="%m/%d/%Y, %H:%M:%S"):
|
||||
return f"{dt_:{fmt}}"
|
||||
|
||||
|
||||
def now(fmt="%m/%d/%Y, %H:%M:%S"):
|
||||
return format_date(dt.now(), fmt)
|
||||
|
||||
|
||||
def remove_class(element, class_name):
|
||||
element.element.classList.remove(class_name)
|
||||
|
||||
|
||||
def add_class(element, class_name):
|
||||
element.element.classList.add(class_name)
|
||||
</code>
|
||||
</pre>
|
||||
<p>antigravity.py</p>
|
||||
<pre class="prism-code language-python">
|
||||
<code class="language-python">
|
||||
import random
|
||||
import sys
|
||||
|
||||
from js import DOMParser, document, setInterval
|
||||
from pyodide.ffi import create_proxy
|
||||
from pyodide.http import open_url
|
||||
|
||||
|
||||
class Antigravity:
|
||||
|
||||
url = "./antigravity.svg"
|
||||
|
||||
def __init__(self, target=None, interval=10, append=True, fly=False):
|
||||
target = target or sys.stdout._out
|
||||
self.target = (
|
||||
document.getElementById(target) if isinstance(target, str) else target
|
||||
)
|
||||
doc = DOMParser.new().parseFromString(
|
||||
open_url(self.url).read(), "image/svg+xml"
|
||||
)
|
||||
self.node = doc.documentElement
|
||||
if append:
|
||||
self.target.append(self.node)
|
||||
else:
|
||||
self.target.replaceChildren(self.node)
|
||||
self.xoffset, self.yoffset = 0, 0
|
||||
self.interval = interval
|
||||
if fly:
|
||||
self.fly()
|
||||
|
||||
def fly(self):
|
||||
setInterval(create_proxy(self.move), self.interval)
|
||||
|
||||
def move(self):
|
||||
char = self.node.getElementsByTagName("g")[1]
|
||||
char.setAttribute("transform", f"translate({self.xoffset}, {-self.yoffset})")
|
||||
self.xoffset += random.normalvariate(0, 1) / 20
|
||||
if self.yoffset < 50:
|
||||
self.yoffset += 0.1
|
||||
else:
|
||||
self.yoffset += random.normalvariate(0, 1) / 20
|
||||
|
||||
_auto = Antigravity(append=True)
|
||||
fly = _auto.fly
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
<script>
|
||||
const viewCodeButton = document.getElementById("view-code-button");
|
||||
const codeSection = document.getElementById("code-section");
|
||||
const handleClick = () => {
|
||||
if (codeSection.classList.contains("code-section-hidden")) {
|
||||
codeSection.classList.remove("code-section-hidden");
|
||||
codeSection.classList.add("code-section-visible");
|
||||
} else {
|
||||
codeSection.classList.remove("code-section-visible");
|
||||
codeSection.classList.add("code-section-hidden");
|
||||
}
|
||||
}
|
||||
viewCodeButton.addEventListener("click", handleClick)
|
||||
viewCodeButton.addEventListener("keydown", (e) => {
|
||||
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
||||
handleClick();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
||||
<link rel='stylesheet' type='text/css' media='screen' href='https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css'>
|
||||
<link rel="icon" type="image/png" href="https://user-images.githubusercontent.com/49681382/166738771-d0c26557-426c-4688-9641-8db5e6b08348.png" />
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -49,8 +49,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button id="run" type="button" class="button is-primary" pys-onClick="run">Run!</button>
|
||||
<button id="clear" type="button" class="button is-danger" pys-onClick="clear">Clear</button>
|
||||
<button id="run" type="button" class="button is-primary" py-onClick="run()">Run!</button>
|
||||
<button id="clear" type="button" class="button is-danger" py-onClick="clear()">Clear</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -7,24 +7,34 @@
|
||||
<title>Simple Clock Demo</title>
|
||||
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<py-env>
|
||||
- paths:
|
||||
- ./utils.py
|
||||
</py-env>
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<link rel="stylesheet" href="./assets/prism/prism.css" />
|
||||
<script defer src="./assets/prism/prism.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar" style="background-color: #000000;">
|
||||
<div class="app-header">
|
||||
<a href="/">
|
||||
<img src="./logo.png" class="logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">Simple Clock</a>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<div class="font-mono">start time: <label id="outputDiv"></label></div>
|
||||
<div id="outputDiv2" class="font-mono"></div>
|
||||
<div id="outputDiv3" class="font-mono"></div>
|
||||
<py-script output="outputDiv">
|
||||
# demonstrates how use the global PyScript pyscript_loader
|
||||
# to send operation log messages to it
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
files = ["./utils.py"]
|
||||
</py-config>
|
||||
<py-script>
|
||||
import utils
|
||||
utils.now()
|
||||
display(utils.now())
|
||||
</py-script>
|
||||
|
||||
<py-script>
|
||||
@@ -43,9 +53,80 @@ async def foo():
|
||||
else:
|
||||
out3.clear()
|
||||
|
||||
# close the global PyScript pyscript_loader
|
||||
pyscript_loader.close()
|
||||
pyscript.run_until_complete(foo())
|
||||
</py-script>
|
||||
</section>
|
||||
<section class="code">
|
||||
<div id="view-code-button" role="button" aria-pressed="false" tabindex="0">View Code</div>
|
||||
<div id="code-section" class="code-section-hidden">
|
||||
<p>index.html</p>
|
||||
<pre class="prism-code language-html">
|
||||
<code class="language-html">
|
||||
<div class="font-mono">start time: <label id="outputDiv"></label></div>
|
||||
<div id="outputDiv2" class="font-mono"></div>
|
||||
<div id="outputDiv3" class="font-mono"></div>
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
files = ["./utils.py"]
|
||||
</py-config>
|
||||
<py-script>
|
||||
from utils import now
|
||||
import asyncio
|
||||
|
||||
async def foo():
|
||||
while True:
|
||||
await asyncio.sleep(1)
|
||||
output = now()
|
||||
Element("outputDiv2").write(output)
|
||||
|
||||
out3 = Element("outputDiv3")
|
||||
if output[-1] in ["0", "4", "8"]:
|
||||
out3.write("It's espresso time!")
|
||||
else:
|
||||
out3.clear()
|
||||
|
||||
pyscript.run_until_complete(foo())
|
||||
</py-script>
|
||||
</code>
|
||||
</pre>
|
||||
<p>utils.py</p>
|
||||
<pre class="prism-code language-python">
|
||||
<code class="language-python">
|
||||
from datetime import datetime as dt
|
||||
|
||||
def format_date(dt_, fmt="%m/%d/%Y, %H:%M:%S"):
|
||||
return f"{dt_:{fmt}}"
|
||||
|
||||
def now(fmt="%m/%d/%Y, %H:%M:%S"):
|
||||
return format_date(dt.now(), fmt)
|
||||
|
||||
def remove_class(element, class_name):
|
||||
element.element.classList.remove(class_name)
|
||||
|
||||
def add_class(element, class_name):
|
||||
element.element.classList.add(class_name)
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
<script>
|
||||
const viewCodeButton = document.getElementById("view-code-button");
|
||||
const codeSection = document.getElementById("code-section");
|
||||
const handleClick = () => {
|
||||
if (codeSection.classList.contains("code-section-hidden")) {
|
||||
codeSection.classList.remove("code-section-hidden");
|
||||
codeSection.classList.add("code-section-visible");
|
||||
} else {
|
||||
codeSection.classList.remove("code-section-visible");
|
||||
codeSection.classList.add("code-section-hidden");
|
||||
}
|
||||
}
|
||||
viewCodeButton.addEventListener("click", handleClick)
|
||||
viewCodeButton.addEventListener("keydown", (e) => {
|
||||
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
||||
handleClick();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -6,18 +6,39 @@
|
||||
<title>Todo App</title>
|
||||
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<py-env>
|
||||
- paths:
|
||||
- ./utils.py
|
||||
</py-env>
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<link rel="stylesheet" href="./assets/prism/prism.css" />
|
||||
<script defer src="./assets/prism/prism.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar" style="background-color: #000000;">
|
||||
<div class="app-header">
|
||||
<a href="/">
|
||||
<img src="./logo.png" class="logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">Pyscript Native TODO App</a>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<h1>To Do List</h1>
|
||||
<py-register-widget src="./pylist.py" name="py-list" klass="PyList"></py-register-widget>
|
||||
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
files = ["./utils.py"]
|
||||
</py-config>
|
||||
|
||||
<py-script>
|
||||
from js import document
|
||||
from pyodide.ffi.wrappers import add_event_listener
|
||||
|
||||
def add_task(*ags, **kws):
|
||||
# create a new dictionary representing the new task
|
||||
new_task_content = Element("new-task-content")
|
||||
task = { "content": new_task_content.value, "done": False, "created_at": dt.now() }
|
||||
|
||||
# add a new task to the list and tell it to use the `content` key to show in the UI
|
||||
@@ -27,24 +48,123 @@
|
||||
# clear the inputbox element used to create the new task
|
||||
new_task_content.clear()
|
||||
|
||||
</py-script>
|
||||
</head>
|
||||
def on_click(evt):
|
||||
add_task()
|
||||
|
||||
<body>
|
||||
<py-title>To Do List</py-title>
|
||||
<py-box widths="4/5;1/5">
|
||||
<py-inputbox id="new-task-content">
|
||||
def on_keypress(e):
|
||||
if (e.code == "Enter"):
|
||||
add_task()
|
||||
</py-inputbox>
|
||||
<py-button id="new-task-btn" label="Add Task!">
|
||||
def on_click(evt):
|
||||
add_task()
|
||||
</py-button>
|
||||
</py-box>
|
||||
def handle_keypress(evt):
|
||||
if evt.key == "Enter":
|
||||
add_task()
|
||||
|
||||
add_event_listener(
|
||||
document.getElementById("new-task-content"),
|
||||
"keypress",
|
||||
handle_keypress
|
||||
)
|
||||
|
||||
</py-script>
|
||||
<div class="py-box">
|
||||
<input id="new-task-content" />
|
||||
<button py-click="add_task()" id="new-task-btn" class="py-button">Add Task!</button>
|
||||
</div>
|
||||
|
||||
<py-list id="myList"></py-list>
|
||||
<py-repl id="my-repl" auto-generate="true"> </py-repl>
|
||||
</section>
|
||||
<section class="code">
|
||||
<div id="view-code-button" role="button" aria-pressed="false" tabindex="0">View Code</div>
|
||||
<div id="code-section" class="code-section-hidden">
|
||||
<p>index.html</p>
|
||||
<pre class="prism-code language-html">
|
||||
<code class="language-html">
|
||||
<py-register-widget src="./pylist.py" name="py-list" klass="PyList"></py-register-widget>
|
||||
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
files = ["./utils.py"]
|
||||
</py-config>
|
||||
|
||||
<py-script>
|
||||
from js import document
|
||||
from pyodide.ffi.wrappers import add_event_listener
|
||||
|
||||
def add_task(*ags, **kws):
|
||||
# create a new dictionary representing the new task
|
||||
new_task_content = Element("new-task-content")
|
||||
task = { "content": new_task_content.value, "done": False, "created_at": dt.now() }
|
||||
|
||||
# add a new task to the list and tell it to use the `content` key to show in the UI
|
||||
# and to use the key `done` to sync the task status with a checkbox element in the UI
|
||||
myList.add(task)
|
||||
|
||||
# clear the inputbox element used to create the new task
|
||||
new_task_content.clear()
|
||||
|
||||
def on_click(evt):
|
||||
add_task()
|
||||
|
||||
def handle_keypress(evt):
|
||||
if evt.key == "Enter":
|
||||
add_task()
|
||||
|
||||
add_event_listener(
|
||||
document.getElementById("new-task-content"),
|
||||
"keypress",
|
||||
handle_keypress
|
||||
)
|
||||
</py-script>
|
||||
<div class="py-box">
|
||||
<input id="new-task-content" />
|
||||
<button py-click="add_task()" id="new-task-btn" class="py-button">Add Task!</button>
|
||||
</div>
|
||||
|
||||
<py-list id="myList"></py-list>
|
||||
<py-repl id="my-repl" auto-generate="true"> </py-repl>
|
||||
</code>
|
||||
</pre>
|
||||
<p>pylist.py</p>
|
||||
<pre class="prism-code language-python">
|
||||
<code class="language-python">
|
||||
import pyscript
|
||||
from datetime import datetime as dt
|
||||
|
||||
class PyItem(pyscript.PyItemTemplate):
|
||||
def on_click(self, evt=None):
|
||||
self.data["done"] = not self.data["done"]
|
||||
self.strike(self.data["done"])
|
||||
|
||||
self.select("input").element.checked = self.data["done"]
|
||||
|
||||
class PyList(pyscript.PyListTemplate):
|
||||
item_class = PyItem
|
||||
|
||||
def add(self, item):
|
||||
if isinstance(item, str):
|
||||
item = {"content": item, "done": False, "created_at": dt.now()}
|
||||
|
||||
super().add(item, labels=["content"], state_key="done")
|
||||
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
<script>
|
||||
const viewCodeButton = document.getElementById("view-code-button");
|
||||
const codeSection = document.getElementById("code-section");
|
||||
const handleClick = () => {
|
||||
if (codeSection.classList.contains("code-section-hidden")) {
|
||||
codeSection.classList.remove("code-section-hidden");
|
||||
codeSection.classList.add("code-section-visible");
|
||||
} else {
|
||||
codeSection.classList.remove("code-section-visible");
|
||||
codeSection.classList.add("code-section-hidden");
|
||||
}
|
||||
}
|
||||
viewCodeButton.addEventListener("click", handleClick)
|
||||
viewCodeButton.addEventListener("keydown", (e) => {
|
||||
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
||||
handleClick();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -7,28 +7,40 @@
|
||||
<title>Todo App</title>
|
||||
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<py-env>
|
||||
- paths:
|
||||
- ./utils.py
|
||||
</py-env>
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="./assets/css/examples.css" />
|
||||
<link rel="stylesheet" href="./assets/prism/prism.css" />
|
||||
<script defer src="./assets/prism/prism.js"></script>
|
||||
</head>
|
||||
|
||||
<body class="container">
|
||||
<!-- <py-repl id="my-repl" auto-generate="true"> </py-repl> -->
|
||||
<body>
|
||||
<nav class="navbar" style="background-color: #000000;">
|
||||
<div class="app-header">
|
||||
<a href="/">
|
||||
<img src="./logo.png" class="logo">
|
||||
</a>
|
||||
<a class="title" href="" style="color: #f0ab3c;">Todo App</a>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="pyscript">
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
files = ["./utils.py"]
|
||||
</py-config>
|
||||
|
||||
<py-script src="./todo.py"> </py-script>
|
||||
|
||||
<main class="max-w-xs mx-auto mt-4">
|
||||
<main>
|
||||
<section>
|
||||
|
||||
<div class="text-center w-full mb-8">
|
||||
<h1 class="text-3xl font-bold text-gray-800 uppercase tracking-tight">To Do List</h1>
|
||||
</div>
|
||||
<div>
|
||||
<input id="new-task-content" class="border flex-1 mr-3 border-gray-300 p-2 rounded" type="text">
|
||||
<button id="new-task-btn" class="p-2 text-white bg-blue-600 border border-blue-600 rounded" type="submit" pys-onClick="add_task">
|
||||
<input id="new-task-content" class="py-input" type="text">
|
||||
<button id="new-task-btn" class="py-button" type="submit" py-click="add_task()">
|
||||
Add task
|
||||
</button>
|
||||
</div>
|
||||
@@ -38,9 +50,9 @@
|
||||
</div>
|
||||
|
||||
<template id="task-template">
|
||||
<section class="task bg-white my-1">
|
||||
<section class="task py-li-element">
|
||||
<label for="flex items-center p-2 ">
|
||||
<input class="mr-2" type="checkbox" class="task-check">
|
||||
<input class="mr-2" type="checkbox">
|
||||
<p class="m-0 inline"></p>
|
||||
</label>
|
||||
</section>
|
||||
@@ -48,5 +60,149 @@
|
||||
|
||||
</section>
|
||||
</main>
|
||||
</section>
|
||||
<section class="code">
|
||||
<div id="view-code-button" role="button" aria-pressed="false" tabindex="0">View Code</div>
|
||||
<div id="code-section" class="code-section-hidden">
|
||||
<p>index.html</p>
|
||||
<pre class="prism-code language-html">
|
||||
<code class="language-html">
|
||||
<py-config>
|
||||
[[fetch]]
|
||||
files = ["./utils.py"]
|
||||
</py-config>
|
||||
<py-script src="./todo.py">
|
||||
</py-script>
|
||||
<main>
|
||||
<section>
|
||||
|
||||
<div class="text-center w-full mb-8">
|
||||
<h1 class="text-3xl font-bold text-gray-800 uppercase tracking-tight">To Do List</h1>
|
||||
</div>
|
||||
<div>
|
||||
<input id="new-task-content" class="py-input" type="text">
|
||||
<button id="new-task-btn" class="py-button" type="submit" py-click="add_task()">
|
||||
Add task
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<py-list id="myList"></py-list>
|
||||
<div id="list-tasks-container" class="flex flex-col-reverse mt-4">
|
||||
</div>
|
||||
|
||||
<template id="task-template">
|
||||
<section class="task py-li-element">
|
||||
<label for="flex items-center p-2 ">
|
||||
<input class="mr-2" type="checkbox">
|
||||
<p class="m-0 inline"></p>
|
||||
</label>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
</section>
|
||||
</main>
|
||||
</code>
|
||||
</pre>
|
||||
<p>utils.py</p>
|
||||
<pre class="prism-code language-python">
|
||||
<code class="language-python">
|
||||
from datetime import datetime as dt
|
||||
|
||||
|
||||
def format_date(dt_, fmt="%m/%d/%Y, %H:%M:%S"):
|
||||
return f"{dt_:{fmt}}"
|
||||
|
||||
|
||||
def now(fmt="%m/%d/%Y, %H:%M:%S"):
|
||||
return format_date(dt.now(), fmt)
|
||||
|
||||
|
||||
def remove_class(element, class_name):
|
||||
element.element.classList.remove(class_name)
|
||||
|
||||
|
||||
def add_class(element, class_name):
|
||||
element.element.classList.add(class_name)
|
||||
</code>
|
||||
</pre>
|
||||
<p>todo.py</p>
|
||||
<pre class="prism-code language-python">
|
||||
<code class="language-python">
|
||||
from datetime import datetime as dt
|
||||
|
||||
from utils import add_class, remove_class
|
||||
|
||||
tasks = []
|
||||
|
||||
# define the task template that will be use to render new templates to the page
|
||||
task_template = Element("task-template").select(".task", from_content=True)
|
||||
task_list = Element("list-tasks-container")
|
||||
new_task_content = Element("new-task-content")
|
||||
|
||||
|
||||
def add_task(*ags, **kws):
|
||||
# ignore empty task
|
||||
if not new_task_content.element.value:
|
||||
return None
|
||||
|
||||
# create task
|
||||
task_id = f"task-{len(tasks)}"
|
||||
task = {
|
||||
"id": task_id,
|
||||
"content": new_task_content.element.value,
|
||||
"done": False,
|
||||
"created_at": dt.now(),
|
||||
}
|
||||
|
||||
tasks.append(task)
|
||||
|
||||
# add the task element to the page as new node in the list by cloning from a
|
||||
# template
|
||||
task_html = task_template.clone(task_id)
|
||||
task_html_content = task_html.select("p")
|
||||
task_html_content.element.innerText = task["content"]
|
||||
task_html_check = task_html.select("input")
|
||||
task_list.element.appendChild(task_html.element)
|
||||
|
||||
def check_task(evt=None):
|
||||
task["done"] = not task["done"]
|
||||
if task["done"]:
|
||||
add_class(task_html_content, "line-through")
|
||||
else:
|
||||
remove_class(task_html_content, "line-through")
|
||||
|
||||
new_task_content.clear()
|
||||
task_html_check.element.onclick = check_task
|
||||
|
||||
|
||||
def add_task_event(e):
|
||||
if e.key == "Enter":
|
||||
add_task()
|
||||
|
||||
|
||||
new_task_content.element.onkeypress = add_task_event
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
<script>
|
||||
const viewCodeButton = document.getElementById("view-code-button");
|
||||
const codeSection = document.getElementById("code-section");
|
||||
const handleClick = () => {
|
||||
if (codeSection.classList.contains("code-section-hidden")) {
|
||||
codeSection.classList.remove("code-section-hidden");
|
||||
codeSection.classList.add("code-section-visible");
|
||||
} else {
|
||||
codeSection.classList.remove("code-section-visible");
|
||||
codeSection.classList.add("code-section-hidden");
|
||||
}
|
||||
}
|
||||
viewCodeButton.addEventListener("click", handleClick)
|
||||
viewCodeButton.addEventListener("keydown", (e) => {
|
||||
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
||||
handleClick();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -28,7 +28,7 @@ def add_task(*ags, **kws):
|
||||
|
||||
# add the task element to the page as new node in the list by cloning from a
|
||||
# template
|
||||
task_html = task_template.clone(task_id, to=task_list)
|
||||
task_html = task_template.clone(task_id)
|
||||
task_html_content = task_html.select("p")
|
||||
task_html_content.element.innerText = task["content"]
|
||||
task_html_check = task_html.select("input")
|
||||
|
||||
@@ -12,14 +12,10 @@
|
||||
crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="./static/toga.css">
|
||||
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
|
||||
<title>Loading...</title>
|
||||
<link rel="icon" type="image/png" href="../favicon.png" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="toga-placeholder">Loading...</div>
|
||||
|
||||
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
|
||||
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
|
||||
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
|
||||
@@ -33,18 +29,27 @@
|
||||
integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI"
|
||||
crossorigin="anonymous">
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<py-config>
|
||||
packages = [
|
||||
"./static/wheels/travertino-0.1.3-py3-none-any.whl",
|
||||
"./static/wheels/toga_core-0.3.0.dev39-py3-none-any.whl",
|
||||
"./static/wheels/toga_web-0.3.0.dev39-py3-none-any.whl",
|
||||
"./static/wheels/freedom-0.0.1-py3-none-any.whl"
|
||||
]
|
||||
terminal = false
|
||||
</py-config>
|
||||
|
||||
<div id="app-placeholder">Loading...</div>
|
||||
<py-script>
|
||||
from pyscript import Element
|
||||
from freedom.__main__ import main
|
||||
|
||||
|
||||
# Remove text from placeholder
|
||||
Element('app-placeholder').write('', append=False)
|
||||
app = main().main_loop()
|
||||
</py-script>
|
||||
</body>
|
||||
<py-env>
|
||||
- './static/wheels/travertino-0.1.3-py3-none-any.whl'
|
||||
- './static/wheels/toga_core-0.3.0.dev33-py3-none-any.whl'
|
||||
- './static/wheels/toga_web-0.3.0.dev33-py3-none-any.whl'
|
||||
- './static/wheels/freedom-0.0.1-py3-none-any.whl'
|
||||
</py-env>
|
||||
<py-script>
|
||||
from toga_web.dom import handle as dom_handle
|
||||
|
||||
from freedom.__main__ import main
|
||||
|
||||
app = main().main_loop(spa=True)
|
||||
</py-script>
|
||||
</html>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2,7 +2,7 @@ from datetime import datetime as dt
|
||||
|
||||
|
||||
def format_date(dt_, fmt="%m/%d/%Y, %H:%M:%S"):
|
||||
return dt_.strftime(fmt)
|
||||
return f"{dt_:{fmt}}"
|
||||
|
||||
|
||||
def now(fmt="%m/%d/%Y, %H:%M:%S"):
|
||||
|
||||
@@ -20,23 +20,19 @@
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/89/three.min.js'></script>
|
||||
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/0.147.0/three.min.js'></script>
|
||||
|
||||
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
|
||||
<script>
|
||||
|
||||
</script>
|
||||
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
||||
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
||||
<py-script>
|
||||
from pyodide import create_proxy, to_js
|
||||
from pyodide.ffi import create_proxy, to_js
|
||||
from js import window
|
||||
from js import Math
|
||||
from js import THREE
|
||||
from js import performance
|
||||
from pyodide import to_js
|
||||
from js import Object
|
||||
|
||||
|
||||
from js import document
|
||||
import asyncio
|
||||
|
||||
mouse = THREE.Vector2.new();
|
||||
|
||||
@@ -53,7 +49,7 @@ def onMouseMove(event):
|
||||
event.preventDefault();
|
||||
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
|
||||
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
|
||||
js.document.addEventListener('mousemove', pyodide.create_proxy(onMouseMove))
|
||||
js.document.addEventListener('mousemove', pyodide.ffi.create_proxy(onMouseMove))
|
||||
|
||||
camera = THREE.PerspectiveCamera.new( 35, window.innerWidth / window.innerHeight, 1, 500 )
|
||||
scene = THREE.Scene.new()
|
||||
@@ -148,49 +144,50 @@ scene.add(light);
|
||||
scene.add(lightBack);
|
||||
|
||||
rectSize = 2
|
||||
intensity = 100
|
||||
intensity = 14
|
||||
rectLight = THREE.RectAreaLight.new( 0x0FFFFF, intensity, rectSize, rectSize )
|
||||
rectLight.position.set( 0, 0, 1 )
|
||||
rectLight.lookAt( 0, 0, 0 )
|
||||
scene.add( rectLight )
|
||||
|
||||
rectLightHelper = THREE.RectAreaLightHelper.new( rectLight );
|
||||
raycaster = THREE.Raycaster.new();
|
||||
uSpeed = 0.1
|
||||
|
||||
time = 0.0003;
|
||||
camera.lookAt(scene.position)
|
||||
|
||||
while True:
|
||||
time = performance.now() * 0.0003;
|
||||
i = 0
|
||||
while i < particularGroup.children.length:
|
||||
newObject = particularGroup.children[i];
|
||||
newObject.rotation.x += newObject.speedValue/10;
|
||||
newObject.rotation.y += newObject.speedValue/10;
|
||||
newObject.rotation.z += newObject.speedValue/10;
|
||||
i += 1
|
||||
async def main():
|
||||
while True:
|
||||
time = performance.now() * 0.0003;
|
||||
i = 0
|
||||
while i < particularGroup.children.length:
|
||||
newObject = particularGroup.children[i];
|
||||
newObject.rotation.x += newObject.speedValue/10;
|
||||
newObject.rotation.y += newObject.speedValue/10;
|
||||
newObject.rotation.z += newObject.speedValue/10;
|
||||
i += 1
|
||||
|
||||
i = 0
|
||||
while i < modularGroup.children.length:
|
||||
newCubes = modularGroup.children[i];
|
||||
newCubes.rotation.x += 0.008;
|
||||
newCubes.rotation.y += 0.005;
|
||||
newCubes.rotation.z += 0.003;
|
||||
i = 0
|
||||
while i < modularGroup.children.length:
|
||||
newCubes = modularGroup.children[i];
|
||||
newCubes.rotation.x += 0.008;
|
||||
newCubes.rotation.y += 0.005;
|
||||
newCubes.rotation.z += 0.003;
|
||||
|
||||
newCubes.position.x = Math.sin(time * newCubes.positionZ) * newCubes.positionY;
|
||||
newCubes.position.y = Math.cos(time * newCubes.positionX) * newCubes.positionZ;
|
||||
newCubes.position.z = Math.sin(time * newCubes.positionY) * newCubes.positionX;
|
||||
i += 1
|
||||
newCubes.position.x = Math.sin(time * newCubes.positionZ) * newCubes.positionY;
|
||||
newCubes.position.y = Math.cos(time * newCubes.positionX) * newCubes.positionZ;
|
||||
newCubes.position.z = Math.sin(time * newCubes.positionY) * newCubes.positionX;
|
||||
i += 1
|
||||
|
||||
particularGroup.rotation.y += 0.005;
|
||||
particularGroup.rotation.y += 0.005;
|
||||
|
||||
modularGroup.rotation.y -= ((mouse.x * 4) + modularGroup.rotation.y) * uSpeed;
|
||||
modularGroup.rotation.x -= ((-mouse.y * 4) + modularGroup.rotation.x) * uSpeed;
|
||||
modularGroup.rotation.y -= ((mouse.x * 4) + modularGroup.rotation.y) * uSpeed;
|
||||
modularGroup.rotation.x -= ((-mouse.y * 4) + modularGroup.rotation.x) * uSpeed;
|
||||
|
||||
renderer.render( scene, camera )
|
||||
await asyncio.sleep(0.02)
|
||||
renderer.render( scene, camera )
|
||||
await asyncio.sleep(0.02)
|
||||
|
||||
asyncio.ensure_future(main())
|
||||
|
||||
</py-script>
|
||||
</body>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user