【问题标题】:streamlit - Sync input fieldsstreamlit - 同步输入字段
【发布时间】: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)

【问题讨论】:

    标签: python streamlit


    【解决方案1】:

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

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

    对于 (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']}")
    

    【讨论】:

      猜你喜欢
      • 2021-11-02
      • 1970-01-01
      • 2021-12-13
      • 1970-01-01
      • 2020-11-13
      • 1970-01-01
      • 2023-01-16
      • 2018-11-18
      • 1970-01-01
      相关资源
      最近更新 更多