【PyScript】サンプル集

プログラミング

どうもです、タドスケです。

PyScriptを触っていて、基本的な機能に関する使い方を色々と調べましたので、サンプル集として公開しておきます。

このページにあるサンプルは自由にお使いいただいて構いません。

※2022/08/23更新
matplotlibを使用したグラフの描画




Hello world

PyScriptでHello worldを表示するサンプルです。

<!-- 必要最小限のPyScript動作 -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />

    <!-- pyscriptを使えるようにするために必要な2行 -->
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
  </head>
  <body>
    <div id="output"/>

    <!-- ここ以下にpythonのコードを書く -->
    <py-script>
output = Element('output')
output.write('Hello world!')
    </py-script>

  </body>
</html>

Element(Id名) で要素を取得し、write() で中身を書き換えています。

メインループ

定期的に実行されるメインループの実装例です。

<!-- 定期的に実行されるメインループを持つサンプル -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />

    <!-- pyscriptを使えるようにするために必要な2行 -->
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
  </head>
  <body>
    <div id="output"/>

    <!-- ここ以下にpythonのコードを書く -->
    <py-script>
from datetime import datetime as dt
import asyncio

# 定数
_FPS = 1.0 / 30

# エレメント
_OUTPUT = Element('output')

# 呼び出す関数はasyncにする必要がある
async def main():
  while True:
    _OUTPUT.write(f'{dt.now()}')
    await asyncio.sleep(_FPS)

pyscript_loader.close()
pyscript.run_until_complete(main())
    </py-script>

  </body>
</html>

呼び出す関数をasyncで定義するのと、pyscript.run_until_complete(main()) で関数を登録しているのがポイントです。

入力

マウス、キー入力を受け取るサンプルです。

<!-- キー入力を取得するサンプル -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />

    <!-- pyscriptを使えるようにするために必要な2行 -->
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>

  </head>
  <body>
  <div style="height:300px;border:solid;">
    Key=<input id="key"/><br/>
    MouseButton=<input id="mousebutton"/><br/>
    MousePos=<input id="mousepos" value="()"/><br/>
  </div>

    <!-- ここ以下にpythonのコードを書く -->
    <py-script>
from pyodide import create_proxy

# エレメント
el_key = Element('key')
el_mousebutton = Element('mousebutton')
el_mousepos = Element('mousepos')

# 呼び出す関数はasyncにする必要がある
async def on_keydown(event):
    """キーが押されたとき."""
    el_key.element.value = event.key

async def on_keyup(event):
    """キーが離されたとき."""
    el_key.element.value = ''

async def on_mousedown(event):
    """マウスボタンが押されたとき."""
    el_mousebutton.element.value = event.button

async def on_mouseup(event):
    """マウスボタンが離されたとき."""
    el_mousebutton.element.value = ''

async def on_mousemove(event):
    """マウスカーソルが移動したとき."""
    el_mousepos.element.value = f'({event.x}, {event.y})'

panel = document.querySelector("body")
panel.addEventListener("keydown", create_proxy(on_keydown))
panel.addEventListener("keyup", create_proxy(on_keyup))
panel.addEventListener("mousedown", create_proxy(on_mousedown))
panel.addEventListener("mouseup", create_proxy(on_mouseup))
panel.addEventListener("mousemove", create_proxy(on_mousemove))
    </py-script>

  </body>
</html>

document.querySelector() 関数で取得した要素に対して addEventListener を呼んでイベントを登録しています。

登録する関数はそのままでは登録できず、create_proxy(func) と指定する必要があります。

イベント名などはJavaScriptのものがそのまま使えるようです。

自作モジュール

pythonコードが大きくなってくると、html内に直接書くのがしんどくなってきます。

py-scriptでは、外部にある自作モジュールを読み込んで実行させることができます。

今回は以下のモジュール(mymodule.py)を読み込みます。

def hello():
    print('pyから表示')


if __name__ == '__main__':
    hello()

やり方は以下の2つがあります。

  1. py-scriptタグにsrc属性をつける
  2. py-envタグ内で宣言して、py-script内でimport

py-scriptタグにsrc属性をつける

<!-- 必要最小限のPyScript動作 -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />

    <!-- pyscriptを使えるようにするために必要な2行 -->
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>

  </head>
  <body>
    <py-script src="./mymodule.py"><!-- main扱いになる -->
print('htmlから表示')  # srcを指定した場合、このコードは実行されない
    </py-script>
  </body>
</html>

まるっとそのままファイルを指定できるので簡単です。

ただしhtmlに書いた他のコードが実行されなくなってしまうようなので、html要素と連携するようなコードを書くのが難しくなるかもしれません。

py-envタグ内で宣言して、py-script内でimport

