2025年5月31日 星期六

Streamlit 學習筆記 : 狀態顯示元件

今天是端午節, 早上忙完祖堂的拜拜後繼續來測試 Streamlit, 本篇旨在測試 Streamlit 的兩個狀態顯示元件的函式 : 進度條元件 st.progress() 與等候元件 st.spinner().

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



1. 進度條元件 st.progress : 

呼叫 st.progress() 函式會建立一個進度條元件, 其參數結構如下 : 

st.progress(value=0, text=None, key=None)

st.progress() 的傳回值為一個 Progress 類別的物件, 我們可以在迴圈中利用其 progress() 方法動態更新進度條的值 (0~100 或 0.0~1.0). 
 
參數說明如下表 :


 st.progress() 參數  說明
 value  必要參數 (預設 0),進度條的進度,整數 (0~100) 或浮點數(0.0~1.0)。
 text  顯示在進度條下方的文字,支援 Markdown 語法與 emoji 表情。
 key  為此進度條元件指定唯一鍵值以避免重複。



例如 : 


測試 1-1 : 直覺但不太理想的進度條 [看原始碼]   

# st-progress-test-1-1.py
import streamlit as st
import time

st.subheader('進度條 st.progress()')
progress_bar=st.progress(0, text='任務處理中 ...')
for i in range(100):
    progress_bar.progress(i, text=f'目前進度:{i}%')    # 更新進度條數值與文字
    time.sleep(0.05)   # 模擬延遲
st.success("✅ 處理完成!")

此例先呼叫 st.progress() 並設定進度初始值為 0, 然後用迴圈與 time.sleep() 模擬處理所需的延遲時間, 在迴圈中呼叫 st.progress() 來更新進度條的值, 結果如下 :




可見進度條上的初始文字訊息 '任務處理中 ...' 一瞬即逝幾乎無作用; 其次, 雖然迴圈從 0~99 剛好 100 圈, 但任務完成時進度顯示會停在 99%, 這會讓使用者誤以為任務還有 1% 未完成, 因此可將上面程式修改為如下較理想的做法 :


測試 1-2 : 較理想的進度條 [看原始碼  

# st-progress-test-1-2.py
import streamlit as st
import time

st.subheader('進度條 st.progress()')
progress_bar=st.progress(0, text='任務處理中 ...')
for i in range(100):
    progress_bar.progress(i, text=f'目前進度:{i}%')    # 更新進度條數值與文字
    time.sleep(0.05)   # 模擬延遲
progress_bar.progress(100, text=f'目前進度:100%')
st.success("✅ 處理完成!")

此例在迴圈結束後再次呼叫 st.progress() 讓進度條之值停在 100, 文字顯示 100%, 這樣使用者就不會誤解任務還有 1% 未完成了, 結果如下 :




或者也可以讓迴圈內從 1% 開始跑到 100% (因為 0% 已在迴圈外初始時跑過了), 這樣就不用在迴圈結束後設定值為 100 了 : 

progress_bar.progress(i + 1, text=f'目前進度:{i + 1}%')    # 更新進度條數值與文字

下面是較進階的範例, 加入了一個可取消任務的按鈕 : 


測試 1-3 : 可取消任務的進度條 [看原始碼  

# st-progress-test-1-3.py
import streamlit as st
import time

st.subheader('進度條 st.progress()')
progress_bar=st.progress(0, text='任務處理中 ...')
st.button("❌ 取消任務", key='cancel')
for i in range(100):
    if st.session_state['cancel']:   # if st.session_state.cancel: 亦可
        progress_bar.progress(0, text='任務取消')
        st.warning('⚠️ 任務已取消')
        break
    progress_bar.progress(i + 1, text=f'目前進度:{i + 1}%')    # 更新進度條數值與文字
    time.sleep(0.05)   # 模擬延遲
if not st.session_state['cancel']:
    st.success("✅ 處理完成!")

此例我們在取消任務的按鈕中用 key 參數指定唯一鍵 cancel, 這樣就會在 st.session_state 字典中以 cancel 為鍵追蹤此按鈕的值 (按鈕有被按下過傳回 True, 否則預設為 False), 我們可以利用此字典取得按鈕的即時狀態, 當它被按下時中斷任務跳出迴圈重設進度條 : 



如果不中斷任務讓進度條跑完結果如下 :




2. 旋轉等待元件 st.spinner() : 

呼叫 st.spinner() 函式並傳入必要參數 text 會建立一個旋轉等待動畫元件, 其參數結構如下 : 

st.spinner(text)

必要參數 text 為顯示在旋轉等待動畫後面的提示文字, 例如 '請耐心等待' 或 '任務執行中' 等, st.camera_input() 的傳回值為一個用來顯示旋轉中的 loading 指示器的上下文管理器  contextlib._GeneratorContextManager 物件, 傳回值本身並無實際用途.  
 
st.spinner() 必須搭配 with 語法使用, 例如 :


測試 2-1 : 模擬的旋轉等候元件 [看原始碼]    

# st-spinner-test-1.py
import streamlit as st
import time

st.subheader('旋轉等待元件 st.spinner()')
with st.spinner('任務執行中, 請耐心等待'):
    time.sleep(15)
st.success("✅ 處理完成!")

此例使用 time.sleep() 模擬處理延遲, 結果如下 : 




可見在等待期間會有一個小圓圈動畫持續旋轉, 迴圈結束後整個等待元件會消失. 

下面則是以檔案上傳為例使用 st.spinner() 顯示等候訊息的範例 : 


測試 2-2 : 檔案上傳時用旋轉等候元件顯示等待訊息 [看原始碼]    

# st-spinner-test-2.py
import streamlit as st
import time

st.subheader('用旋轉等待元件 st.spinner() 顯示等候訊息')
uploaded_file=st.file_uploader('請上傳 MP4 影片', type=['mp4'])
if uploaded_file:  # 檔案上傳完成
    st.write(f'📄 檔案名稱:**{uploaded_file.name}**')
    with st.spinner('處理中請耐心等候 ... '):  # 等候資料處理
        time.sleep(5)  # 這裡可以處理存檔, 轉檔, 解碼等耗時工作
    st.success('✅ 影片上傳成功, 請按 Play 播放.')
    st.video(uploaded_file)
else:  # 尚未選取檔案時顯示提示, 並停止執行等待上傳
    st.info('請上傳一個 MP4 檔案')  # 尚未上傳則提示
    st.stop()     # 停止 rerun 等待使用者選取檔案後再繼續執行程式

此例使用 if else 判斷檔案是否上傳完成, 選取檔案後會立即上傳, 完成後 uploaded_file 會傳回物件, 沒有選取檔案或上傳中會傳回 None, 這時會顯示 st.info() 的提示訊息並以 st.stop() 停止 rerun 動作. 上傳完成則用 st.spinner() 顯示等候資料處理任務 (例如存檔等) 完成之訊息, 此處使用 time.sleep() 來模擬, 結果如下 : 




點選檔案上傳 : 




上傳完成後顯示等候訊息 :




處理完畢顯示播放元件 :




由此例可知, st.spinner() 只是用來表示 "後續處理中" 的視覺提示而已, 與上傳進度本身無關. 

沒有留言 :