streamlit 使用
streamlit 框架的一些用法,整理自官方文档:Streamlit documentation
版本:1.38
安装、运行
# 安装
pip install streamlit
# 运行
## 方式1
streamlit run app.py
## 方式2 作为 Python 模块运行
python -m streamlit run app.py
## 方式3 通过URL
streamlit run https://raw.githubusercontent.com/streamlit/demo-uber-nyc-pickups/master/streamlit_app.py
给脚本传参:
必须在两个连字符之后传递。否则,这些参数会被解释为 Streamlit 本身的参数
streamlit run your_script.py [-- script args]
概念
架构 & 执行
Caching
Session State
Forms
Fragments
小部件行为
- 一个用户的操作不会影响任何其他用户的部件
- 小部件函数调用返回小部件的当前值,该值是一个简单的 Python 类型(例如,
st.button
返回一个布尔值) - 小部件在用户与其交互之前的第一次调用时返回其默认值
- 小部件的身份取决于传递给小部件函数的参数。更改小部件的标签、最小值或最大值、默认值、占位符文本、帮助文本或键将导致它重置
- 如果在脚本运行中不调用小部件函数,Streamlit 将删除小部件的信息,包括其在会话状态中的键值对。如果稍后调用相同的小部件函数,Streamlit 会将其视为一个新小部件
同一页面上不能出现两个相同小部件,需要用key区分
# This will cause a DuplicateWidgetID error.
st.button("OK")
st.button("OK")
st.button("OK", key="privacy")
st.button("OK", key="terms")
如果小部件的任何定义参数发生变化,Streamlit 将把它视为一个新小部件,并进行重置
将小部件值保存在会话状态中,以便在页面之间保留它们
import streamlit as st
def store_value(key):
st.session_state[key] = st.session_state["_"+key]
def load_value(key):
st.session_state["_"+key] = st.session_state[key]
load_value("my_key")
st.number_input("Number of filters", key="_my_key", on_change=store_value, args=["my_key"])
小部件生命周期
当 Streamlit 执行脚本运行到最后时,它将删除所有未在屏幕上渲染的小部件的内存数据
中断小部件清理过程
要保留带有 key="my_key"
的小部件的信息,只需将此代码添加到每个页面的顶部:
st.session_state.my_key = st.session_state.my_key
在更改小部件参数时保留状态
import streamlit as st
# Set default value
if "a" not in st.session_state:
st.session_state.a = 5
cols = st.columns(2)
minimum = cols[0].number_input("Min", 1, 5, key="min")
maximum = cols[1].number_input("Max", 6, 10, 10, key="max")
def update_value():
# Helper function to ensure consistency between widget parameters and value
st.session_state.a = min(st.session_state.a, maximum)
st.session_state.a = max(st.session_state.a, minimum)
# Validate the slider value before rendering
update_value()
st.slider("A", minimum, maximum, key="a")
update_value()
辅助函数在做两件事
它确保参数值没有不一致的更改,它也中断了小部件清理过程
当小部件的最小值或最大值发生变化时,Streamlit 会将其视为运行时的新小部件
如果没有将值保存到 st.session_state.a
,该值将被丢弃并被“新”小部件的默认值替换
多页面应用
目录结构
项目目录/
├── pages/
│ ├── a_page.py
│ └── another_page.py
└── your_homepage.py
可以在文件名中使用数字前缀来调整页面顺序
项目目录/
├── pages/
│ ├── 1_a_page.py
│ └── 2_another_page.py
└── your_homepage.py=
如果想使用此选项自定义导航菜单,可以通过配置( client.showSidebarNavigation = false
)停用默认导航
然后,使用 st.page_link
手动构建自定义导航菜单
使用 st.page_link
,可以更改导航菜单中的页面标签和图标,但不能更改页面的 URL
术语
一个页面包含以下四个识别部分:
- Page source 页面源代码:一个包含页面源代码的 Python 文件或可调用函数
- Page label 页面标签:页面在导航菜单中识别的方式
- Page title 页面标题:HTML
<title>
元素的内容,以及该页面在浏览器标签中的标识方式 - Page URL pathname 页面 URL 路径名:该页面相对于应用根 URL 的相对路径
页面可以有两个图标:
- Page favicon 页面图标:浏览器选项卡中紧邻页面标题的图标
- Page icon 页面图标:导航菜单中页面标签旁边的图标
自动页面标签和 URL
文件名和可调用部分
number
: 一个非负整数separator
: 任何下划线 ("_"
), 破折号 ("-"
), 和空格 (" "
) 的组合identifier
: 直到但不包括".py"
".py"
如何将文件名转换为标签和标题
页面的 identifier
中的任何下划线都被视为空格。因此,前导和尾随的下划线不会显示。连续的下划线将显示为一个空格
Awesome page:
"Awesome page.py"
"Awesome_page.py"
"02Awesome_page.py"
"--Awesome_page.py"
"1_Awesome_page.py"
"33 - Awesome page.py"
Awesome_page()
_Awesome_page()
__Awesome_page__()
在页面之间导航
导航菜单,该菜单出现在侧边栏中
用户单击此导航小部件时,应用程序会重新运行并加载选定的页面
可以选择隐藏默认导航 UI 并使用 st.page_link
构建自己的导航菜单
使用 st.switch_page
通过编程切换页面
通过 URL 在页面之间导航会创建一个新的浏览器会话
页面、导航
your-repository/ ├── page_1.py ├── page_2.py └── streamlit_app.py
import streamlit as st
pg = st.navigation([st.Page("page_1.py"), st.Page("page_2.py")])
pg.run()
定义页面
your-repository/ ├── create.py ├── delete.py └── streamlit_app.py
import streamlit as st
create_page = st.Page("create.py", title="Create entry", icon=":material/add_circle:")
delete_page = st.Page("delete.py", title="Delete entry", icon=":material/delete:")
pg = st.navigation([create_page, delete_page])
st.set_page_config(page_title="Data manager", page_icon=":material/edit:")
pg.run()
自定义导航
st.navigation
允许在导航中插入标题
或者禁用默认的导航小部件,并使用 st.page_link
构建自定义导航菜单
可以动态更改传递给 st.navigation
的页面。但是,只有由 st.navigation
返回的页面接受 .run()
方法
EG:
当用户开始新会话时,他们未登录。在这种情况下,唯一可用的页面是登录页面。如果用户尝试通过 URL 访问其他页面,它将创建一个新会话,Streamlit 不会识别该页面。用户将被重定向到登录页面。但是,在用户登录后,他们将看到一个包含三个部分的导航菜单,并被定向到仪表板作为应用程序的默认页面(即主页)
your-repository/ ├── reports │ ├── alerts.py │ ├── bugs.py │ └── dashboard.py ├── tools │ ├── history.py │ └── search.py └── streamlit_app.py
import streamlit as st
if "logged_in" not in st.session_state:
st.session_state.logged_in = False
def login():
if st.button("Log in"):
st.session_state.logged_in = True
st.rerun()
def logout():
if st.button("Log out"):
st.session_state.logged_in = False
st.rerun()
login_page = st.Page(login, title="Log in", icon=":material/login:")
logout_page = st.Page(logout, title="Log out", icon=":material/logout:")
dashboard = st.Page(
"reports/dashboard.py", title="Dashboard", icon=":material/dashboard:", default=True
)
bugs = st.Page("reports/bugs.py", title="Bug reports", icon=":material/bug_report:")
alerts = st.Page(
"reports/alerts.py", title="System alerts", icon=":material/notification_important:"
)
search = st.Page("tools/search.py", title="Search", icon=":material/search:")
history = st.Page("tools/history.py", title="History", icon=":material/history:")
if st.session_state.logged_in:
pg = st.navigation(
{
"Account": [logout_page],
"Reports": [dashboard, bugs, alerts],
"Tools": [search, history],
}
)
else:
pg = st.navigation([login_page])
pg.run()
如果希望对导航菜单有更多控制权,可以隐藏默认导航并构建自己的导航菜单。可以在 st.navigation
命令中包含 position="hidden"
来隐藏默认导航。如果希望一个页面对用户可用,但不在导航菜单中显示,则必须使用此方法。如果页面未包含在 st.navigation
中,则用户无法被路由到该页面。这适用于通过 URL 进行导航以及 st.switch_page
和 st.page_link
等命令
页面目录
当入口文件旁边有一个 pages/
目录时,Streamlit 会将其中的每个 Python 文件识别为一个页面
your_working_directory/ ├── pages/ │ ├── a_page.py │ └── another_page.py └── your_homepage.py
侧边栏中页面的排序方式
Examples:
Filename | Rendered label |
---|---|
1 - first page.py | first page |
12 monkeys.py | monkeys |
123.py | 123 |
123_hello_dear_world.py | hello dear world |
_12 monkeys.py | 12 monkeys |
使用 st.set_page_config
设置 title
或 favicon
时,仅适用于当前页面
使用 st.set_page_config
设置 layout
时,该设置将保持在会话期间,直到通过另一个调用 st.set_page_config
进行更改
如果使用 st.set_page_config
来设置 layout
,建议在所有页面上调用它
页面在全局范围内共享相同的 Python 模块:
# page1.py
import foo
foo.hello = 123
# page2.py
import foo
st.write(foo.hello) # If page1 already executed, this writes 123
页面共享相同的 st.session_state:
# page1.py
import streamlit as st
if "shared" not in st.session_state:
st.session_state["shared"] = True
# page2.py
import streamlit as st
st.write(st.session_state["shared"]) # If page1 already executed, this writes True
在多页应用中使用小部件
在 Streamlit 应用程序中创建小部件时,Streamlit 会生成一个小部件 ID 并使用它使小部件具有状态
随着应用程序在用户交互下重新运行,Streamlit 通过将小部件的值与其 ID 关联来跟踪小部件的值
小部件的 ID 取决于创建它的页面。如果在两个不同的页面上定义了相同的小部件,那么当切换页面时,该小部件将重置为其默认值
选项 1(推荐):在入口文件执行部件命令
当在入口文件中执行小部件命令时,Streamlit 会将小部件与入口文件关联,而不是与特定页面关联
由于入口文件在每次应用程序重新运行时都会执行,因此入口文件中的任何小部件在用户在页面之间切换时都将保持状态
选项 2:将窗口小部件值保存到会话状态中的一个虚拟键中
如果想从一个部件导航离开并返回到它,同时保留它的值,或者如果想在多个页面上使用同一个部件,请在 st.session_state
中使用一个单独的键来独立保存值,与部件无关
在本例中,一个临时键与部件一起使用。临时键使用下划线前缀。因此, "_my_key"
用作部件键,但数据被复制到 "my_key"
以便在页面之间保存
import streamlit as st
def store_value():
# Copy the value to the permanent key
st.session_state["my_key"] = st.session_state["_my_key"]
# Copy the saved value to the temporary key
st.session_state["_my_key"] = st.session_state["my_key"]
st.number_input("Number of filters", key="_my_key", on_change=store_value)
如果将其功能化以与多个小部件一起使用,它可能看起来像这样:
import streamlit as st
def store_value(key):
st.session_state[key] = st.session_state["_"+key]
def load_value(key):
st.session_state["_"+key] = st.session_state[key]
load_value("my_key")
st.number_input("Number of filters", key="_my_key", on_change=store_value, args=["my_key"])
选项三:中断小部件清理过程
当 Streamlit 运行结束时,它会删除所有未渲染的小部件的数据。这包括与当前页面无关的任何小部件的数据。但是,如果在应用程序运行期间重新保存键值对,Streamlit 不会将键值对与任何小部件关联,直到再次使用该键执行小部件命令
如果在每个页面的顶部都有以下代码,那么任何带有键 "my_key"
的小部件将在其渲染(或不渲染)的位置保持其值。或者,如果使用 st.navigation
和 st.Page
,则可以在执行页面之前在入口文件中包含一次
if "my_key" in st.session_state:
st.session_state.my_key = st.session_state.my_key
应用 设计
动画、更新元素
按钮行为及示例
使用 st.button
创建的按钮不会保留状态
在因点击按钮而重新运行脚本时,它们返回 True
,然后在下次脚本重新运行时立即返回 False
如果一个显示元素嵌套在 if st.button('Click me'):
内部,当点击按钮时该元素将可见,并在用户采取下一动作后立即消失
这是因为脚本重新运行,按钮的返回值变为 False
何时使用 if st.button()
当代码依赖于按钮的值时,它将在按钮被点击时执行一次,而不会再次执行(直到按钮再次被点击)
适合在嵌套按钮内:
- 瞬时消失的消息
- 每次点击保存数据的过程,数据可以保存到会话状态、文件或数据库中
不适合在按钮内部嵌套:
- 展示在用户继续时应保持的项目
- 其他小部件在使用时会导致脚本重新运行
- 既不修改会话状态也不写入文件/数据库的进程
带按钮的常见逻辑
显示带有按钮的临时消息
想给用户一个按钮,以检查条目是否有效,但在用户继续操作时不显示该检查
import streamlit as st
animal_shelter = ['cat', 'dog', 'rabbit', 'bird']
animal = st.text_input('Type an animal')
if st.button('Check availability'):
have_it = animal.lower() in animal_shelter
'We have that animal!' if have_it else 'We don\'t have that animal.'
用户可以点击一个按钮来检查他们的 animal
字符串是否在 animal_shelter
列表中
当用户点击“检查可用性”时,他们会看到“我们有这种动物!”或“我们没有这种动物”
如果在 st.text_input
中更改动物,脚本将重新运行,并且消息会消失,直到他们再次点击“检查可用性”
有状态按钮
希望一个被点击的按钮继续保持 True
,请在 st.session_state
中创建一个值,并在回调中使用该按钮将该值设置为 True
import streamlit as st
if 'clicked' not in st.session_state:
st.session_state.clicked = False
def click_button():
st.session_state.clicked = True
st.button('Click me', on_click=click_button)
if st.session_state.clicked:
# The message and nested widget will remain on the page
st.write('Button clicked!')
st.slider('Select a value')
切换按钮
希望一个按钮像切换开关那样工作,可以考虑使用 st.checkbox
否则,可以使用一个带回调函数的按钮来反转保存在 st.session_state
中的布尔值
使用 st.button
来切换另一个小部件的显示与隐藏。通过根据 st.session_state
中的值有条件地显示 st.slider
,用户可以与滑块交互,而不会使其消失:
import streamlit as st
if 'button' not in st.session_state:
st.session_state.button = False
def click_button():
st.session_state.button = not st.session_state.button
st.button('Click me', on_click=click_button)
if st.session_state.button:
# The message and nested widget will remain on the page
st.write('Button is on!')
st.slider('Select a value')
else:
st.write('Button is off!')
或者,在滑块的 disabled
参数上使用 st.session_state
中的值:
import streamlit as st
if 'button' not in st.session_state:
st.session_state.button = False
def click_button():
st.session_state.button = not st.session_state.button
st.button('Click me', on_click=click_button)
st.slider('Select a value', disabled=st.session_state.button)
继续或控制流程阶段的按钮
脚本中有四个阶段:
- 在用户开始之前
- 用户输入姓名
- 用户选择一种颜色
- 用户收到感谢信息
开始的按钮将阶段从 0 推进到 1
结束的按钮将阶段从 3 重置到 0
阶段 1 和 2 中使用的其他小部件具有回调以设置阶段
如果有一个具有依赖步骤的流程,并且想要保持之前的阶段可见,则此类回调会强制用户在更改早期小部件时重新跟踪后续阶段
import streamlit as st
if 'stage' not in st.session_state:
st.session_state.stage = 0
def set_state(i):
st.session_state.stage = i
if st.session_state.stage == 0:
st.button('Begin', on_click=set_state, args=[1])
if st.session_state.stage >= 1:
name = st.text_input('Name', on_change=set_state, args=[2])
if st.session_state.stage >= 2:
st.write(f'Hello {name}!')
color = st.selectbox(
'Pick a Color',
[None, 'red', 'orange', 'green', 'blue', 'violet'],
on_change=set_state, args=[3]
)
if color is None:
set_state(2)
if st.session_state.stage >= 3:
st.write(f':{color}[Thank you!]')
st.button('Start Over', on_click=set_state, args=[0])