Shiny for Python

Ryan Johnson


What is Shiny

Shiny makes it easy to build web applications. It enables you to customize the layout and style of your application and dynamically respond to events, such as a button press, or dropdown selection.

What is Shiny for Python?

What the last slide said…

…but for Python! 🐍

Let’s create a Shiny for Python app!

Step 1 - Import Shiny

from shiny import *

Step 2 - Add UI, Server, App

  • Shiny applications consist of two parts: the user interface (or UI), and the server function. These are combined using a shiny.App object.
from shiny import *

# UI ------
app_ui = ui.page_fluid()

# Server ------
def server (input, output, session):
    ...

# Run app ----
app = App(app_ui, server)

Step 3 - Add Slider

from shiny import *

# UI ------
app_ui = ui.page_fluid(
    ui.input_slider("n", "N", 0, 100, 40)
)

# Server ------
def server (input, output, session):
    ...

# Run app ----
app = App(app_ui, server)

Step 4 - Add Server Logic

\[ n * 2 \]

from shiny import *

# UI ------
app_ui = ui.page_fluid(
    ui.input_slider("n", "N", 0, 100, 40)
)

# Server ------
def server (input, output, session):
    @render.text
    def txt():
        return f"n*2 is {input.n() * 2}"

# Run app ----
app = App(app_ui, server)

Step 5 - Add Text Output

from shiny import *

# UI ------
app_ui = ui.page_fluid(
    ui.input_slider("n", "N", 0, 100, 40),
    ui.output_text_verbatim("txt")
)

# Server ------
def server (input, output, session):
    @render.text
    def txt():
        return f"n*2 is {input.n() * 2}"

# Run app ----
app = App(app_ui, server)

User Interface (UI) - Inputs

All input UI objects take the same first two string arguments:

  • id: an identifier used to refer to input’s value in the server code. For example, id="x1" corresponds with input.x1() in the server function.
  • label: a description for the input that will appear next to it.
  • Check out all the UI inputs here
  • The next slides will show the most common UI objects

UI - Checkbox Inputs

from shiny import ui, App

app_ui = ui.page_fluid(
    ui.input_checkbox("x1", "Checkbox")
)

app = App(app_ui, None)

UI - Numeric Inputs

from shiny import ui, App

app_ui = ui.page_fluid(
    ui.input_numeric("x1", "Number", value=10),
    ui.input_slider("x2", "Slider", value=10, min=0, max=20),
)

app = App(app_ui, None)

UI - Text Inputs

from shiny import ui, App

app_ui = ui.page_fluid(
    ui.input_text("x1", "Text", placeholder="Enter text"),
    ui.input_text_area("x2", "Text area", placeholder="Enter text"),
    ui.input_password ("x3", "Password", placeholder="Enter password"),
)

app = App(app_ui, None)

UI - Selection Inputs

from shiny import ui, App

choices = {"a": "Choice A", "b": "Choice B"}

app_ui = ui.page_fluid(
    ui.input_select("x1", "Select", choices),
    ui.input_checkbox_group("x2", "Checkbox group", choices),
    ui.input_radio_buttons("x3", "Radio buttons", choices),
)

app = App(app_ui, None)

UI - Date Inputs

from shiny import ui, App

choices = {"a": "Choice A", "b": "Choice B"}

app_ui = ui.page_fluid(
    ui.input_date("x9", "Date input"),
    ui.input_date_range("x10", "Date range input"),
)

app = App(app_ui, None)

UI - Inputs

Click here to view a Shiny for Python app with a variety of inputs!

UI - Creating Outputs

  • UI outputs create a spot on the webpage to put results from the server.
  • All UI outputs require an id argument, which corresponds to the server’s output ID.

UI - Creating Outputs

For example, if you create this UI output:

ui.output_text(id = "my_text")

Then you could connect it to the server output using the code below:

def server(input, output, session):
    @render.text
    def my_text():
        return "some text to show"

UI - Outputs

Match the UI outputs IDs with the server output IDs.

