You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

解决Streamlit中点击编辑/删除按钮后界面延迟更新问题

问题修复方案

问题根源

  1. Edit按钮需两次点击:原代码中st.stop()的使用时机导致页面提前终止渲染,edit_id的更新无法即时触发表单区域重新加载
  2. Delete后物料不即时消失:同样因st.stop()中断脚本执行,删除操作后的物料列表无法即时刷新

修复后的完整代码

from datetime import datetime as dt
import streamlit as st
import pandas as pd
from io import BytesIO
import uuid

st.set_page_config(layout="wide")
st.title("Material Creation")

# -------------------------------------------------
# Session state initialization
# -------------------------------------------------
if "materials_df" not in st.session_state:
    st.session_state.materials_df = pd.DataFrame()

if "edit_id" not in st.session_state:
    st.session_state.edit_id = None

if "custom_inputs" not in st.session_state:
    st.session_state.custom_inputs = {}

if "delete_uid" not in st.session_state:
    st.session_state.delete_uid = None

if "edit_uid" not in st.session_state:
    st.session_state.edit_uid = None

# -------------------------------------------------
# Load source Excel
# -------------------------------------------------
@st.cache_data
def load_data(path="Sample.xlsx"):
    return pd.read_excel(path)

df = load_data("Sample.xlsx")

# -------------------------------------------------
# Menu headers
# -------------------------------------------------
menu_config_headers = {
    'Polymer': ['Manufacturer', 'Subtype', 'Product Name', 'Grade', 'Color'],
    'Coating': ['Manufacturer', 'Subtype', 'Product Name', 'Grade', 'Color'],
    'Misc': ['Manufacturer', 'Subtype', 'Product Name', 'Grade', 'Color'],
    'Solder Paste': ['Manufacturer', 'Grade', 'Flux', 'Metal Load', 'Powder Size', 'Recycled Content', 'Solder Alloy'],
    'Solder Alloy': ['Manufacturer', 'Grade', 'Form', 'Recycled Content', 'Perform Size', 'Solder Alloy'],
    'Shaped Metal': ['Manufacturer', 'Subtype', 'Product Name', 'Grade', 'Form', 'Metal/Alloy', 'Recycled Content', 'Standard and Standard Designating Body', 'Thickness'],
    'Bulk Metal': ['Manufacturer', 'Grade', 'Form', 'Metal/Alloy', 'Purity/Concentration', 'Recycled Content', 'Standard and Standard Designating Body'],
    'Plating': ['Manufacturer', 'Form', 'Manufacturer Performing Plating', 'Metal/Alloy', 'Purity/Concentration', 'Recycled Content'],
    'Chemical': ['Manufacturer', 'Chemical Name / Description', 'Purity/Concentration'],
    'Magnet': ['Manufacturer', 'Subtype', 'Grade', 'Recycled Content'],
    'Process': ['Manufacturer', 'Subtype', 'Product Name', 'Grade'],
    'Semiconductor': ['Manufacturer', 'Grade', 'Semiconductor Description', 'Semiconductor Type'],
    'Surface Treatment Process': ['Manufacturer', 'Subtype', 'Color', 'Composition of Coating / "Target" Material', 'Purity/Concentration', 'Substrate: Base Metal', 'Surface Treatment Type / Specification'],
    'Anodization': ['Manufacturer', 'Color', 'Anodization Details', 'Metal + "Anodizing"', 'Substrate: Base Metal']
}

@st.cache_data
def build_menu_config(df, headers):
    menu = {}
    for mat, cols in headers.items():
        sub = df[df["Material"] == mat]
        menu[mat] = {
            c: sub[c].dropna().unique().tolist() if c in sub.columns else []
            for c in cols
        }
    return menu

menu_config = build_menu_config(df, menu_config_headers)

# -------------------------------------------------
# 优先处理删除/编辑状态,确保渲染前完成状态更新
# -------------------------------------------------
if st.session_state.delete_uid:
    st.session_state.materials_df = st.session_state.materials_df[
        st.session_state.materials_df["ID"] != st.session_state.delete_uid
    ]
    if st.session_state.edit_id == st.session_state.delete_uid:
        st.session_state.edit_id = None
    st.success("Material deleted")
    st.session_state.delete_uid = None

