I have a small Streamlit app for creating materials.
It just has a dropdown menu and stores created materials until you click "Download".
It was working fine til I added a "Delete" button.
Now I have to click on "Edit" twice for it to populate the dropdown menus. And when I click "Delete" it says the material has been deleted, but it's still showing on the screen until I select something from the dropdown menus.
Any help much appreciated.
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 = {}
# -------------------------------------------------
# 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)
# -------------------------------------------------
# Layout
# -------------------------------------------------
form_col, preview_col = st.columns([1.2, 1])
# =================================================
# FORM
# =================================================
with form_col:
# Get existing row if editing
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() # Series
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}
# -------------------------------------------------
# Buttons
# -------------------------------------------------
col1, col2 = st.columns(2)
with col1:
if st.button("Create Material"):
if not st.session_state.edit_id:
# Add new material
record["ID"] = str(uuid.uuid4())
st.session_state.materials_df = pd.concat(
[st.session_state.materials_df, pd.DataFrame([record])],
ignore_index=True
)
else:
# Update existing material by ID
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:
delete_uid = None
edit_uid = None
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}")
# Buttons
btn_col1, btn_col2 = st.columns([1, 1])
with btn_col1:
if st.button("Edit", key=f"edit-{uid}"):
edit_uid = uid
with btn_col2:
if st.button("Delete", key=f"delete-{uid}"):
delete_uid = uid
# Apply delete/edit after loop
if delete_uid:
st.session_state.materials_df = st.session_state.materials_df[
st.session_state.materials_df["ID"] != delete_uid
]
if st.session_state.edit_id == delete_uid:
st.session_state.edit_id = None
st.success("Material deleted")
st.stop()
if edit_uid:
st.session_state.edit_id = edit_uid
st.stop()