pythonplotlyplotly-pythonpy-shiny

User inputs for controlling a plotly plot, using Shiny in Python


I am trying to have user defined input to control sunburst plots. This is all coded in Python, and used both the Shiny and Plotly package. The reason for using these over dash is as I have worked with both of these in R, however this project is required to be in Python.

The idea is that, using numeric inputs, a user can edit the parameters that feed into a sunburst plot. There will be multiple inputs, but the code below applies to just a single value, as I assume that any answer will be scalable.

import plotly.graph_objs as go
import plotly.express as px
from shiny import App, reactive, render, ui,  Inputs, Outputs, Session
from shinywidgets import output_widget, register_widget
import pandas as pd



def panel_box(*args, **kwargs):
    return ui.div(
        ui.div(*args, class_="card-body"),
        **kwargs,
        class_="card mb-3",
    )

app_ui = ui.page_fluid(
    {"class": "p-4"},
    ui.row(
        ui.column(
            4,
            panel_box(
                ui.input_numeric("FirstValue", "FirstValue", min = 0, value=2),

            ),
        ),
        ui.column(
            8,
            output_widget("scatterplot"),
        ),
    ),
)

def server(input: Inputs, output: Outputs, session: Session):
    
    FirstValue = reactive.Value(2)

    @reactive.Effect
    @reactive.event(input.FirstValue)
    def _():
        FirstValue.set(input.FirstValue())

    scatterplot = go.FigureWidget(
        data=[
            go.Sunburst(
                labels = ["Eve", "Cain", "Seth", "Enos", "Noam", "Abel", "Awan", "Enoch", "Azura"],
                parents = ["", "Eve", "Eve", "Seth", "Seth", "Eve", "Eve", "Awan", "Eve" ],
                values = [2, 14, 12, 10, 2, 6, 6, 4, 4],

            ),
        ],
        layout={"showlegend": False},
    )

    @reactive.Effect
    def _():
        scatterplot.data[0].values = [FirstValue, 14, 12, 10, 2, 6, 6, 4, 4]

    register_widget("scatterplot", scatterplot)


app = App(app_ui, server)

This currently comes up with the error Error in Effect: <shiny.reactive._reactives.Value object at 0x000002275FC35540> is not JSON serializable.

I've tried several other approaches, many of which break the reactive property - this is the closest I have gotten.

How can I make the plot linked to the values the user defines?


Solution

  • I think the error is coming from trying to set the scatterplot data using the FirstValue object instead of its value.

    I have very little experience with shiny, but looking through the documentation, you may be able to do away with the reactive event, and directly use @output and @render_widget decorators to create the plotly figure using the specified input.

    import plotly.graph_objs as go
    import plotly.express as px
    from shiny import App, reactive, render, ui,  Inputs, Outputs, Session
    from shinywidgets import output_widget, render_widget, register_widget
    import pandas as pd
    
    
    
    def panel_box(*args, **kwargs):
        return ui.div(
            ui.div(*args, class_="card-body"),
            **kwargs,
            class_="card mb-3",
        )
    
    app_ui = ui.page_fluid(
        {"class": "p-4"},
        ui.row(
            ui.column(
                4,
                panel_box(
                    ui.input_numeric("FirstValue", "FirstValue", min = 0, value=2),
    
                ),
            ),
            ui.column(
                8,
                output_widget("my_widget"),
            ),
        ),
    )
    
    def server(input: Inputs, output: Outputs, session: Session):
        
        @output
        @render_widget
        def my_widget():
            fig = go.FigureWidget(
                data=[
                    go.Sunburst(
                        labels = ["Eve", "Cain", "Seth", "Enos", "Noam", "Abel", "Awan", "Enoch", "Azura"],
                        parents = ["", "Eve", "Eve", "Seth", "Seth", "Eve", "Eve", "Awan", "Eve" ],
                        values = [input.FirstValue(), 14, 12, 10, 2, 6, 6, 4, 4],
    
                    ),
                ],
                layout={"showlegend": False},
            )
            return fig
    
    app = App(app_ui, server)
    app.run()
    

    enter image description here