Streamlitとは
StreamlitはPython向けのWebアプリケーションフレームワークです。
Streamlitを使うとPythonオンリーでWebアプリケーションを作成できるのが特徴です。特にPandasなどと組み合わせたデータアプリケーションを得意としています。また、最近ではLLMアプリケーションのフロントエンドとしても活用されています。
Streamlitでウィンドウサイズを取得するには
Streamlitでウィンドウサイズや描画領域サイズを取得するには以下の2つの方法がありそうです。標準のAPIでは実現できないため、何らかの拡張(3rd-Partyコンポーネント)を利用する必要があります。
- streamlit_js_evalを利用する
- st-screen-statsを利用する
どちらかというと後者がオススメかも。
Streamlitというか今どきのWebアプリケーションフレームワークにおいてはレスポンシブデザインに対応することが基本になっているので、ウィンドウサイズをアプリのロジックで取得して何かやるのはイマイチなのですが、たまにやりたくなるシーンがあるんですよね…
streamlit_js_evalを利用する
streamlit_js_evalはJavaScriptの実行結果を取得できる拡張です。汎用的で強力な拡張ですね。
拡張はpip install streamlit_js_eval
することでインストールできます。
サンプルコードと実際の実行結果は以下のような感じ。
from streamlit_js_eval import streamlit_js_eval
# JavaScriptを実行してサイズを取得
innerWidth = streamlit_js_eval(js_expressions='window.top.innerWidth', key='WIDTH', want_output=True)
innerHeight = streamlit_js_eval(js_expressions='window.top.innerHeight', key='HEIGHT', want_output=True)
st.write("幅:", innerWidth)
st.write("高さ:", innerHeight)
# keyに指定した名前でセッションステートにも保存されている
st.write("幅:", st.session_state['WIDTH'])
st.write("高さ:", st.session_state['HEIGHT'])
# 以降は説明用の記載。再描画された回数を表示する
if st.session_state.get('RenderCount'):
st.session_state['RenderCount'] += 1
else:
st.session_state['RenderCount'] = 1
st.write("再描画された回数:", st.session_state['RenderCount'])
挙動としては最初に呼び出されたタイミングで非同期にJavaScriptを実行しているようで、その実行結果が出てからStreamlitアプリがrerunされているようです。
そのため、呼び出した直後はNone
が返ってきて、次の瞬間にあらためて実行結果が得られます。先ほどのサンプルコードでも、F5すると必ず再描画が1回されることが確認できます。
単純に実行結果をst.write
するだけであれば、F5したときNone
が一瞬見えるだけで済むのですが、ロジックによっては問題が生じそうなのでご注意ください。
この方法のいいところは、ソースコードがかなりシンプルになることです。
一方で、イマイチなところは、ウィンドウのリサイズに対応していないところ…ウィンドウがリサイズしたことは分からないので、何らかのきっかけでrerunされて初めて最新のウィンドウサイズが取得できます。
ちなみに、海外のフォーラムなどでは以下のようなコード
innerWidth = streamlit_js_eval(js_expressions='window.innerWidth', key='WIDTH', want_output=True)
でOKという記述もあるのですが、Streamlitの拡張はStreamlitアプリ内にiframeが配置されてその中で動作するので、window.innerWidth
で取得できるのは以下の領域の幅になります。
このdivの中にstCustomComponentV1
というクラスのiframeが作成されて、その中でJavaScriptが実行されています。
ちょっとした罠ではあるのですが…あえてこの幅を使いたいシーンもありそう。
ただ、レイアウト上、謎の空間ができてしまうのもイマイチかも…?
ちなみに、streamlit_js_eval以外にもJavaScriptを実行するための拡張があるようです。正しく使えば色々遊べそうなので試してみてくださいね!
st-screen-statsを利用する
st-screen-statsはJavaScriptでウィンドウサイズやら何やらをまとめて取得するための拡張です。
拡張はpip install streamlit-screen-stats
することでインストールできる…ことになっているのですが、依存している他の拡張があるようで、自分の実行環境ではpip install streamlit-screen-stats streamlit-browser-session-storage streamlit-local-storage
とする必要がありました。
サンプルコードと実際の実行結果は以下のような感じ。
import st_screen_stats
# ウィンドウサイズが変更されたときの処理。特に何もしなくてもいい
def onScreenSizeChange(user_message):
print(f"onScreenSizeChange() called: user_message={user_message}")
# ScreenDataでウィンドウサイズを含む色々な値を取得
screenData = st_screen_stats.ScreenData()
# on_changeに関数を指定すると、ウィンドウサイズが変更されたときにその関数が呼ばれる。指定してもしなくても再実行される
screen_stats_result = screenData.st_screen_data(key="screen_stats", on_change=onScreenSizeChange("サイズが変更されました"))
st.write(screen_stats_result)
# keyに指定した名前でセッションステートにも保存されている
st.write(st.session_state["screen_stats"])
スクリーンやウィンドウに関する代表的な値が一気に取得できます。
また、この拡張はウィンドウのリサイズに対応していて、ウィンドウがリサイズされるとぬるりとrerunされます。このとき、コールバックも受け取ることができます。
ScreenData
というクラスを介さないといけないのがちょっと煩雑だなあという気はしますが、総じて使いやすい拡張です。
ちなみに、こちらもstreamlit_js_evalと本質的な仕組みは同じなので、レイアウト上、謎の空間ができます。
まとめ
Streamlitは開発者はPythonオンリーで!という設計思想なので少し遠回りになってしまいますが、ウィンドウサイズを取得することができました!
例えばブラウザが横長のときと縦長のときで表示するコンポーネントを変えたい、みたいなシーンで使ってみるといいかもしれません。