1次、2次、、と深く潜るタイプのページ遷移のテクニックが見つからなかったので自分なりに実装してみました。
ページ遷移
Streamlitを使っていると、
この一連のページ遷移を実装したい場面が出てきます。
これを実現します。
ポップアップボタン
See the Pen Untitled by py guda (@gudapy) on CodePen.
一度切りの押しボタンです。一定時間後に消えるポップアップを表示したり、処理のトリガーとなります。ポップアップメッセージ自体はst.spinner
を使うと出来ます。しかしページ遷移との組み合わせにすると、ページ遷移後にボタン操作などを行った場合、『ページの状態がクリアされてしまう』『ポップアップメッセージが消えない』、といった問題がいくつか出てきます。ページ遷移後の状態を保持しつつポップアップ(ボタン)を実装する、これを実現します。
肝となるStreamlit API
st.session_state()
st.empty()
st.spinner()
ノートブック
Streamlitでのページ遷移とポップアップボタン
動作
ページ移動からの、ページ遷移+ポップアップの一連の動作です。
逐次処理順に番号[n]
を振りました。
GIF動画です
使い方
メニューバー
から、
ランタイム
>すべてのセルを実行
そもそもGoogle Colaboratory
の使い方は、という方はこちら。
ソース
ベースはこれです。
app.py
import streamlit as st
import time
pop()
とpop_btn()
にtime
が必要です。
# config
st.set_page_config(
page_title="Streamlitでのページ遷移とポップアップボタン",
layout="wide",
initial_sidebar_state="expanded"
)
ページのタイトルや幅を設定できます。
st.set_page_config
https://docs.streamlit.io/library/api-reference/utilities/st.set_page_config
# init
def init():
if "init" not in st.session_state:
st.session_state.init=True
reset_session()
count()
return True
else:
return False
# st.sidebar.selectboxの切り替わりを感知
def tab_session():
if not st.session_state.now_tab==st.session_state.tab:
reset_session()
st.session_state.now_tab=st.session_state.tab
def layer_session(layer=0):
st.session_state.layer=layer
def reset_session():
st.session_state.now_tab=None
layer_session()
主に
ページ遷移
処理部分を管理しています。
表示ページの途中でst.sidebar.selectboxが切り替わった場合にlayerをリセットして対応しています。st.session_state
https://docs.streamlit.io/library/api-reference/session-state
# extends
def ck():
if "ck" not in st.session_state:
st.session_state.ck=-1
st.session_state.ck+=1
return st.session_state.ck
def count():
if "count" not in st.session_state:
st.session_state.count=0
st.session_state.count+=1
webアプリとしての処理部分です。例えばここに
APIを叩く処理
など好きな処理を追加していきます。
# UI components
def deco_horizontal(func):
def wrapper(*args, **kwargs):
st.write("---")
func(*args, **kwargs)
st.write("---")
return wrapper
@deco_horizontal
def back_btn():
st.button(f"戻る。[{ck()}]",on_click=reset_session)
def btn(label="ボタン",key=None,onclick=None,done=None):
if st.button(label,key=key,on_click=onclick) and done:
done()
def pop_btn(label="pop",key=None, layer=0, onclick=lambda:None, done=None, description=None):
placeholder=st.empty()
with placeholder.container():
if description:
st.write(description)
res=st.button(label,key=key,on_click=lambda:[placeholder.empty(),layer_session(layer),onclick()])
if res:
if done:
with placeholder:
done()
placeholder.empty()
def pop(msg, done=None, interval=1):
with st.spinner(msg):
time.sleep(interval)
if done:
done()
ポップアップボタンと必要最低限のUI部品です。
ポップアップ後の処理とポップアップボタンそのものが残らないようにplaceholder.empty()
によってクリアしています。
done
には一度表示させて消したい
処理を登録します。
onclick
には表示に関わりのない
処理を登録します。
onclick
にst.write
などを登録すると(逐次処理的に)ページ上部に表示されてしまいます。st.empty
https://docs.streamlit.io/library/api-reference/layout/st.empty
# contents
def index():
st.write("ここは **Indexページ** です。")
def sample_content(i):
st.write(f"ここは **ページ {i}** です。[{ck()}]")
pop_btn(
label=f"POPボタン",
layer=2,
onclick=lambda:[count(),print(f"ck[{ck()}]")],
done=lambda:[
pop(f"POPボタンが押されました[{ck()}]"),
st.success(f"成功![{ck()}]"),
time.sleep(1)
]
)
ページのコンテンツ部分です。
ボタンが押された直後にカウントするようonclick
にcount()を登録しました。
ボタンが押された後の表示処理としてdone
に逐次処理を登録しています。
st.columns
やst.sidebar
によって出力場所を変えることもできます。st.columns
https://docs.streamlit.io/library/api-reference/layout/st.columns
st.sidebar
https://docs.streamlit.io/library/api-reference/layout/st.sidebar
# body
init()
st.session_state.ck=0
st.session_state.tab = st.sidebar.selectbox("選択してください。", ["Index","List"])
tab_session()# TAB切り替えの管理
# delay
time.sleep(0.1)
#
_tab=st.session_state.tab
_layer=st.session_state.layer
if _tab=="Index":
if _layer==0 or _layer==1:
layer_session(1)
index()
elif _tab=="List":
if _layer==0 or _layer==1:
layer_session(1)
sample_content(1)
elif _layer==2:
st.info(f"POPによるページ遷移をしました。[{ck()}]")
sample_content(1)
back_btn()
各コンテンツには
st.sidebar
でアクセスします。
st.sidebar
でなくとも、条件分岐で表示させることができれば、どの方法でも良いと思います。
今どこのページにいるかはst.sesion_state.tab
とst.session_state.layer
で判別しています。
st.session_state.tab
は各コンテンツ。
st.session_state.layer
は深層で分岐することによってコンテンツ内遷移をしています。
st.sidebar
https://docs.streamlit.io/library/api-reference/layout/st.sidebar
おわりに
汎用的に使えて動作もしますが、やり方はこれであっているのかはわかりません。
他にもっと良い方法があるのかもしれません。
参考