Skip to content

Add busy indicators (aka spinners) #918

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 70 commits into from
May 10, 2024
Merged

Add busy indicators (aka spinners) #918

merged 70 commits into from
May 10, 2024

Conversation

nstrayer
Copy link
Collaborator

@nstrayer nstrayer commented Dec 18, 2023

This PR adds busy indication, providing users with a visual cue when the server is busy calculating outputs or otherwise serving requests to the client. More specifically, a spinner is shown on each calculating/recalculating output, and a pulsing banner is shown at the top of the page when the app is otherwise busy. Use the new ui.busy_indicator.options() function to customize the appearance of the busy indicators and ui.busy_indicator.use() to disable/enable them.

Here is an illustration of the default spinner and pulse experience:

shiny run shiny/api-examples/busy_indicators/app-express.py
busy.mp4

This PR also adds busyIndicatorOptions() to customize the appearance of both spinners and the pulse. Here's an example showing off 4 of the main spinner_types available: ring, bars, dots, and pulse.

shiny run examples/busy_indicators/app.py
spinner-types.mp4

@nstrayer nstrayer requested review from wch and jcheng5 December 18, 2023 20:20
@wch
Copy link
Collaborator

wch commented Dec 19, 2023

If the user experience here is smooth and not jarring, then I think it would make sense to enable spinners by default.

Copy link
Collaborator Author

@nstrayer nstrayer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the scope to enable spinners by default at the page-level. A small spinner is placed in the upper right corner of an app when any element on the page has .recalculating. There is also a way to enable spinners for a specific element or to turn them off by adding the class no-spinner to (anywhere on) the page.

@nstrayer nstrayer requested a review from wch December 22, 2023 23:39
@cpsievert cpsievert self-assigned this Apr 3, 2024
@cpsievert cpsievert enabled auto-merge May 10, 2024 21:42
@cpsievert cpsievert added this pull request to the merge queue May 10, 2024
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks May 10, 2024
@cpsievert cpsievert merged commit 811f8c7 into main May 10, 2024
32 checks passed
@cpsievert cpsievert deleted the spinners branch May 10, 2024 22:09
@gfleetwood
Copy link

gfleetwood commented May 14, 2024

Hello. I've been tracking this thread for this feature, and tried it out - after upgrading the library - since I saw that it was merged. However, the line ui.busy_indicators.options(spinner_type = 'ring') gives an error:

module 'shiny.ui' has no attribute 'busy_indicators'

When will this be added to the pypi package, and to the documentation?

@jcheng5
Copy link
Collaborator

jcheng5 commented May 14, 2024

@gfleetwood You can install from main with pip install git+https://github.com/posit-dev/py-shiny. We merge to main much more frequently than we release to PyPI, it might be a couple of weeks or so before the next one.

@gfleetwood
Copy link

@jcheng5 Got it. Thanks!

@gfleetwood
Copy link

@nstrayer Hello. I looked at the three examples showing how this feature works, but I'm still confused. It was working for a while in my app, but now it's not, and I don't know what is wrong. This is a minimal example, except that I will have multiple functions to display many dataframes, each having its own busy indicator.

from shiny import App, reactive, render, req, ui
from shiny.ui import TagList, div, h3, head_content, tags
import pandas as pd 
import requests

def server(input, output, session):

    @render.data_frame
    @reactive.event(input.search_button)
    def get_df():

        server_endpoint = 'https://'
        payload_in = {'search_term': input.search_term().strip()}

        #ui.busy_indicators.options(spinner_type = 'ring'),
        response = requests.post(server_endpoint, json = payload_in)
        payload_out = response.json()['data']

        result = render.DataGrid(pd.DataFrame(json.loads(payload_out)), width = '1000px') 

        return(result)

app_ui = ui.page_fluid(

    #ui.busy_indicators.options(spinner_type = 'ring'),

    ui.input_text('search_term', 'Enter Search Term'),
    ui.input_action_button('search_button', 'Search'),
    ui.busy_indicators.use(spinners = "spinners"),
    ui.output_data_frame('get_df'),
)

app = App(app_ui, server)

@cpsievert
Copy link
Collaborator

cpsievert commented May 28, 2024

@gfleetwood there were some issues with recent releases that are now fixed in 0.10.2 -- please try upgrading to see if fixes your problems: pip install -U shiny

If you are still having problems after upgrading, please feel free to file a new issue (I promise I'll respond 😄 )

@gfleetwood
Copy link

gfleetwood commented May 29, 2024

@cpsievert No need, the upgrade did it. Thanks!

EDIT: The issue returned. I opened an issue here: #1433

@roivant-matts
Copy link

I'm having trouble using this with express - is there a minimal express example?

@cpsievert
Copy link
Collaborator

@roivant-matts here's a very minimal one

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants