STOP/suppress re-run. re-run on command or by button ONLY

Is there a way to suppress a re-run from occurring to keep the session state as is and only re-run by button or command?

for instance, I have multiple select.boxes / AG_Grid dataframes and I want to edit all before re-run occurs

could the following arguments be used on selectbox?
|on_change (callable)|An optional callback invoked when this selectbox’s value changes.|
|args (tuple)|An optional tuple of args to pass to the callback.|
|kwargs (dict)|An optional dict of kwargs to pass to the callback.|

Hey @jquest, did you get a chance to look up st.form? It enables you to batch n widgets together so that the app only refreshes once you submit the whole form (meaning the n widgets)!

Thanks this looks like it should work!

1 Like

hey @arnaud do you know how to work an AGgrid inside a form? I use ADgrid to store vars in sessionstate … so when I click save i get a return of the dataframe where a user make inputs…but couldn’t get it to work in the form.

I’ll keep trying and report back if I can get it

Hey @jquest!

Do you mind sharing a minimal bit of code that reproduces what you’re doing? And where you want to go? :slight_smile:

I figured it out! you can change AGgrid to update mode on “MANUAL” which acts similar to forms. I’m going to use both forms and AGgrid to improve app experience! thanks!

FYI for anyone who wants to see the power of AGgrids run the below : )

from distutils import errors
from distutils.log import error
import streamlit as st
import pandas as pd
import numpy as np
import altair as alt
from itertools import cycle
import os
from PIL import Image

from st_aggrid import GridOptionsBuilder, AgGrid, GridUpdateMode, DataReturnMode, JsCode
main_root = os.getcwd()
db_root = os.path.join(main_root, ‘db’)
db_app_root = os.path.join(db_root, ‘app’)
bee_image = os.path.join(db_root, ‘bee.jpg’)
image = Image.open(bee_image)

st.set_page_config(

page_title=“Ex-stream-ly Cool App”,

page_icon=image,

layout=“wide”,

initial_sidebar_state=“expanded”,

menu_items={

‘Get Help’: ‘extremelycoolapp.com’,

‘Report a bug’: “extremelycoolapp.com”,

‘About’: “# This is a header. This is an extremely cool app!”

}

)

df = pd.DataFrame([[33,]*1000])

st.dataframe(df)

np.random.seed(42)

@st.cache(allow_output_mutation=True)
def fetch_data(samples):
deltas = cycle([
pd.Timedelta(weeks=-2),
pd.Timedelta(days=-1),
pd.Timedelta(hours=-1),
pd.Timedelta(0),
pd.Timedelta(minutes=5),
pd.Timedelta(seconds=10),
pd.Timedelta(microseconds=50),
pd.Timedelta(microseconds=10)
])
dummy_data = {
“date_time_naive”:pd.date_range(‘2021-01-01’, periods=samples),
“apple”:np.random.randint(0,100,samples) / 3.0,
“banana”:np.random.randint(0,100,samples) / 5.0,
“chocolate”:np.random.randint(0,100,samples),
“group”: np.random.choice([‘A’,‘B’], size=samples),
“date_only”:pd.date_range(‘2020-01-01’, periods=samples).date,
“timedelta”:[next(deltas) for i in range(samples)],
“date_tz_aware”:pd.date_range(‘2022-01-01’, periods=samples, tz=“Asia/Katmandu”)
}
return pd.DataFrame(dummy_data)

#Example controlers
st.sidebar.subheader(“St-AgGrid example options”)

sample_size = st.sidebar.number_input(“rows”, min_value=10, value=30)
grid_height = st.sidebar.number_input(“Grid height”, min_value=200, max_value=800, value=300)

return_mode = st.sidebar.selectbox(“Return Mode”, list(DataReturnMode.members), index=1)
return_mode_value = DataReturnMode.members[return_mode]

update_mode = st.sidebar.selectbox(“Update Mode”, list(GridUpdateMode.members), index=6)
update_mode_value = GridUpdateMode.members[update_mode]

#enterprise modules
enable_enterprise_modules = st.sidebar.checkbox(“Enable Enterprise Modules”)
if enable_enterprise_modules:
enable_sidebar =st.sidebar.checkbox(“Enable grid sidebar”, value=False)
else:
enable_sidebar = False

#features
fit_columns_on_grid_load = st.sidebar.checkbox(“Fit Grid Columns on Load”)

enable_selection=st.sidebar.checkbox(“Enable row selection”, value=True)
if enable_selection:
st.sidebar.subheader(“Selection options”)
selection_mode = st.sidebar.radio(“Selection Mode”, [‘single’,‘multiple’], index=1)

use_checkbox = st.sidebar.checkbox("Use check box for selection", value=True)
if use_checkbox:
    groupSelectsChildren = st.sidebar.checkbox("Group checkbox select children", value=True)
    groupSelectsFiltered = st.sidebar.checkbox("Group checkbox includes filtered", value=True)

if ((selection_mode == 'multiple') & (not use_checkbox)):
    rowMultiSelectWithClick = st.sidebar.checkbox("Multiselect with click (instead of holding CTRL)", value=False)
    if not rowMultiSelectWithClick:
        suppressRowDeselection = st.sidebar.checkbox("Suppress deselection (while holding CTRL)", value=False)
    else:
        suppressRowDeselection=False
st.sidebar.text("___")

