Ah, yes. You’ll have to collect edits then only commit them upon changing the filter instead, I think. I’m on mobile now, but I’ll try to write something up in a little bit.
Hi Debbie,
for a better overview I sent you the full code structure.
If the solution has anything to do with containers, nested functions, st.stop or similar, this has perhaps influence on the full code.
You can probably ignore the details of the widgets - only that you know the whole structure (with your filter solution inside )
import pandas as pd
import streamlit as st
import plotly.express as px
from streamlit import session_state as ss
import time
st.set_page_config(layout='wide', page_title='Test')
# Set title and design elements according to RB brand guide
st.title('Test')
@st.cache_data
def run_query():
# Here is normally my SQL query
df_sql=pd.DataFrame({"A":['1','2','3'],"B":['2','3','4'],"C":['3','4','5']})
return df_sql
if 'df' not in ss:
ss.df = run_query()
ss.df['Active'] = True
if 'setvis' not in ss:
ss.setvis = 'B'
# Upload new file and add to data_editor (here - double existing df)
if 'file_processed' not in ss:
ss['file_processed'] = False
uploaded_file = st.file_uploader('**Upload file**', type= ['csv'] )
if uploaded_file is None:
ss['file_processed'] = False
if uploaded_file is not None and not ss['file_processed']:
ss['filter']= ''
ss.df=pd.concat([ss.df,ss.df],ignore_index=True)
ss['file_processed'] = True
# Create columns
col1, col2 = st.columns([8,2],gap="large")
# Complex filter mechanism in combination with st.data_editor by mathcatsand 🧡
def active_dfa():
return ss.df[ss.df['Active'] == True].copy()
def get_index(row):
return active_dfa().iloc[row].name
def commit():
for row in ss.editor['edited_rows']:
row_index = get_index(row)
for key, value in ss.editor['edited_rows'][row].items():
ss.df.at[row_index, key] = value
# Create filter
name = col1.text_input('Search for ....',key='filter')
if name == '':
ss.df.Active = True
else:
ss.df.Active = False
ss.df.loc[ss.df['A'].str.contains(name, case=False), 'Active'] = True
# Dataeditor
df_ed=col1.data_editor(active_dfa(),hide_index=True, use_container_width=True,column_order=['A','B','C'],key='editor',on_change=commit)
# Plot precreation depending radiobutton
ss.setvis=col2.radio('Set visualization category',['B','C'], horizontal=True)
df_plot=ss.df.groupby(ss.setvis,dropna=False).agg('count')['A']
fig1= px.pie(names=df_plot.index,values=df_plot).update_traces(sort=False)
fig2= px.bar(x=df_plot.index, y=df_plot, log_y=True).update_layout(showlegend=False)
# Establish vizualisation in different tabs
tab1, tab2 = col2.tabs(['distribution pie', 'distribution bar'])
with tab1:
st.plotly_chart(fig1, theme='streamlit', use_container_width=True)
with tab2:
st.plotly_chart(fig2, theme='streamlit', use_container_width=True)
# Save dataframe
if col1.button('Save dataframe'):
time(5) # Save it anywhere
st.cache_data.clear()
del ss.df
It’s not particularly dependent on other stuff. Basically move the commit
callback function to the filter input and then ensure the commit action is at the top of every other page to make sure edits are not lost if someone filters+edits+changes pages (without unfiltering) or makes an edit while not filtered (and doesn’t immediately change the filter). You could also add a confirm button or something to make sure any final edits are committed; it just depends on what kind of a workflow you want the users to have.
import streamlit as st
import pandas as pd
import time
if "dfa" not in st.session_state:
st.session_state["dfa"] = pd.DataFrame(
{
"Par": ["Apple", "Strawberry", "Banana"],
"Cat1": ["good", "good", "bad"],
"Cat2": ["healthy", "healthy", "unhealthy"],
"Active": [False, False, False],
}
)
def active_dfa():
return st.session_state["dfa"][st.session_state["dfa"]["Active"] == True].copy()
def get_index(row):
return active_dfa().iloc[row].name
def commit():
for row in st.session_state.editor["edited_rows"]:
row_index = get_index(row)
for key, value in st.session_state.editor["edited_rows"][row].items():
st.session_state["dfa"].at[row_index, key] = value
st.header("Filter and edit data")
name = st.text_input("Search for ...", on_change=commit)
if name == "":
st.session_state["dfa"].Active = True
else:
st.session_state["dfa"].Active = False
st.session_state["dfa"].loc[
st.session_state["dfa"]["Par"].str.contains(name, case=False), "Active"
] = True
edited_dfa = st.data_editor(
active_dfa(), column_order=["Par", "Cat1", "Cat2"], key="editor"
)
st.button("Save", on_click=commit)
At the beginning of every other page (if you have them), after importing or redefining the necessary functions, add:
if "dfa" in st.session_state:
commit()
Thanks @mathcatsand !!!
Great to have solved this with you.
Think about the different commit possibilities.
Another idea I had is using a form, where the data_editor is nested. With the form_submit_button I raised the commit then.
with st.form('fde') as f:
edited_dfa = st.data_editor(
active_dfa(), column_order=["Par", "Cat1", "Cat2"], key="editor"
)
st.form_submit_button(on_click=commit)
This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.