🌏

Streamlitを使ってボタンでイベントを起こす処理を試してみた

2022/03/18に公開

streamlit でボタンを押してイベントを実行する処理をいくつか試したのでまとめます。

ポイントは以下のサイトにあるように st.session_state を活用し、ボタンを押した際に streamlit が再実行されても変数を保存することです。

https://qiita.com/kuriyan1204/items/d8342f3a40c6aeb57e5e

コードはこちら

実際にデプロイしたものが以下になります。

https://huggingface.co/spaces/KJMAN678/streamlit_session

session_state 処理

おそらく session_state は辞書型で、最初に下記コマンドで初期値を設定する必要があります。

初期値設定は1度だけ実行するように、key がない場合にのみ 初期値を設定するようにしましょう。

でないとイベントを実行して streamlit が再実行されるたび、延々と初期値設定が行われてしまいます。

import streamlit as st

if 'count' not in st.session_state:
  st.session_state["count"] = 0

上記は初期値に 0 (数値データ) を入れてますが、list でも 辞書でも文字列でもなんでも設定できます。

クリックするたび数字を増やす

session_state["count"] の初期値(0)を設定し、ボタンを押すたび session_state["count"]を +1 してやりましょう。

if 'count' not in st.session_state:
  st.session_state["count"] = 0
  
if st.button("カウント", key=0):
  st.session_state["count"] += 1
  
st.write("カウント", st.session_state["count"])

これでボタンを押した回数を表示することができます。

ボタンを押した数だけテキストを出力する

下記のように、range() の引数に st.session_state["increasement"] を設定し、ボタンを押した回数だけ st.write() 実行するようにしてやります。

if 'increasement' not in st.session_state:
  st.session_state["increasement"] = 0
  
if st.button("カウント", key=1):
  st.session_state["increasement"] += 1

for i in range(st.session_state["increasement"]):
  st.write(f"ボタンを押した回数 {i+1} 回目分")

そうすればボタンを押すたびにテキストが増えていきます。

##

テキスト入力の内容を出力する追加ボタンと、追加したものを1つずつ削除するボタン

今度はテキスト入力フィールドの中身 = 文字列をボタンを押した回数だけ st.session_state に保存したいので、初期値をリストにします。

また、追加ボタンと削除ボタンをキレイにならべたいので st.columns() を使います。

https://docs.streamlit.io/library/api-reference/layout/st.columns

追加ボタンを押せば append()、削除ボタンを押せば remove() を実行するようにしてやります。

最後に先ほどと同じように range() を使って st.session_state["text_list"] の中身を出力してやりましょう。

text = st.text_input("表示したい単語を入力してください")

if 'text_list' not in st.session_state:
  st.session_state["text_list"] = []

col1, col2 = st.columns(2)

with col1:
  if st.button("追加", key=2):
    st.session_state["text_list"].append(text)

with col2:
  if st.button("削除", key=3): 
    st.session_state["text_list"].remove(text)
      
for output_text in st.session_state["text_list"]:
  st.write("", output_text)

追加ボタンを押すたび、テキスト入力フィールドの文字列が出力されます。

また、出力されている文字列をテキスト入力フィールドに入れて削除ボタンを押すと、出力された文字列が消えます。

下記の場合は上記の状態から「キジ」をテキスト入力フィールドにいれて削除ボタンを押したので「キジ」が消えました。

複数のカラムをボタンを押すたび出力する

こんな感じです。

st.columns() で並べたものをそのまま出力すると、行がキレイにそろわないことがあります。

特にそれぞれのカラムの中身のサイズが違うと、行がキレイにそろいません。

こういうときは st.container() を使います。

https://docs.streamlit.io/library/api-reference/layout/st.container

https://docs.streamlit.io/library/api-reference/session-state

ちなみに下記では st.container() 以下の処理を関数化しています。

別にしなくてもいいのですが、関数内に入れた streamlit 処理は、その関数を実行することでまとめて実行することができます。

if 'add_container' not in st.session_state:
  st.session_state["add_container"] = 0

col3, col4 = st.columns(2)

with col3:
  if st.button("追加", key=4):
    st.session_state["add_container"] += 1

with col4:
  if st.button("削除", key=5):
    if st.session_state["add_container"] >= 1:
      st.session_state["add_container"] -= 1


def add_container(row_num):
  with st.container():
    col1, col2, col3 = st.columns(3)
    
    with col1:
      st.write(f"This is left side in {row_num+1} row.")
      
    with col2:
      st.write(f"This is middle side in {row_num+1} row.")
      
    with col3:
      st.write(f"This is right side in {row_num+1} row.")
      
for i in range(st.session_state["add_container"]):
  add_container(i)

再掲ですが、ボタンをおすたびに3つのカラムのテキストを出力されます。

もちろん削除も可能です。

ボタンを押すたびヴィジェットを追加する

最後に、ボタンを押すたびヴィジェットを追加する処理をやってみます。

単純にヴィジェットを追加するだけでなく、ヴィジェットの出力結果もちゃんと保存されるようにしてみましょう。

ボタンを押すたび、 st.slider() でスライダーバーを追加し、その横にスライダーバーで設定した数値が出力されるようにしてみましょう。

ヴィジェットの引数である key (ヴィジェットを個別に認識するためのIDのようなもの)をうまく管理してやれば可能です。

key に重複しない数値を設定するだけでもいいのですが、ここは key にユニークIDを設定してみます。

ユニークIDは uuid を使って生成することができます。

https://rurukblog.com/post/python-uuid/

st.session_state["unique_id"] の初期値を list とし、追加ボタンを押すたびに list にユニークIDを追加、削除ボタンを押すたびに list にユニークIDを削除してやります。

import uuid

if 'unique_id' not in st.session_state:
  st.session_state["unique_id"] = []

col5, col6 = st.columns(2)

with col5:
  if st.button("追加", key=6):
    st.session_state["unique_id"].append(uuid.uuid1())

with col6:
  if st.button("削除", key=7):
    st.session_state["unique_id"].pop(-1)
    
for unique_id in st.session_state["unique_id"]:
  
  with st.container():
    col7, col8 = st.columns(2)

    with col7:
      slider_value = st.slider(
        "数値",
        min_value=0,
        max_value=14,
        value=0,
        key=unique_id
      )
    with col8:
      st.write("")
      st.write("")
      st.write(slider_value)

これでスライダーバーの出力結果も保存したまま、追加ボタンを押せばスライダーバーが増え、削除ボタンを押せばスライダーバーが削除される処理が実装できました。

以上になります、最後までお読みいただきありがとうございました。

Discussion