if st.session_state.edit_uid:
    st.session_state.edit_id = st.session_state.edit_uid
    st.session_state.edit_uid = None

# -------------------------------------------------
# Layout
# -------------------------------------------------
form_col, preview_col = st.columns([1.2, 1])

# =================================================
# FORM
# =================================================
with form_col:
    existing_row = None
    if st.session_state.edit_id:
        existing_row = st.session_state.materials_df[
            st.session_state.materials_df["ID"] == st.session_state.edit_id
        ].squeeze()

    category = st.selectbox(
        "Type",
        [""] + list(menu_config.keys()),
        index=0 if existing_row is None else list(menu_config.keys()).index(existing_row["Type"]) + 1
    )

    selected_values = {}

    if category:
        for label, options in menu_config[category].items():
            options_with_add = ["", "➕ Add new"] + options

            existing_value = existing_row[label] if existing_row is not None and label in existing_row else None

            choice = st.selectbox(
                label,
                options_with_add,
                index=options_with_add.index(existing_value) if existing_value in options_with_add else 0,
                key=f"select-{label}"
            )

            if choice == "➕ Add new":
                custom_value = st.text_input(
                    f"Enter new {label}",
                    value=existing_value or "",
                    key=f"input-{label}"
                )
                selected_values[label] = custom_value
            else:
                selected_values[label] = choice or None

        record = {"Type": category, **selected_values}

        col1, col2 = st.columns(2)

        with col1:
            if st.button("Create Material"):
                if not st.session_state.edit_id:
                    record["ID"] = str(uuid.uuid4())
                    st.session_state.materials_df = pd.concat(
                        [st.session_state.materials_df, pd.DataFrame([record])],
                        ignore_index=True
                    )
                else:
                    row_idx = st.session_state.materials_df.index[
                        st.session_state.materials_df["ID"] == st.session_state.edit_id
                    ][0]
                    for k, v in record.items():
                        st.session_state.materials_df.loc[row_idx, k] = v
                    st.session_state.edit_id = None

                st.success("Material saved")

        with col2:
            if not st.session_state.materials_df.empty:
                output = BytesIO()
                with pd.ExcelWriter(output, engine="xlsxwriter") as writer:
                    st.session_state.materials_df.drop(columns=["ID"], errors="ignore").to_excel(
                        writer, index=False, sheet_name="Materials"
                    )

                timestamp = dt.now().strftime("%Y%m%d")
                st.download_button(
                    "Download materials",
                    data=output.getvalue(),
                    file_name=f"Materials_{timestamp}.xlsx",
                    mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
                )

# =================================================
# PREVIEW / EDIT / DELETE
# =================================================
with preview_col:
    st.subheader("Created Materials")

    if st.session_state.materials_df.empty:
        st.info("No materials created yet.")
    else:
        for _, row in st.session_state.materials_df.iterrows():
            uid = row["ID"]

            with st.container():
                for field, value in row.dropna().items():
                    if field != "ID":
                        st.markdown(f"**{field}:** {value}")

                btn_col1, btn_col2 = st.columns([1, 1])
                with btn_col1:
                    if st.button("Edit", key=f"edit-{uid}"):
                        st.session_state.edit_uid = uid
                        st.rerun()
                with btn_col2:
                    if st.button("Delete", key=f"delete-{uid}"):
                        st.session_state.delete_uid = uid
                        st.rerun()

关键修改说明

  1. 调整状态处理顺序:将删除/编辑的状态更新逻辑移到页面渲染前,确保状态变更后再加载界面
  2. 替换st.stop()为st.rerun():点击按钮后立即触发页面重新渲染,让状态变化即时显示
  3. 临时状态存储:用独立的delete_uid/edit_uid临时存储操作目标ID,避免循环内直接修改状态导致的渲染异常

内容的提问来源于stack exchange,提问作者Maryna Longnickel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 18:07:02