Imgui みたいに Declarative UI でいい感じです.
開発前の一般的な注意点
- 開発時
.py
リロードはfrom
あたりで取り込んだソースコードには反映されないので注意です.- 効率化やパッケージ化(モジュール化)などのために
.py
を分けている場合ですね - https://github.com/streamlit/streamlit/issues/1131
- https://github.com/streamlit/streamlit/issues/358
- 正確には
streamit run
したディレクトリ以外にある.py
はウォッチしない模様(どれが自作のか既存パッケージなのかわからないからですかね).PYTHONPATH
にウォッチしたいパスを設定でリロードできるかもしれません https://docs.streamlit.io/knowledge-base/using-streamlit/streamlit-watch-changes-other-modules-importing-app
- 効率化やパッケージ化(モジュール化)などのために
- UI の値を保存したり編集したい場合は
st.session_state
に保存します. - ボタン押したらリロードしたい場合などは,
experimental_rerun()
を明示的に呼ぶ必要があります.
ページロードするとへんなところにスクロールしてしまう.
header(#
, ##
) が重複しているからになります.
st.markdown("#")
で改行していたりとか...
(筆者は streamlit 利用のライブラリでそのようなコードがあって原因突き止めるの面倒でした)
重複チェックは面倒そうですが, 頑張って unique な header 割当るようにしましょう.
tab 化していて, レイアウト調整したい場合は
や css で頑張る感じですかね.
ページロードで top にこさせたい
無理やり top にこさせる手もあるようです.
checkbox
value には初期値を指定するので,
st.session_state.check0 = st.checkbox("check0", value=st.session_state.check0)
みたいなことをすると挙動が怪しくなるので注意です.
(チェックしても元に戻ったり...)
value は実行中固定であるのが想定されているが, 変化したらそれで再初期化するからのようです.
後述しますが, on_change
でコールバック仕掛けるなどを考えた場合,
key 指定して, implicit に session_state に保存しそれを取得がいいかもしれません.
st.checkbox("check0", value=True, key="check0_key")
st.write(st.session_state.check0_key)
これは number_input などにも当てはまります.
on_change と連携したい場合は, st.session_state
には初期値を入れらせないので, 初期値は別途管理しないといけずちょいめんどうですね...
ファイルブラウザ
サーバー側のファイルをブラウズして, 処理するファイルを指定とかしたい...
標準ではありません.
こんな感じで自前で頑張ります...
Linux(or macOS) でローカル動作前提であれば, tk を使う手もあるかもしれません.
(リモートで動かすとリモート側で tk のウィンドウが出てしまう)
サーバ側にフォルダを作る.
標準ではありません.
自前でがんばります...
ファイルのアップロード
ただ, フォルダを指定してアップロードはできないようです.
widget の値を操作したい
たとえばスライダー A の値を元に別のスライダー B の値を変更したいなど.
on_change
コールバックと session_state でがんばります.
最近(2022/10)だと, widget を key
付きでつくると, session_state
に自動でステートが生成されるようですので, 明示的に作成
if 'age' not in st.session_state:
st.session_state['age'] = 10
なのは不要です(昔の streamlit の discussion だと明示的に作るのをやってたりする).
def plus_one():
if st.session_state["slider"] < 10:
st.session_state.slider += 1
else:
pass
return
# when creating the button, assign the name of your callback
# function to the on_click parameter
add_one = st.button("Add one to the slider", on_click=plus_one, key="add_one")
slide_val = st.slider("Pick a number", 0, 10, key="slider")
ただ UI 全体にリフレッシュをかけるのかちょっと反応わるいです.
(コールバックで experimental_rerun()
すると多少反応よくなるかも)
session_state["key"]
も session_state.key
もどちらも同じです.
on_click, on_change コールバック
ボタンが押されてたときなどにコールバックをしかけることができます.
ipywidgets などと同じで引数は設定できません.
UI 要素固有のメソッド名にするか, lambda で頑張るか, functool.partial
で対応になります.
st.session_state.text_input cannot be modified after the widget with key ***
is instantiated.
text_input や number_input などの一部?の widget は st.session_state
直書き換えではタイトルのようなエラーがでてしまいます.
https://zenn.dev/alivelimb/books/python-web-frontend/viewer/about-streamlit#%E3%82%B3%E3%83%BC%E3%83%AB%E3%83%90%E3%83%83%E3%82%AF%E3%82%92%E4%BD%BF%E3%81%86%E3%82%B1%E3%83%BC%E3%82%B9
ありがとうございます. コールバック経由で変更が必要でした.
また, 先に session_state で値をセットしておくと反映されないので注意です!
# NG
st.session_state["myval"] = 0.0
st.number_input("val", key="myval")
def update():
# エラーは出ないが反映されない
st.session_state.myval = 1.0
st.button("click", on_click=update)
Image crop, 領域選択
標準ではありません.
あたりでがんばります.
Table
st.dataframe
, st.table
がありますが, エディットできません.
aggrid を streamlit に対応したものでがんばります.
Streamlit st_aggrid でデータテーブル表 UI の調整メモ
https://qiita.com/syoyo/items/1b4027299a97fdf45728
DataFrame でデータを追加するときの注意
ボタン押したりしたら行追加したい...
Pandas append or loc を使います.
append の場合, Pandas の新しいオブジェクトを作るようで, 変数を使うとうまく反映されないので注意です!
(Python の場合変数は基本リファレンス(のはず)と思って処理するとテーブル反映されない...)
df = st.session_state.df
df = df.append({"Title": "Bora"}, ignore_index=True)
# テーブルは反映されない...
st.dataframe(st.session_state.df)
# `st.session_state.df` に結果を返せば反映される
st.session_state.df = df
また,
st.dataframe(st.session_state.df)
if st.button("Update"):
st.session_state.df = ...
のようにするとボタン押してもすぐには反映されません. ↑にあるように experimental_rerun()
でアップデートさせるか, 後述のように placeholder で対応します.
さらに, st.dataframe
と st.table
は key
パラメータがありません.
(pandas のオブジェクトのアドレスあたりで判定しているんじゃろか?)
stdqm
stqdm で iteration ごとに row を追加して dataframe アップデートしたいときもあります.
ただ, stqdm 内で append して st.dataftame
すると新しく dataframe widget が生成されて追加となってしまいます(dataframe は key
が指定できないため, widget を固定化?できない).
for _ in stqdm(range(10)):
st.session_state.df = st.session_state.df.append(...)
st.dataframe(st.session_state.df)
# 複数 dataframe が生成されてしまう. loc で追加の場合も同等
experimental_rerun だと tqdm のイテレーションが終了してしまいますので...
empty() で placeholder を作って対応します!
placeholder = st.empty()
placeholder.dataframe(st.session_state.df)
for _ in stqdm(range(10)):
st.session_state.df = st.session_state.df.append(...)
# widget 作り直し
placeholder.empty()
placeholder.dataframe(st.session_state.df)
plot
ありがとうございます.
組み込みで plot 機能ありました.
matplotlib(pyplot)もあります!
その他 UI
React を使っているので, React or JavaScript でがんばります...