<!-- 必要最小限のPyScript動作 -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />

    <!-- pyscriptを使えるようにするために必要な2行 -->
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>

    <!-- importするために、ここに書いておく必要がある -->
    <!-- 自作モジュールを使う時は paths: 以降にモジュール名を書く -->
    <py-env>
      - paths:
        - ./mymodule.py
    </py-env>

  </head>
  <body>
    <py-script>
import mymodule  # importなのでmain扱いにならない

print('htmlから表示')
mymodule.hello()  # モジュール内の関数を呼び出せる
    </py-script>
  </body>
</html>

importしたモジュール内の関数をhtml内から呼び出すことができます。

htmlに依存しないコードをモジュール内に書いておいて、依存部分だけhtml側に書くようにするのが良さそうです。

ローカルストレージによるセーブ機能

ローカルストレージ機能を利用して、ブラウザ上にセーブデータを保存するサンプルです。

ページをリロードする度に数字が増えます。
※ローカルストレージ機能をOFFにしている場合は動きません

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />

    <!-- pyscriptを使えるようにするために必要な2行 -->
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
  </head>
  <body>
    <input id="output" style="width:300px"/>

    <py-script>
from js import localStorage, document

# localStorage.removeItem('count')  # 値をリセットしたい場合はコメントアウトを外す

# 読み込み
count = localStorage.getItem('count')

if count is None:
  count = 0  # 値が無ければ初期値を設定
else:
  count = int(count) + 1  # 値があれば加算

# 保存
localStorage.setItem('count', count)

output = document.querySelector('#output')
output.value = f'{count} 回目のご来店ありがとうございます。'
    </py-script>

  </body>
</html>

from js import localStorage でlocalStorageを使えるようにします。

後はJavaScriptのlocalStorageの使い方を参考に、getItem, setItemでデータを読み書きできます。

注意点として、localStorageで扱えるのは文字列型のみなので、数値型を保存するにはint-str関数による変換が必要です。

Canvasを利用した描画

canvasを取ってきて描画するサンプルです。

<!-- canvasを利用した描画サンプル -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />

    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
  </head>
  <body>
    <canvas id="output" width="300px" height="200px"/>

    <py-script>
import math
from js import document, Element, CanvasRenderingContext2D, Image

canvas: Element = document.querySelector('#output')
ctx: CanvasRenderingContext2D = canvas.getContext('2d')


def draw_rect():
  """四角の描画."""
  ctx.fillStyle = "rgb(0, 0, 0)"
  ctx.fillRect(0, 0, 300, 200)

def draw_arc():
  """円の描画."""
  center_x = 50  # 中心X
  center_y = 50  # 中心Y
  radius = 20  # 半径
  angle_start = 0 * math.pi / 180  # 開始角度
  angle_end = 360 * math.pi / 180  # 終了角度
  anticlockwise = False  # 反時計回りにするか
  ctx.fillStyle = "rgb(0, 255, 255)"
  ctx.arc( center_x, center_y, radius, angle_start, angle_end, anticlockwise ) ;
  ctx.fill()

def draw_text():
  """テキストの描画."""
  ctx.font = "15px bold sans-serif"
  ctx.fillStyle = "rgb(255, 255, 255)"
  ctx.fillText(f"canvasに描いています", 5, 20)

def draw_image():
  """画像の描画."""
  x = 100
  y = 60
  w = 50
  h = 50
  image = Image.new()  # pythonではnew Image()とできないので、特殊な書き方になる
  image.src = 'image.png'
  image.onload = lambda e: ctx.drawImage(image, x, y, w, h)  # 読み込み待ちをしないと表示されない


draw_rect()
draw_arc()
draw_text()
draw_image()

    </py-script>
  </body>
</html>

getContext(‘2d’) で取ってきたcontextに対して、JavaScriptと同じ形式の関数が呼べます。

画像だけはちょっと特殊で、以下の対応が必要です。

  • imageインスタンスはImage.new() で生成する(JavaScriptとは違う書き方)
  • image.onloadに読み込み完了後のメソッドを登録し、その中でdrawImageを呼ぶ

matplotlibを利用したグラフの描画

matplotlibを使用してグラフを描画するサンプルです。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />

    <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>
  </head>
  <body>
    <div id="output"></div>
    <py-script>
import matplotlib.pyplot as plt

x = [i for i in range(100)]
y = [i**2 for i in range(100)]

fig = plt.figure()
plt.plot(x, y)

pyscript.write('output', fig)
    </py-script>
  </body>
</html>

matplotlibを単体で使う際にはshow()関数を使うのですが、PyScriptの場合はウィンドウを生成することができないので、代わりにpyscript.writeでdivタグに貼り付けています。

貼り付けるためにグラフの描画領域(fig)を明示的に取得しています。
(showで表示するだけならこの行は省略可能)

コメント

タイトルとURLをコピーしました