pythonpy-shiny

Is there a table widget for Shiny for python that calls back edited cells?


Similar question to Is there an interactive table widget for Shiny for python that calls back selected rows?, but slightly different: Instead of just selecting rows (and getting the information about which row is selected), I want to edit cells in the displayed table and get back the edited table in a callback.

On https://shiny.posit.co/py/docs/ipywidgets.html, there are three ipywidgets libraries mentioned. I tried for example with ipyregulartable, this example should be runnable:

from shiny import ui, render, App, reactive
import ipyregulartable as rt
import pandas as pd
from shinywidgets import output_widget, reactive_read, register_widget

app_ui = ui.page_fluid(
    ui.layout_sidebar(
        ui.panel_sidebar(),
        ui.panel_main(
            output_widget("table"),
            ui.output_table("changed_table"),
        ),
    )
)


def server(input, output, session):
    dummy = pd.DataFrame(
        {
            "title": ["A", "B", "C"],
            "img_name": ["food.png", "food2.jpg", "food3.jpg"],
        }
    )
    tbl = rt.RegularTableWidget(dummy)
    tbl.editable(0, 100) # don't know if necessary
    register_widget("table", tbl)

    @output
    @render.table
    def changed_table():
        changed = reactive_read(tbl, "datamodel")
        return pd.DataFrame(changed._data)


app = App(app_ui, server)

The dummy data is both displayed as a widget and the data received by the reactive_read() is displayed as well. However, when I edit the table, the changes are not reflected in the attribute "datamodel" which is caught by reactive_read.

Does anybody know how to catch the changes with ipyregulartable? Or any other possibility to have an interactive table in shiny/python with a callback for cell edits? Similar question was also posted here.


Solution

  • Found a solution how to catch table changes, by using the package ipydatagrid. The following was helpful: https://github.com/bloomberg/ipydatagrid/blob/main/examples/CellEditing.ipynb

    from shiny import ui, render, App, reactive
    from ipydatagrid import DataGrid
    import pandas as pd
    from shinywidgets import output_widget, register_widget
    
    app_ui = ui.page_fluid(
        ui.layout_sidebar(
            ui.panel_sidebar(),
            ui.panel_main(
                output_widget("table"),
            ),
        )
    )
    
    
    def server(input, output, session):
        dummy = pd.DataFrame(
            {
                "title": ["A", "B", "C"],
                "img_name": ["food.png", "food2.jpg", "food3.jpg"],
            }
        )
        tbl = DataGrid(dummy, editable=True)
        register_widget("table", tbl)
    
        # Create a reactive value for tracking cell changes
        cell_changes = reactive.Value()
    
        def on_cell_changed(cell):
            cell_changes.set(str(cell))
    
        # register callback
        tbl.on_cell_change(on_cell_changed)
    
        # Print change, just to demonstrate
        @reactive.Effect
        def print_change():
            print(eval(cell_changes()))
    
    
    app = App(app_ui, server)
    

    The reactive value cell_changes() could be used to change the table dummy and to redisplay it by means of an @output.