Table (or grid) with multiple inputs

Hi,

I’m new to Streamlit and it’s awesome so far. However, I feel I’ve reached the limit of what I am capable of without asking for help. My requirement is a simple one, but it seems it falls outside of the capabilities of the components and how they work currenlty. I feel I’m asking that bit too much…

Basically, I have some contacts in a database, and I want to record whether they have either paid and/or attended an event by adding two columns into a table (or grid) as shown in the example below. I already have the data in a table, no issues, it’s just adding the controls in to two columns at the end of the table that I need. And possibly how to update records in the database, although I’m confident that once I get over this initial hurdle I’ll be able to figure that bit out.

I’ve looked at columns, tables and aggrid so far, but it’s getting over my head. Am I missing something simple here, or should I move on to something that’s not Streamlit that would better suit my needs?

Thanks for reading!

1 Like

There is a sample code to add checkboxes in the column.

I tried experimenting it and it worked.

streamlit==1.17.0
streamlit-aggrid==0.3.3
import streamlit as st
import pandas as pd
from st_aggrid import AgGrid, GridOptionsBuilder, JsCode


data = {
    'Name': ['a', 'b', 'c'],
    'Paid': [True, False, True],
    'Attended': [False, True, True]
}

checkbox_renderer = JsCode("""
class CheckboxRenderer{

    init(params) {
        this.params = params;

        this.eGui = document.createElement('input');
        this.eGui.type = 'checkbox';
        this.eGui.checked = params.value;

        this.checkedHandler = this.checkedHandler.bind(this);
        this.eGui.addEventListener('click', this.checkedHandler);
    }

    checkedHandler(e) {
        let checked = e.target.checked;
        let colId = this.params.column.colId;
        this.params.node.setDataValue(colId, checked);
    }

    getGui(params) {
        return this.eGui;
    }

    destroy(params) {
    this.eGui.removeEventListener('click', this.checkedHandler);
    }
}//end class
""")

df = pd.DataFrame(data)

st.write('#### init data')
st.dataframe(df)

gb = GridOptionsBuilder.from_dataframe(df)
gb.configure_column('Paid', editable=True, cellRenderer=checkbox_renderer)
gb.configure_column('Attended', editable=True, cellRenderer=checkbox_renderer)

st.write('#### interface')
ag = AgGrid(
    df, 
    gridOptions=gb.build(),
    allow_unsafe_jscode=True,
    enable_enterprise_modules=False
)

new_data = ag['data']

st.write('#### updated data')
st.dataframe(new_data)

You can do whatever you want with the new_data.

Output

3 Likes

This is brilliant! Exactly what I needed, thank you very much. :smiling_face_with_three_hearts:

For saving the resulting changes to the database, would I iterate throguh the data in new_data and write each change to the database, or is there a more efficient way? One thing I’m noticing is that for every 10 lines of code in .Net (I’m used to .Net development), there is a single line in Python!! Old habits die hard.

Thanks for your help thus far!

We have init and updated dataframe, we can get the difference and that would reduce your iteration.

Add the following code below the existing code.

st.write('#### Changed')
diff = new_data[~new_data.apply(tuple,1).isin(df.apply(tuple,1))]
st.dataframe(diff)

You may iterate on the diff.

Output

Hey, thanks again. That works, but it’s adding lots of duplicate entires into the database. I think I’m missing a fundamental understanding of how Streamlit works at this point. I just want to commit the entries in the upadated data (diff) dataframe and reload the page, yet the dataframe keeps getting bigger the more changes I add.

What database do you use?

Hi! I’m using Microsoft SQL Server. I have made great progress on this over the weekend, and I just wanted to say thank you again for your help.

1 Like

That’s working swimmingly, nice.