Using forms

当您不想每次用户输入后重新运行脚本时,st.form 可以帮助您!表单使得将用户输入批量处理为一次重新运行变得容易。本使用表单的指南提供了示例,并解释了用户如何与表单交互。

在以下示例中,用户可以设置多个参数来更新地图。当用户更改参数时,脚本不会重新运行,地图也不会更新。当用户点击标有“更新地图”的按钮提交表单时,脚本将重新运行,地图将更新。

如果用户在任何时候点击表单外的“生成新点”,脚本将重新运行。如果用户在表单中有任何未提交的更改,这些更改将不会随重新运行一起发送。对表单所做的所有更改只有在表单本身提交时才会发送到Python后端。

View source codeexpand_more
import streamlit as st import pandas as pd import numpy as np def get_data(): df = pd.DataFrame({ "lat": np.random.randn(200) / 50 + 37.76, "lon": np.random.randn(200) / 50 + -122.4, "team": ['A','B']*100 }) return df if st.button('Generate new points'): st.session_state.df = get_data() if 'df' not in st.session_state: st.session_state.df = get_data() df = st.session_state.df with st.form("my_form"): header = st.columns([1,2,2]) header[0].subheader('Color') header[1].subheader('Opacity') header[2].subheader('Size') row1 = st.columns([1,2,2]) colorA = row1[0].color_picker('Team A', '#0000FF') opacityA = row1[1].slider('A opacity', 20, 100, 50, label_visibility='hidden') sizeA = row1[2].slider('A size', 50, 200, 100, step=10, label_visibility='hidden') row2 = st.columns([1,2,2]) colorB = row2[0].color_picker('Team B', '#FF0000') opacityB = row2[1].slider('B opacity', 20, 100, 50, label_visibility='hidden') sizeB = row2[2].slider('B size', 50, 200, 100, step=10, label_visibility='hidden') st.form_submit_button('Update map') alphaA = int(opacityA*255/100) alphaB = int(opacityB*255/100) df['color'] = np.where(df.team=='A',colorA+f'{alphaA:02x}',colorB+f'{alphaB:02x}') df['size'] = np.where(df.team=='A',sizeA, sizeB) st.map(df, size='size', color='color')

如果一个小部件不在表单中,每当用户更改其值时,该小部件将触发脚本重新运行。对于具有键输入的小部件(st.number_inputst.text_inputst.text_area),当用户点击或离开小部件时,新值会触发重新运行。用户还可以在光标处于小部件活动状态时按下Enter键提交更改。

另一方面,如果一个小部件位于表单内部,当用户点击或离开该小部件时,脚本不会重新运行。对于表单内部的小部件,脚本将在表单提交时重新运行,并且表单内的所有小部件将将其更新后的值发送到Python后端。

Forms

用户可以在光标位于接受键盘输入的小部件中时,使用键盘上的Enter键提交表单。在st.number_inputst.text_input中,用户按下Enter键提交表单。在st.text_area中,用户按下Ctrl+Enter/⌘+Enter提交表单。

Keyboard-submit forms

在表单提交之前,表单内的所有小部件都将具有默认值,就像表单外的小部件具有默认值一样。

import streamlit as st with st.form("my_form"): st.write("Inside the form") my_number = st.slider('Pick a number', 1, 10) my_color = st.selectbox('Pick a color', ['red','orange','green','blue','violet']) st.form_submit_button('Submit my picks') # This is outside the form st.write(my_number) st.write(my_color)

当调用st.form时,前端会创建一个容器。你可以像处理其他容器元素一样向该容器写入内容。也就是说,你可以使用Python的with语句,如上例所示,或者你可以将表单容器分配给一个变量并直接调用其方法。此外,你可以在表单容器中的任何位置放置st.form_submit_button

import streamlit as st animal = st.form('my_animal') # This is writing directly to the main body. Since the form container is # defined above, this will appear below everything written in the form. sound = st.selectbox('Sounds like', ['meow','woof','squeak','tweet']) # These methods called on the form container, so they appear inside the form. submit = animal.form_submit_button(f'Say it with {sound}!') sentence = animal.text_input('Your sentence:', 'Where\'s the tuna?') say_it = sentence.rstrip('.,!?') + f', {sound}!' if submit: animal.subheader(say_it) else: animal.subheader(' ')

表单的目的是覆盖Streamlit的默认行为,即一旦用户做出更改,脚本就会重新运行。对于表单外的小部件,逻辑流程是:

  1. 用户在前端更改了小部件的值。
  2. 小部件的值在st.session_state和Python后端(服务器)中更新。
  3. 脚本重新运行开始。
  4. 如果小部件有回调函数,它将在页面重新运行之前作为前缀执行。
  5. 当重新运行期间执行更新后的小部件功能时,它会输出新值。

对于表单内的小部件,用户所做的任何更改(步骤1)在表单提交之前不会传递到Python后端(步骤2)。此外,表单内唯一可以具有回调函数的小部件是st.form_submit_button。如果您需要使用新提交的值执行过程,您有三种主要模式可以这样做。

如果您需要在表单提交后执行一次性过程,您可以将该过程条件化在st.form_submit_button上,并在表单之后执行它。如果您需要过程的结果显示在表单上方,您可以使用容器来控制表单相对于输出的显示位置。

import streamlit as st col1,col2 = st.columns([1,2]) col1.title('Sum:') with st.form('addition'): a = st.number_input('a') b = st.number_input('b') submit = st.form_submit_button('add') if submit: col2.title(f'{a+b:.2f}')

您可以使用回调来执行一个过程,作为脚本重新运行的前缀。

priority_high

重要

在回调中处理新更新的值时,不要通过argskwargs参数直接将这些值传递给回调。您需要为在回调中使用的任何小部件分配一个键。如果您在回调的主体中从st.session_state查找该小部件的值,您将能够访问新提交的值。请参见下面的示例。

import streamlit as st if 'sum' not in st.session_state: st.session_state.sum = '' def sum(): result = st.session_state.a + st.session_state.b st.session_state.sum = result col1,col2 = st.columns(2) col1.title('Sum:') if isinstance(st.session_state.sum, float): col2.title(f'{st.session_state.sum:.2f}') with st.form('addition'): st.number_input('a', key = 'a') st.number_input('b', key = 'b') st.form_submit_button('add', on_click=sum)

如果你的进程影响了表单上方的内容,另一种选择是使用额外的重新运行。不过,这可能不太节省资源,并且可能不如上述选项理想。

import streamlit as st if 'sum' not in st.session_state: st.session_state.sum = '' col1,col2 = st.columns(2) col1.title('Sum:') if isinstance(st.session_state.sum, float): col2.title(f'{st.session_state.sum:.2f}') with st.form('addition'): a = st.number_input('a') b = st.number_input('b') submit = st.form_submit_button('add') # The value of st.session_state.sum is updated at the end of the script rerun, # so the displayed value at the top in col2 does not show the new sum. Trigger # a second rerun when the form is submitted to update the value above. st.session_state.sum = a + b if submit: st.rerun()
  • 每个表单必须包含一个st.form_submit_button
  • st.buttonst.download_button 不能添加到表单中。
  • st.form 不能嵌入到另一个 st.form 中。
  • 回调函数只能分配给表单内的st.form_submit_button;表单中的其他小部件不能有回调。
  • 表单内相互依赖的小部件不太可能特别有用。如果你将widget1的值传递给widget2,而它们都在表单内,那么widget2只会在表单提交时更新。
forum

还有问题吗?

我们的 论坛 充满了有用的信息和Streamlit专家。