Fix this app

03:00

Server Logic

In Shiny, the server logic is defined within a function which takes three arguments:

  1. input
  2. output
  3. session
def server(input, output, session):
    # Server code goes here

Server Logic

The server function is where you:

  1. Access and use inputs
  2. Define outputs

Server Logic - Access Inputs

Input values are accessed via input.x(), where x is the id of the input.

Server Logic - Access Inputs

Comparing Shiny for Python to R

  • Shiny for Python: input.x()
  • Shiny for R: input$x

Server Logic - Defining Outputs

Defining outputs is a three step process:

  1. Create a function with no parameters
def server(input, output, session):
    def txt():

Server Logic - Defining Outputs

Defining outputs is a three step process:

  1. Create a function with no parameters
  1. Apply a @render.___ decorator
def server(input, output, session):
    @render.text
    def txt():

Server Logic - Defining Outputs

Defining outputs is a three step process:

  1. Create a function with no parameters
  2. Apply a @render.___ decorator
  1. Add logic
def server(input, output, session):
    @render.text
    def txt():
        if input.enable():
            return "Yes!"
        else:
            return "No!"

Putting it together!

Make this app work!

Working app here

05:00

A quick primer on reactivity

What if my Shiny app does more than just: \[ n * 2 \]

A quick primer on reactivity

Create a separate reactive function!

@reactive.Calc
def double_x():
    return input.x() * 2


And then use the reactive function in an output:

@render.text
def txt():
    return f"x * 2 is {double_x()}"
  • Shiny for Python: reactive.Calc
  • Shiny for R: shiny::reactive()

A quick primer on reactivity

Fix this app!

  • Add a reactive.Calc that does something to the value n
  • add the reactive.Calc function to the output
03:00

A quick primer on reactivity

Comparing Shiny for Python to R

  • Shiny for R: shiny::reactive()
  • Shiny for Python: reactive.Calc
  • Shiny for R: shiny::observe()
  • Shiny for Python: reactive.Effect

More info here on Shiny for Python reactivity

Reactive Events

Maybe you want to control when your outputs run.

…enter @reactive.event()

from shiny import *

app_ui = ui.page_fluid(
    ui.input_slider("n", "N", min=1, max=100, value=1),
    ui.input_action_button("compute", "Compute!"),
    ui.output_text_verbatim("result", placeholder=True))

def server(input, output, session):
    @render.text
    @reactive.event(input.compute)
    async def result():
        return f"Result: {input.n()}"

app = App(app_ui, server)

See example app here.

Layout and Style

Sidebar Layout

app_ui = ui.page_sidebar(

    ui.sidebar(
        ...
    ),
    ui.card(
        ...
    ),
)

Run app here

Layout and Style - Rows and Columns

from shiny import App, ui

style="border: 1px solid #999;"

app_ui = ui.page_fluid(
    ui.row(
        ui.column(4, "row-1 col-1", style=style),
        ui.column(8, "row-1 col-2", style=style),
    ),
    ui.row(
        ui.column(6, "row-2 col-1", style=style),
        ui.column(6, "row-2 col-2", style=style),
    ),
)

app = App(app_ui, None)

Layout and Style - Cards

Cards are a flexible and extensible content container for your inputs and outputs.

See app here.

Layout and Style - Tabs and Navigation

  • Allows you to create apps with multiple pages

  • Explore navigation options here.

  • Take 1 min and explore this app.

01:00

Wrap Up

We only just scratched the surface 😬!


…but you can learn more here!

Final Project

Recreate this application.

Start here.

03:00

Sharing your Shiny for Python Apps

  • Shinylive: for simple applications with minimal package dependencies

  • shinyapps.io: hosted service for deploying shiny for python apps

  • Posit Connect: Requires v2022.07+

Deploying to Posit Connect

  1. Install rsconnect-python package
  2. Register you Connect server/account
  3. Create a requirements.txt file
  4. Deploy!
rsconnect deploy shiny . app.py