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.
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
.