LoginSignup
6
4

More than 1 year has passed since last update.

Streamlit で機械学習, 画像処理系の UI を作るメモ

Last updated at Posted at 2022-10-03

Imgui みたいに Declarative UI でいい感じです.

開発前の一般的な注意点

ページロードするとへんなところにスクロールしてしまう.

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.dataframest.tablekey パラメータがありません.
(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 でがんばります...

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4