2025年6月4日 星期三

Streamlit 學習筆記 : 容器與版面配置元件 (六) 表單元件

表單是用來收集使用者輸入資料的重要 UI 元件, 本篇旨在測試 Streamlit 的兩個表單元件函式 st.form() 與表單提交按鈕函式 st.form_submit_button(). 

本系列所有測試文章參考 : 


呼叫 st.form() 函式並傳入必要參數 key 會建立一個表單元件, 其參數結構如下 : 

st.form(key, clear_on_submit=False, border=True)


 st.form() 參數  說明
key 表單的唯一識別鍵 (必要參數),用來避免元件重複 (不顯示)。
clear_on_submit提交後是否清除欄位資料 (True/False, 預設 False)。
border 是否顯示表單外框 (True/False, 預設 True)。自 Streamlit v1.18+ 起支援。


傳回值為一個 DeltaGenerator 容器物件, 它必須搭配 with 語法才能在表單中加入 UI 元件, 例如 :

with st.form(key='login_form'):
    account=st.text_input('帳號:')
    password=st.text_input('密碼:', type='password')
    login=st.form_submit_button('登入')

表單元件 st.form() 內的最後一個 UI 元件通常是用來提交表單的 st.form_submit_button() 按鈕, 它的外觀與一般按鈕元件 st.button() 相同, 但功能上有所不同 :
  • st.form_submit_button() 是設計來與 st.form() 搭配使用以實現延遲提交機制. 所謂延遲提交意思是表單中的所有輸入元件例如 st.text_input(), st.slider(), st.selectbox() 等在未按下提交按鈕之前, 其輸入值無法被程式邏輯取得. 
  • 雖然使用者可以即時操作這些輸入元件 (例如拖曳滑桿或輸入文字等), 元件也會即時顯示變化, 但只有當 st.form_submit_button() 被按下時整個表單區塊才會被一次提交並觸發頁面重新執行, 這時程式邏輯才能取得到輸入元件最新的值. 
而一般按鈕 st.button() 則是即時觸發的元件, 一按就會讓整個頁面立刻 rerun, 因此若 UI 元件沒放在表單容器內, 則程式邏輯可以即時取得其輸入值.  

提交按鈕的參數結構如下 (與一般按鈕相同) : 

st.form_submit_button(label, key=None, help=None, on_click=None, args=None, kwargs=None, disabled=False, use_container_width=False)

參數說明如下表 :


st.form_submit_button() 參數 說明
label 按鈕上顯示的文字(必耀參數)。
key 元件的唯一識別字串。用於避免與其他按鈕衝突。
help 滑鼠懸停時顯示的提示說明文字。
on_click 當按鈕被點擊時要呼叫的函式(通常搭配 callback 使用)。
args 傳入 on_click 的位置參數(tuple)。
kwargs 傳入 on_click 的關鍵字參數(dict)。
disabled 設為 True 時按鈕會呈現灰色並不可點擊。
use_container_width 設為 True 時按鈕會撐滿整個容器寬度。


例如 : 


測試 1 : 預設的表單 [看原始碼]   

# st-form-test-1.py
import streamlit as st

with st.form(key='login_form'):
    account=st.text_input('帳號:')
    password=st.text_input('密碼:', type='password')
    if st.form_submit_button('取得表單輸入', type='primary'):
        st.write(f'帳號:{account}')
        st.write(f'密碼:{password}')     
if st.button('取得表單輸入'):
    st.write(f'帳號:{account}')
    st.write(f'密碼:{password}')
   
此例在表單區塊內有一個提交按鈕; 外面則有一個一般按鈕, 初始化結果如下 :




在表單內輸入帳號密碼, 然後先按表單外的一般按鈕, 結果如下 : 




由於表單尚未提交, 所以即使兩個文字欄位都有輸入資料, 但表單外的程式邏輯 (即一般按鈕的取值) 無法取得表單內的輸入值. 這時按下表單內的提交按鈕 : 




可見表單提交會觸發程式 rerun 重新渲染網頁, 這時表單內的 if 為真因而輸出帳密, 表單外原來輸出的空帳密都被清除. 這時再次按下外面的一般按鈕會馬上觸發 rerun, 表單內原先的輸出會被清掉, 而原先提交的帳密資料還保存在 st.session_state 字典裡, 所以能夠取得其值 :




上例中的預設表單有邊界, 且提交後不會清除欄位內的輸入值, 下面範例測試參數 border=False 與 clear_on_submit=True 的效果 :


測試 2 : 參數 broder=False 與 clear_on_submit=True 的效果 [看原始碼]   

# st-form-test-2.py
import streamlit as st

with st.form(key='login_form', border=False, clear_on_submit=True):
    account=st.text_input('帳號:')
    password=st.text_input('密碼:', type='password')
    if st.form_submit_button('提交', type='primary'):
        st.write(f'帳號:{account}')
        st.write(f'密碼:{password}')  

此例將參數 border 設為 False (無邊界), clear_on_submit 設為 True (提交後清除欄位), 下面是輸入欄位值尚未提交時, 可見表單無邊界 :




按下提交鈕後表單內的所有欄位都被清空 :




下面範例測試提交按鈕的 on_click 與 args 參數 : 


測試 3 : 測試提交按鈕的 on_click 參數 [看原始碼]  

# st-form-test-3.py
import streamlit as st

def on_submit_click():
    account=st.session_state['account']
    password=st.session_state['password']
    st.session_state['login_info']=f'帳號:{account}\n\n密碼:{password}'

with st.form(key='login_form'):
    account=st.text_input('帳號:', key='account')
    password=st.text_input('密碼:', key='password', type='password')
    st.form_submit_button(
        '提交',
        type='primary',
        on_click=on_submit_click,
        )
       
if 'login_info' in st.session_state:
    st.write(st.session_state['login_info'])
   
此例於表單內的 st_text_input() 元件上添加 key 參數以便能於 st.session_state 字典中紀錄此兩欄位之值, 當按下提交按鈕時呼叫 on_submit_click(), 然後從 st.session_state 字典中取得欄位值產生輸出字串並記錄於 st.session_state 字典的 login_info 鍵, 結果如下 :




注意, 由於表單延遲提交特性, 如果把表單欄位值利用 args 或 kwargs 傳遞將無法取得輸入之值, 所以雖然提交按鈕 st.form_submit_button() 有 args 與 kwargs 這兩個參數, 但那只是繼承一般按鈕物件而來的, 它們在提交按鈕上沒有實質用處.

沒有留言 :