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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions