streamlit - 同步输入字段

Posted

技术标签:

【中文标题】streamlit - 同步输入字段【英文标题】:streamlit - Sync input fields 【发布时间】:2020-12-02 03:33:18 【问题描述】:

我有两个允许用户选择的输入字段

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

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

您如何使用 streamlit 实现该行为?

到目前为止我尝试了什么

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

users = [(1, 'Jim'), (2, 'Jim'), (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, 'Jim'), (2, 'Jim'), (3, 'Jane')]
users.sort(key=lambda user: user[1])  # sort by name

id_input = st.sidebar.empty()

options = ['%s (%d)' % (name, id) for id, 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】:

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

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

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

对于 (2),使用占位符并在选择发生更改时刷新小部件是一件简单的事情。重要的是,如果选择没有改变,小部件应该刷新,否则我们会得到DuplicateWidgetID 错误(因为小部件的内容也不会改变,它们会生成相同的键)。

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

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, "Jim"), (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']")

【讨论】:

以上是关于streamlit - 同步输入字段的主要内容,如果未能解决你的问题,请参考以下文章

streamlit 笔记:

多选/选择框在第一次选择后不等待 - Streamlit

如何在虚拟 conda 环境中设置 streamlit 的路径

Streamlit 缓存 Keras 训练模型

kettle从文本文件输入时怎么拆分字段

“ __conform__() 不是有效的 Streamlit 命令。”