Streamlitでアプリを作る際にページ切り替えを入れ込む際の作り方と、能動的にページを再読み込み(=アプリ再起動)させたいけどどうやるんだ!という時のための記事です。
streamlitの挙動と状態保持変数の説明
streamlitではボタンなどのウィジェットの値が変更される度に全てのコードが再実行されます。
なので、ページ遷移を行うためにはページ管理用の変数を作成し、実行の最初でその値により処理を分岐させるという方法を取るのが一般的かと思います。
イメージとしては下記のようなコードになります。
import streamlit as st
def main():
# 1ページ目表示
st.sidebar.title("test_streamlit")
st.markdown("## ボタンでページを変えましょう")
st.sidebar.button("ページ切り替えボタン", on_click=change_page)
def change_page():
# ページ切り替えボタンコールバック
st.session_state["page_control"]=1
def next_page():
# 2ページ目表示
st.sidebar.title("ページが切り替わりました")
st.markdown("## 次のページです")
# 状態保持する変数を作成して確認
if ("page_control" in st.session_state and
st.session_state["page_control"] == 1):
next_page()
else:
st.session_state["page_control"] = 0
main()
※ st.session_state[変数名]でコード再実行が行われても値を保持する変数を定義出来ます
各ウィジェットの引数にあるon_clickやon_changeなどのコールバックを設定した場合、コールバック関数を実行→アプリが再実行という順番で動作します。
上記コードはコールバック関数内で変数の値を更新することでページ遷移を行っています。
※ この時、最初のページから次のページに持ち越したい値がある場合は、同様にst.session_stateで定義しておくか、コールバック関数にargs,kwargsなどのキーワード引数として持ち越すという方法があります
ページ再読み込みのユースケース
あまり実用的ではないかもしれませんが、例えばファイルのアップロードを契機に解析用のページに移動させたい場合などに使えます。
まずは下記のコードを見てみましょう。
import streamlit as st
def main():
# 1ページ目表示
st.sidebar.title("test_streamlit")
st.session_state["file"]=st.sidebar.file_uploader("upload", on_change=change_page)
def change_page():
# ページ遷移ボタンコールバック
st.session_state["page_control"]=1
def next_page():
# 2ページ目表示
st.sidebar.title("ページが切り替わりました")
st.markdown("## 次のページです")
# 状態保持する変数を作成して確認
if ("page_control" in st.session_state and
st.session_state["page_control"] == 1):
next_page()
else:
st.session_state["page_control"] = 0
ページ遷移後の関数内でst.session_state["file"]の中身を確認するとわかるのですが、値はNoneとなってしまっています。
ページ遷移をさせずに下記のような形でファイルの中身を読むと、ちゃんと値が格納されています。
st.session_state["file"]=st.sidebar.file_uploader("upload")
if st.session_state["file"] != None:
st.markdown("ファイルの中身")
st.markdown(st.session_state["file"])
関数の中身を追えていないのでどのような仕様でこうなっているのかわかりませんが、兎にも角にもページ遷移+ファイルアップロード情報を持ち越しをしたい場合は、コールバックのon_changeを使わずになんとかするしかありません。
ここで表題にもあるページ再読み込みの機能が役に立ちます。
解決法
1行加えるだけです。
import streamlit as st
def main():
# 1ページ目表示
st.sidebar.title("test_streamlit")
st.session_state["file"]=st.sidebar.file_uploader("upload")
if st.session_state["file"] != None:
st.session_state["page_control"] = 1
raise st.experimental_rerun()
def next_page():
# 2ページ目表示
st.sidebar.title("ページが切り替わりました")
st.markdown("## 次のページです")
st.markdown("ファイルの中身")
st.markdown(st.session_state["file"])
# 状態保持する変数を作成して確認
if ("page_control" in st.session_state and
st.session_state["page_control"] == 1):
next_page()
else:
st.session_state["page_control"] = 0
main()