PythonのBokehで作ったグラフをWebアプリ化する話です。バックエンドで作ったグラフを、フロントエンドからWebAPIで呼び出してReactで描画します。
Bokehサーバーという選択肢もありますが、フロントエンドの技術でレイアウトするのもまた一興だと思います。ニッチすぎでしょうか??
APIの用意
バックエンドサーバーはいまどきっぽいFastAPIで作ります。
以下の例はFastAPIである必要性は全くないですが、リクエストが来たらBokehでグラフを作ってjson化して返すモジュールです。
import json
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware
import bokeh
from bokeh.plotting import figure
app = FastAPI()
# cors対策
app.add_middleware(
CORSMiddleware,
allow_origins=['*'],
allow_credentials=True,
allow_methods=['*'],
allow_headers=['*'],
)
@app.get('/plot')
def get_plot():
x = [1, 2, 3]
y = [5, 2, 4]
p = figure(width=500, height=300)
p.line(x, y)
return json.dumps(bokeh.embed.json_item(p)
ファイル名はmain.pyで保存します。
これでAPI用のモジュールができたので、サーバーを立ち上げます。ホストやポートはお好みで。
uvicorn main:app --reload --host 0.0.0.0 --port 8000
フロントエンドの用意
フロントエンドの環境は無数にありますが、今回はViteで作ります。
ViteでReactのテンプレートを使って、その後bokehjsも入れます。
準備ができたら、ウェブサーバーを立ち上げます。
yarn create vite my-app --template react
cd my-app
yarn
yarn add @bokeh/bokehjs
yarn dev --host 0.0.0.0 --port 3000
このままだと、Viteが用意したトップページのままなので、src/App.jsxを書き換えます。
以下はボタンが配置されただけのページです。
import * as Bokeh from '@bokeh/bokehjs'
export default function App() {
const handleClick = () => {
fetch('http://YOUR_IP_ADDRESS:PORT/plot').then(res => res.json())
.then(data => {
Bokeh.embed.embed_item(JSON.parse(data), 'testPlot')
})
}
return (
<>
<button onClick={handleClick}>send</button>
<div id='testPlot' className='bk-root'></div>
</>
)
}
以上でいけそうかと思ったのですが、ブラウザでエラーがでていました。
JQUERYがないとのメッセージがでていたのですが、どうもbokehjs内部での呼び出し方が適切でないようでしたので、index.htmlのheadでCDNからjqueryを読み込みます。
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
これで私の環境では無事動きました。
sendを叩くとビスケットではなくプロットが増えます。