posit-dev / py-shiny

Shiny for Python
https://shiny.posit.co/py/
MIT License
1.31k stars 79 forks source link

Table output: use selected row as a reaactive input #390

Open corey-dawson opened 1 year ago

corey-dawson commented 1 year ago

In R Shiny, there are built in controls for selecting a row in a table output. The row index (of the row selected) can be used as an input to trigger other events. How can this functionality (user selected rows on a table) be used in Python Shiny version? Any example would be excellent. thanks in advance

Section 2.1.1 of this R DT manual 2.1.1 Row Selection

bartverweire commented 1 year ago

Hi,

you could use qgrid. An example is shown below. Note that you will need ipywidgets < 8.

"""
Requirements:
shiny
shinywidgets
ipywidgets==7.6.5
pandas
qgrid
"""
from shiny import *
from shinywidgets import *

import pandas as pd
import qgrid

app_ui = ui.page_fluid(
    # qgrid output widget
    output_widget("qgrid_output"),

    # text to put the feedback of row selection
    ui.output_text_verbatim("out_feedback")
)

def server(input, output, session):

    selected_rows = reactive.Value()

    @reactive.Calc
    def iris():
        """
        Load the iris data set
        """
        return pd.read_csv('https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv')

    @output
    @render_widget
    def qgrid_output():
        """
        Render the qgrid table
        We also define a handler for the selection_changed event.
        """
        w = qgrid.show_grid(iris(), show_toolbar=False)
        # define the handler for a selection change
        # See https://qgrid.readthedocs.io/en/latest/index.html?highlight=selection_changed for info
        w.on("selection_changed", qgrid_select_handler)

        return w

    def qgrid_select_handler(event, widget):
        """
        The handler is a regular python function, not a reactive function.
        If we want to use the result in shiny, we need to update a reactive.Value
        :param event: event data
        :param widget: the widget on which the event occurred
        :return:
        """
        # Get the data frame that is displayed. This may differ from the original data frame
        # because of filtering, sorting, ...
        df = widget.get_changed_df()
        # Get the selected rows. This is an array of row indexes (after filtering, sorting, ...)
        sel = event["new"]

        # Set the value of the selected_rows reactive.Value to the data frame obtained by applying the index
        selected_rows.set(df.iloc[sel,:])

    @output
    @render.text
    def out_feedback():
        """
        Uses the reactive Value selected_rows, which was updated by the qgrid handler
        :return:
        """
        return selected_rows()

app = App(app_ui, server)

def main():
    run_app(app)

if __name__ == "__main__":
    main()

Kind Regards

Bart

gshotwell commented 9 months ago

You can do the same thing with shiny for python, here are the docs. We currently don't have programmatic selection, but we're starting work on that feature imminently.

https://shiny.posit.co/py/components/outputs/data-grid/#select-rows