enable_pagination = st.sidebar.checkbox(“Enable pagination”, value=False)
if enable_pagination:
st.sidebar.subheader(“Pagination options”)
paginationAutoSize = st.sidebar.checkbox(“Auto pagination size”, value=True)
if not paginationAutoSize:
paginationPageSize = st.sidebar.number_input(“Page size”, value=5, min_value=0, max_value=sample_size)
st.sidebar.text(“___”)

df = fetch_data(sample_size)

#Infer basic colDefs from dataframe types
gb = GridOptionsBuilder.from_dataframe(df)

#customize gridOptions
gb.configure_default_column(groupable=True, value=True, enableRowGroup=True, aggFunc=‘sum’, editable=True)
gb.configure_column(“date_only”, type=[“dateColumnFilter”,“customDateTimeFormat”], custom_format_string=‘yyyy-MM-dd’, pivot=True)
gb.configure_column(“date_tz_aware”, type=[“dateColumnFilter”,“customDateTimeFormat”], custom_format_string=‘yyyy-MM-dd HH:mm zzz’, pivot=True)

gb.configure_column(“apple”, type=[“numericColumn”,“numberColumnFilter”,“customNumericFormat”], precision=2, aggFunc=‘sum’)
gb.configure_column(“banana”, type=[“numericColumn”, “numberColumnFilter”, “customNumericFormat”], precision=1, aggFunc=‘avg’)
gb.configure_column(“chocolate”, type=[“numericColumn”, “numberColumnFilter”, “customCurrencyFormat”], custom_currency_symbol=“R$”, aggFunc=‘max’)

#configures last row to use custom styles based on cell’s value, injecting JsCode on components front end
cellsytle_jscode = JsCode(“”"
function(params) {
if (params.value == ‘A’) {
return {
‘color’: ‘white’,
‘backgroundColor’: ‘darkred’
}
} else {
return {
‘color’: ‘black’,
‘backgroundColor’: ‘white’
}
}
};
“”")
gb.configure_column(“group”, cellStyle=cellsytle_jscode)

if enable_sidebar:
gb.configure_side_bar()

if enable_selection:
gb.configure_selection(selection_mode)
if use_checkbox:
gb.configure_selection(selection_mode, use_checkbox=True, groupSelectsChildren=groupSelectsChildren, groupSelectsFiltered=groupSelectsFiltered)
if ((selection_mode == ‘multiple’) & (not use_checkbox)):
gb.configure_selection(selection_mode, use_checkbox=False, rowMultiSelectWithClick=rowMultiSelectWithClick, suppressRowDeselection=suppressRowDeselection)

if enable_pagination:
if paginationAutoSize:
gb.configure_pagination(paginationAutoPageSize=True)
else:
gb.configure_pagination(paginationAutoPageSize=False, paginationPageSize=paginationPageSize)

gb.configure_grid_options(domLayout=‘normal’)
gridOptions = gb.build()

#Display the grid
st.header(“Streamlit Ag-Grid”)
st.markdown(“”"
AgGrid can handle many types of columns and will try to render the most human readable way.
On editions, grid will fallback to string representation of data, DateTime and TimeDeltas are converted to ISO format.
Custom display formating may be applied to numeric fields, but returned data will still be numeric.
“”")

grid_response = AgGrid(
df,
gridOptions=gridOptions,
height=grid_height,
width=‘100%’,
data_return_mode=return_mode_value,
update_mode=update_mode_value,
fit_columns_on_grid_load=fit_columns_on_grid_load,
allow_unsafe_jscode=True, #Set it to True to allow jsfunction to be injected
enable_enterprise_modules=enable_enterprise_modules
)

df = grid_response[‘data’]
selected = grid_response[‘selected_rows’]
selected_df = pd.DataFrame(selected).apply(pd.to_numeric, errors=‘coerce’)

with st.spinner(“Displaying results…”):
#displays the chart
chart_data = df.loc[:,[‘apple’,‘banana’,‘chocolate’]].assign(source=‘total’)

if not selected_df.empty :
    selected_data = selected_df.loc[:,['apple','banana','chocolate']].assign(source='selection')
    chart_data = pd.concat([chart_data, selected_data])

chart_data = pd.melt(chart_data, id_vars=['source'], var_name="item", value_name="quantity")
#st.dataframe(chart_data)
chart = alt.Chart(data=chart_data).mark_bar().encode(
    x=alt.X("item:O"),
    y=alt.Y("sum(quantity):Q", stack=False),
    color=alt.Color('source:N', scale=alt.Scale(domain=['total','selection'])),
)

st.header("Component Outputs - Example chart")
st.markdown("""
This chart is built with data returned from the grid. rows that are selected are also identified.
Experiment selecting rows, group and filtering and check how the chart updates to match.
""")

st.altair_chart(chart, use_container_width=True)

st.subheader("Returned grid data:") 
#returning as HTML table bc streamlit has issues when rendering dataframes with timedeltas:
# https://github.com/streamlit/streamlit/issues/3781
st.markdown(grid_response['data'].to_html(), unsafe_allow_html=True)

st.subheader("grid selection:")
st.write(grid_response['selected_rows'])

st.header("Generated gridOptions")
st.markdown("""
    All grid configuration is done thorugh a dictionary passed as ```gridOptions``` parameter to AgGrid call.
    You can build it yourself, or use ```gridOptionBuilder``` helper class.  
    Ag-Grid documentation can be read [here](https://www.ag-grid.com/documentation)
""")
st.write(gridOptions)