okld / streamlit-elements

Create a draggable and resizable dashboard in Streamlit, featuring Material UI widgets, Monaco editor (Visual Studio Code), Nivo charts, and more!
MIT License
734 stars 82 forks source link

DataGrid shows no table (only the arrows for switching pages) #21

Open yoeldk opened 1 year ago

yoeldk commented 1 year ago

Hey Everyone, I tried running the following code (taken from the streamlit-gallery repo), but I cannot see any table (only the arrows for switching pages are shown). wondering what am I missing here:

import streamlit as st
from streamlit_elements import mui, elements

DEFAULT_COLUMNS = [
    { "field": 'id', "headerName": 'ID', "width": 90 },
    { "field": 'firstName', "headerName": 'First name', "width": 150, "editable": True, },
    { "field": 'lastName', "headerName": 'Last name', "width": 150, "editable": True, },
    { "field": 'age', "headerName": 'Age', "type": 'number', "width": 110, "editable": True, },
]

DEFAULT_ROWS = [
{ "id": 1, "lastName": 'Snow', "firstName": 'Jon', "age": 35 },
{ "id": 2, "lastName": 'Lannister', "firstName": 'Cersei', "age": 42 },
{ "id": 3, "lastName": 'Lannister', "firstName": 'Jaime', "age": 45 },
{ "id": 4, "lastName": 'Stark', "firstName": 'Arya', "age": 16 },
{ "id": 5, "lastName": 'Targaryen', "firstName": 'Daenerys', "age": None },
{ "id": 6, "lastName": 'Melisandre', "firstName": None, "age": 150 },
{ "id": 7, "lastName": 'Clifford', "firstName": 'Ferrara', "age": 44 },
{ "id": 8, "lastName": 'Frances', "firstName": 'Rossini', "age": 36 },
{ "id": 9, "lastName": 'Roxie', "firstName": 'Harvey', "age": 65 },
]

with elements("new_element"):
    mui.DataGrid(
                columns=DEFAULT_COLUMNS,
                rows = DEFAULT_ROWS,
                pageSize=5,
                checkboxSelection=True,
            )
acschofield commented 1 year ago

This code worked for me:

from streamlit_elements_fluence import elements, mui, html, sync, dashboard
import streamlit as st

st.title("Test")
columns = [
    {"field": "id", "headerName": "ID", "width": 70},
    {"field": "firstName", "headerName": "First name", "width": 130},
    {"field": "lastName", "headerName": "Last name", "width": 130},
    {
        "field": "age",
        "headerName": "Age",
        "type": "number",
        " width": 90,
    },
]

rows = [
    {"id": 1, "lastName": "Snow", "firstName": "Jon", "age": 35},
    {"id": 2, "lastName": "Lannister", "firstName": "Cersei", "age": 42},
    {"id": 3, "lastName": "Lannister", "firstName": "Jaime", "age": 45},
]
with elements("playground"):
    with mui.Box(sx={"height": 300}):
        mui.DataGrid(
            columns=columns,
            rows=rows,
            pageSize=2,
            checkboxSelection=True,
        )

Note the "mui.Box" surrounding the DataGrid

2023-03-09 15_37_05-Main · Streamlit - Personal - Microsoft​ Edge

yoeldk commented 1 year ago

Thanks @acschofield that was super helpful! I have another question that is not directly related but wondered if you know the answer - any idea how to add links to that table ? TIA

acschofield commented 1 year ago

Not sure how to do that or even if it's possible with the python interface. In our application, the user selects a row and then presses one of the buttons below the table to take an action on that row. A bit clunky in UI terms but it may be a work around for you.

yoeldk commented 1 year ago

That's also a possibility. Can you please share the code for doing that?

acschofield commented 1 year ago

It's part of a large application so it is difficult to share. But basically, you set a selection callback on the datagrid:

                    mui.DataGridPro(
                        columns=columns,
                        rows=rows,
                        checkboxSelection=False,
                        disableSelectionOnClick=False,
                        onSelectionModelChange=_handle_select, ...

Then in "_handle_select" you can store the selected row in session_state:

    def _handle_select(model) -> None:
        st.session_state.selected_item= model[0] if len(model) else None

selected_item gets the "id" of the selcted row.

Then in your button handling code you can reference st.session_state.selected_item and take some action-

yoeldk commented 1 year ago

Thank you so much @acschofield

neldivad commented 1 year ago
class DataGrid(Dashboard.Item):
    DEFAULT_COLUMNS = [
        { "field": 'id', "headerName": 'ID', "width": 90 },
        { "field": 'firstName', "headerName": 'First name', "width": 150, "editable": True, },
        { "field": 'lastName', "headerName": 'Last name', "width": 150, "editable": True, },
        { "field": 'age', "headerName": 'Age', "type": 'number', "width": 110, "editable": True, },
    ]
    DEFAULT_ROWS = [
        { "id": 1, "lastName": 'Snow', "firstName": 'Jon', "age": 35 },
        { "id": 2, "lastName": 'Lannister', "firstName": 'Cersei', "age": 42 },
        { "id": 3, "lastName": 'Lannister', "firstName": 'Jaime', "age": 45 },
        { "id": 4, "lastName": 'Stark', "firstName": 'Arya', "age": 16 },
        { "id": 5, "lastName": 'Targaryen', "firstName": 'Daenerys', "age": None },
        { "id": 6, "lastName": 'Melisandre', "firstName": None, "age": 150 },
        { "id": 7, "lastName": 'Clifford', "firstName": 'Ferrara', "age": 44 },
        { "id": 8, "lastName": 'Frances', "firstName": 'Rossini', "age": 36 },
        { "id": 9, "lastName": 'Roxie', "firstName": 'Harvey', "age": 65 },
    ]

    def _handle_edit(self, params):
        print(params)

    def __init__(self, board, x, y, w, h, df=None, **item_props):
        super().__init__(board, x, y, w, h, **item_props)
        self.df = df

        if df is not None:
            self.columns = [{"field": col, "headerName": col, "width": 150, "editable": True} for col in df.columns]
        else:
            self.columns = self.DEFAULT_COLUMNS

    def __call__(self):
        if self.df is not None:
            self.df['id'] = range(1, len(self.df) + 1) # Create a unique id col to workaround ElementFrontendError
            data = self.df.to_dict('records')
        else:
            data = self.DEFAULT_ROWS

        with mui.Paper(key=self._key, sx={"display": "flex", "flexDirection": "column", "borderRadius": 3, "overflow": "hidden"}, elevation=1):
            with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
                mui.icon.ViewCompact()
                mui.Typography("Data grid")

            with mui.Box(sx={"flex": 1, "minHeight": 0}):
                mui.DataGrid(
                    columns=self.columns,
                    rows=data,
                    pageSize=5,
                    rowsPerPageOptions=[5],
                    checkboxSelection=True,
                    disableSelectionOnClick=True,
                    onCellEditCommit=self._handle_edit,
                )

Usage:

  from mui_elements.data_grid import DataGrid

  if 'w' not in state:
    board = Dashboard()

    w = SimpleNamespace(
      dashboard= board,
      table = DataGrid(board, 1,2,3,4, df=df), # your custom pd dataframe
    )
    state.w = w
  else:
    w = state.w

  with elements('ABC'):
    event.Hotkey("ctrl+s", sync(), bindInputs=True, overrideDefault=True)

    with w.dashboard(rowHeight=57):
      w.table()