Hi! Everyone, Is there a way to insert a button (not checkbox) inside a dataframe. So that for each row we have a separate button and when clicked we can perform some action.
Hello Vedant ,
As far as I know, you canāt directly add a button inside a (native) dataframe ā There may be a hack but Iām not aware of it!
Some questions:
- Why wouldnāt the checkbox be a viable option?
- Have you also tried other options such as streamlit-aggrid?
Thanks,
Charly
Iām facing the same issue, I have a table where one column needs to redirect to a ādetailsā page with that rowās info. I want to use st.page_link
to go to the details page since it preserves st.session_state
and is generally easy to use, or add a st.button
with a st.switch_page
callback. A link column doesnāt solve my problem because I would need to point to āhttp://localhost:port/ā during development and change the link for a deploy, and to my knowledge there is no way to get the current url in streamlit (that doesnāt use JS or an equally hacky solution). Honestly, a generic callback option when clicking a table cell would be the best and easiest (I think) solution. Iāll try to use streamlit-aggrid but it would be better to have a native option for streamlit.
You can use a checkbox instead of a button. With 1.35, it has a feature to select a single row, column, etc.
Here is a sample demo.
After pressing the page link.
streamlit_app.py
import streamlit as st
import pandas as pd
data = {
'Country': ['United States', 'Canada', 'Germany', 'France', 'Japan'],
'Capital': ['Washington, D.C.', 'Ottawa', 'Berlin', 'Paris', 'Tokyo']
}
df = pd.DataFrame(data)
st.title('Countries and Their Capitals')
event = st.dataframe(
df,
on_select='rerun',
selection_mode='single-row'
)
if len(event.selection['rows']):
selected_row = event.selection['rows'][0]
country = df.iloc[selected_row]['Country']
capital = df.iloc[selected_row]['Capital']
st.session_state['country_data'] = {'country': country, 'capital': capital}
st.page_link('pages/detail.py', label=f'Goto {country} Page', icon='šŗļø')
pages/detail.py
import streamlit as st
st.set_page_config(layout='centered')
st.subheader('Detail Page')
st.write(st.session_state['country_data'])
Thanks for the code snippet, @ferdy!
I actually used checkboxes (as in a boolean column in an editable table) to solve this issue before the selection feature was available, but I donāt think this is the best solution yet.
My main issue with checkboxes is that from a design pov they imply that more than one option can be selected, which is not the desired behavior. A radio button would be better, but then I would need another button to confirm the userās selection, which is an unneeded button press. The best solution, in my opinion, still is a button row.
Iāll look into the select row and column feature as soon as I have some time, maybe thereās a way to achieve this behavior without using checkboxes.
That belief is incorrect. Have a look on this discussion at ux stack.
The link you presented reinforces what I said. Checkboxes imply zero or more selections, but in my use case only one option should be selected, which triggers a redirect to another page. Radio buttons would be more appropriate since options are mutually exclusive, but it still feels strange clicking a radio button to go to another page.
You are incorrect again. You said
Would like to see a solution as wellā¦ Aggrid is cool, but I want to use actual streamlit buttons, not HTML rendered ones
Same here - Iām building an overview table, and each row should redirect to the (dynamically generated) details page of this rowās entry.
My work-around is:
- Configure the streamlit dataframe with
selection_mode="single-row"
- Define a callback that is executed every time the row selection changes
def single_row_selection_callback():
# note: it is not possible to have st.switch_page in the callback
st.session_state["mp_single_row_selection_callback"] = True
event = st.dataframe(
data,
on_select=single_row_selection_callback,
selection_mode="single-row",
)
modules = event.selection.rows
if st.session_state.get("mp_single_row_selection_callback", False) is True:
st.session_state["mp_single_row_selection_callback"] = False
st.session_state["mp_selected_uid"] = int(data.loc[modules[0]]["uid"]) if len(modules) > 0 else None
if st.session_state["mp_selected_uid"] is not None:
st.cache_data.clear() # clear the cache and force the module details page to refresh
st.switch_page("pages/5_module_details.py")
The very first column can be used to select an entry (a āmoduleā in my case):
selecting one of the rows via the first column will cause the script to switch to a new page with details about the selected entry.
For now this is good enough, but it would be much nicer to be able to click on a cell, e.g. UID
or Name
and have the script switch to the corresponding details page.
Currently, thereās an open issue requesting this feature. It is useful for when you need to execute functions relevant to a row.
Under the hood Streamlit uses the Glide data grid for the dataframes, it seems it supports buttons as well, so integration to Streamlit should be possible.
Iād be willing to contribute, but adding this functionality seems quite involved as, looking at the other column types, many files need to be modified/added.
Iāve been looking at NiceGUI, which seems to have more features and freedom compared to Streamlit, to transfer my app so I could get buttons-in-rows feature. However, with NiceGUI itās literally hacking a custom language blob into Python script to make it happenā¦ it looks the part, but unmaintainable and code is ugly.
For now the best solution for me, using Streamlit, remains using columns with st.button()
.