Skip to content

Shiny busy indicator does not appear on widgets due to shiny-ipywidget-output visibility #213

@hypebright

Description

@hypebright

When using shinywidgets.render_plotly in Shiny for Python, the container div for the widget (.shiny-ipywidget-output) is set to visibility: hidden during initialization. This prevents the Shiny busy indicator (spinner) from showing while the widget is rendering.

Reprex:

from shiny import App, reactive, render, ui
import pandas as pd
import plotly.express as px
import plotnine as p9
import asyncio
from shinywidgets import render_plotly, output_widget

app_ui = ui.page_fillable(
    ui.input_action_button("submit", "Let's go"),
    ui.layout_column_wrap(
        ui.card(
            ui.card_header("Card 1"),
            output_widget("plot"),
        ),
        ui.card(
            ui.card_header("Card 2"),
            ui.output_data_frame("table"),
        ),
        ui.card(
            ui.card_header("Card 3"),
            ui.output_plot("plot_p9"),
        ),
        width=1 / 3,
        fill=False,
    ),
)

def server(input, output, session):
    @reactive.calc
    @reactive.event(input.submit)
    async def analysis_result():
        await asyncio.sleep(5)
        return {
            "evals": pd.DataFrame({
                "category": ["Code Quality", "Documentation", "Testing", "Performance", "Security"],
                "score": [70, 60, 50, 80, 40],
            })
        }

    @render_plotly
    async def plot():
        res = await analysis_result()
        return px.bar(res["evals"], x="score", y="category", orientation="h")

    @render.plot
    async def plot_p9():
        res = await analysis_result()
        return (
            p9.ggplot(res["evals"], p9.aes(x="category", y="score"))
            + p9.geom_col()
            + p9.coord_flip()
            + p9.theme_minimal()
        )

    @render.data_frame
    async def table():
        res = await analysis_result()
        return res["evals"]

app = App(app_ui, server)

It would be nice to have Shiny's busy indicator visible over widgets as well, similar to the data_frame and "regular" plot.

The behavior comes from here:

async renderValue(el: HTMLElement, data): Promise<void> {
    // Allow for a None/null value to hide the widget (css inspired by htmlwidgets)
    if (!data) {
      el.style.visibility = "hidden";
      return;
    } else {
      el.style.visibility = "inherit";
    }

    // … rest of initialization …
}

I'm sure it's there for a reason (layout flickering?), but perhaps an outer visible div can be added that Shiny can use to attach the recalculating class to? Or introduce a parameter to skip hiding the div when there's no data?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions