Selecting a single row while using st.data_editor with column config st.column_config.CheckboxColumn

Summary

Would love to enable checkbox for single selection while using st.data_editor() with column_config={"Select": st.column_config.CheckboxColumn()}

Code snippet: (from documentation )

                def dataframe_with_selections(df):
                    df_with_selections = df.copy()
                    df_with_selections.insert(0, "Select", False)
                    # Get dataframe row-selections from user with st.data_editor
                    edited_df = st.data_editor(
                        df_with_selections,
                        hide_index=True,
                        column_config={"Select": st.column_config.CheckboxColumn(required=True)},
                        disabled=df.columns,
                        num_rows="dynamic",
                    )

                    # Filter the dataframe using the temporary column, then drop the column
                    selected_rows = edited_df[edited_df.Select]
                    return selected_rows.drop('Select', axis=1)
                  
                selection = dataframe_with_selections(df)
                st.write("Your selection:")
                st.write(selection)

What I’m aiming for :

Single checkbox selection is enabled only.

How the above code is working:

With the above code, end-user can opt for multiple selections.


Probably I’m missing something very obvious. Any help would be appreciated! :balloon:

Cheers
Avra

Hi @AvratanuBiswas

Thanks for the question, could you elaborate a bit more on “single checkbox selection is enabled only”, not quite sure what the intended checkbox should be like.

In addition to default, st.column_config.CheckboxColumn() also has disabled and default parameters that may be explored upon.

Best regards,
Chanin

Hi,
I think I have the same issue. Is there a possibility to implement the option for single selection next to multiple selection in st.column_config.CheckboxColumn() or data_editor? Or maybe it was done and I don’t see the corresponding parameter. As far as I know, *it is only possible to check multiple check boxes. However, I want the user to only check one box. It is not possible via session_state to figure out which was the last selection due to the row index sorting. Otherwise, I could force to put all to false except the last selection. My workaround at the moment: If the user checks a second box and the index of the selection therefore exceeds 1, i force a page reload to uncheck (put false) all the boxes. This is rather ugly solution but the only one I have found.

Thanks a lot for your help!
Best regards,
Liv

def dataframe_with_selections(df_tools):
df_with_selections = df_tools.copy()
df_with_selections.insert(0, “Select”, False)

 # Get dataframe row-selections from user with st.data_editor
 edited_df = st.data_editor(
    df_with_selections,
    key="data_editor",
    hide_index=True,
    column_config={"Select": st.column_config.CheckboxColumn(required=True)},
    disabled=df_tools.columns,
 )

 # Filter the dataframe using the temporary column, then drop the column
 selected_rows = edited_df[edited_df.Select]
 return selected_rows.drop('Select', axis=1)
 if len(selected_rows.index) > 1:
  streamlit_js_eval(js_expressions="parent.window.location.reload()")

 return selected_rows

selection = dataframe_with_selections(df_tools)

2 Likes

Hey Avra, sorry totally forgot to answer on this. We already chatted that there’s probably no good way to do this today. I thought about it a bit today but I also couldn’t find any workaround (other than manually checking how many rows were selected and showing an error). We’ll definitely support this though once we implement row selections as a built-in feature!

3 Likes

This would be so useful!

Hi there
I believe that the implementation of a st.column_config.radiobutton or something similar might solve the issue

+1 for this feature (something equivalent to CheckboxColumn but only one cell can be selected at any given time. Many useful use cases for this , and the current workaround is very very messy (I’m using st.data_editor’s onchange, having to reset all the oother checked columns, and then rebuilding the dataframe. Which has a side effect that it loses any columnst he user may have sorted on).

1 Like

Essential for me – multiselect is a very different problem being solved, single select is essential for many (or at least my) applications. Stuck using a dodgey aggrid workaround until this is solved.
Radio, checkbox, or double-click row select (if highlighted) would be fine as long as it’s single-select.
Vote +1!

1 Like

Any update on that?

Any updates here ?

+1 This feature would be quite useful.

1 Like

Any workarounds while we await the update?

Utilize the on_change parameter. Record what has changed, set the column value to false, and update the dataframe on the change.

Complete sample code

import streamlit as st
from streamlit import session_state as ss
import pandas as pd


if 'selected_row_index' not in ss:
    ss.selected_row_index = None


@st.cache_data
def get_data(nrows):
    url = 'https://raw.githubusercontent.com/Munees11/Auto-MPG-prediction/master/Scripts/auto_mpg_dataset.csv'
    return pd.read_csv(url, nrows=nrows)


if 'df' not in ss:
    ss.df = get_data(10)
    ss.df['selected'] = [False] * len(ss.df)
    ss.df = ss.df[['cylinders', 'car_name', 'mpg', 'selected']]


def mpg_change():
    edited_rows: dict = ss.mpg['edited_rows']
    ss.selected_row_index = next(iter(edited_rows))
    ss.df = ss.df.assign(selected=False)
    update_dict = {idx: values for idx, values in edited_rows.items()}
    ss.df.update(pd.DataFrame.from_dict(update_dict, orient='index'))


def main():
    st.markdown('### Data Editor')
    with st.container(border=True):
        edf = st.data_editor(
            ss.df,
            hide_index=True,
            on_change=mpg_change,
            key='mpg',
            use_container_width=True
        )
    st.write(f'selected row index: {ss.selected_row_index}')
    if ss.selected_row_index is not None:
        st.write(f'car name: {ss.df.at[ss.selected_row_index, "car_name"]}')


if __name__ == '__main__':
    main()

1 Like

Thank you @ferdy , not sure I would have figured that one out. Still a bit more blinking than I would like but it does work!

Use this around 2x faster:

ss.df.loc[ss.df['selected'], 'selected'] = False

instead of:

ss.df = ss.df.assign(selected=False)
1 Like

This will be great feature to have. Any updates?

Some examples are here.