streamlit-同步输入字段

问题描述

我有两个输入字段,这些字段允许用户通过

进行选择
  • ID,在输入的数字中
  • (排序的)名称,在选择框中

更改一个输入字段应该更新另一个输入字段,以使其保持同步。

enter image description here

您如何通过streamlit实现这种行为?

到目前为止我尝试过的事情

已选择ID->更新名称选择框:

users = [(1,'Jim'),(2,(3,'Jane')]
users.sort(key=lambda user: user[1])  # sort by name

selected_id = st.sidebar.number_input('ID',value=1)

options = ['%s (%d)' % (name,id) for id,name in users]
index = [i for i,user in enumerate(users) if user[0] == selected_id][0]
selected_option = st.sidebar.selectbox('Name',options,index)

选择名称->输入更新ID编号(使用st.empty()):

users = [(1,'Jane')]
users.sort(key=lambda user: user[1])  # sort by name

id_input = st.sidebar.empty()

options = ['%s (%d)' % (name,name in users]
selected_option = st.sidebar.selectbox('Name',options)

# e.g. get 2 from "Jim (2)"
id = int(re.match(r'\w+ \((\d+)\)',selected_option).group(1))
selected_id = id_input.number_input('ID',value=id)

解决方法

要保持小部件同步,需要解决两个问题:

  1. 我们需要能够判断任一小部件何时导致当前选择发生变化;和
  2. 我们需要在脚本结束时更新两个小部件的状态,以便在重新运行脚本以进行视觉更新时浏览器保持新值。

对于 (1),如果不引入某种持久状态,就没有办法做到这一点。没有办法在脚本运行之间存储当前选择,我们只能将两个小部件的值相互比较并与默认值进行比较。一旦widgets被改变,这会导致问题:例如,如果默认值为1,数字输入的值为2,选择框的值为3,我们无法分辨是数字输入还是选择框最近更改的(因此将哪个更新为最新值)。

对于 (2),只要选择更改,就可以使用占位符并刷新小部件。重要的是,如果选择没有改变,小部件应该刷新,否则我们会得到 DuplicateWidgetID 错误(因为小部件的内容也不会改变,它们将具有生成的密钥相同)。

以下是一些代码,展示了处理这两个问题并在最后捕获用户选择的一种方法。请注意,以这种方式使用 @st.cache 将在多个浏览器会话中保留一个全局选择,并允许任何人通过 Streamlit 菜单 -> 'Clear cache' 清除选择,如果有多个用户,这可能是一个问题同时访问脚本。

import re

import streamlit as st

# Simple persistent state: The dictionary returned by `get_state()` will be
# persistent across browser sessions.
@st.cache(allow_output_mutation=True)
def get_state():
    return {}


# The actual creation of the widgets is done in this function.
# Whenever the selection changes,this function is also used to refresh the input
# widgets so that they reflect their new state in the browser when the script is re-run
# to get visual updates.
def display_widgets():
    users = [(1,"Jim"),(2,(3,"Jane")]
    users.sort(key=lambda user: user[1])  # sort by name
    options = ["%s (%d)" % (name,id) for id,name in users]
    index = [i for i,user in enumerate(users) if user[0] == state["selection"]][0]

    return (
        number_placeholder.number_input(
            "ID",value=state["selection"],min_value=1,max_value=3,),option_placeholder.selectbox("Name",options,index),)


state = get_state()

# Set to the default selection
if "selection" not in state:
    state["selection"] = 1

# Initial layout
number_placeholder = st.sidebar.empty()
option_placeholder = st.sidebar.empty()

# Grab input and detect changes
selected_number,selected_option = display_widgets()

input_changed = False

if selected_number != state["selection"] and not input_changed:
    # Number changed
    state["selection"] = selected_number
    input_changed = True
    display_widgets()

selected_option_id = int(re.match(r"\w+ \((\d+)\)",selected_option).group(1))
if selected_option_id != state["selection"] and not input_changed:
    # Selectbox changed
    state["selection"] = selected_option_id
    input_changed = True
    display_widgets()

st.write(f"The selected ID was: {state['selection']}")

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...