Read-me-first Shiny tutorial II: finding the buzzers and butlers in a sample app

To get started with Shiny:

  • start RStudio,
  • install the Shiny package (Tools/Install Packages… on the RStudio menu),
  • open a new Shiny app (File/New File/Shiny Web App),
  • in the dialog that opens, give the app a name,
  • leave the radio button on Single File (app.R),
  • select a directory for the file,
  • click Create,
  • (a sample app will appear in the new script window that opens, called app.R),
  • run the sample app by clicking the Run App button at the upper-right of that window,
  • (a new, browser-like window will open in front of RStudio that displays the sample app),
  • play with the sample app to see what it does.

There are two ways to quit a Shiny app that’s running on your own computer like this one is (rather than on a server). One is just to close the app’s window by clicking the X in its upper-right corner.

However, sometimes your code will crash and that window will close, leaving you no X to click, but Shiny continues to run, blocking you from being able to restart the app in RStudio. That’s why it’s important to know the second way to quit a Shiny app.

At the upper-right of RStudio’s console window there’s now a small red stop sign. It’s a part of RStudio, not Shiny, and always appears while R code is executing. Click it and the app will close and you’ll get back control of the RStudio console.

Now you know one way to start a Shiny app and two ways to stop a Shiny app. Next let’s find the buzzers and the butlers in the sample code (if you don’t know what I mean by those words, start at the first page of this read-me-first Shiny tutorial and I’ll wait for you here). Remember there are three kinds of buzzers: inputs, buzzer variables, and buzzer-butlers; and three kinds of butlers: render… functions, buzzer-butlers, and observers.

library(shiny)

# Define UI for application that draws a histogram
ui <- fluidPage(
 
   # Application title
   titlePanel("Old Faithful Geyser Data"),
 
   # Sidebar with a slider input for number of bins 
   sidebarLayout(
      sidebarPanel(
         sliderInput("bins",
                     "Number of bins:",
                     min = 1,
                     max = 50,
                     value = 30)
      ),
 
      # Show a plot of the generated distribution
      mainPanel(
         plotOutput("distPlot")
      )
   )
)

# Define server logic required to draw a histogram
server <- function(input, output) {
 
   output$distPlot <- renderPlot({
      # generate bins based on input$bins from ui.R
      x <- faithful[, 2] 
      bins <- seq(min(x), max(x), length.out = input$bins + 1)
 
      # draw the histogram with the specified number of bins
      hist(x, breaks = bins, col = 'darkgray', border = 'white')
   })
}

# Run the application 
shinyApp(ui = ui, server = server)

As you examine the sample code on your screen or above, you’ll see it has two main pieces. First there’s a section where a variable called ui is defined. Select that section (from ui down to just above the server section) and run it by pressing control-enter or command-enter, depending on your system). Now, in RStudio’s console, just enter ui and press enter to see what just got stuffed into that variable.

<div class="container-fluid">
  <h2>Old Faithful Geyser Data</h2>
  <div class="row">
    <div class="col-sm-4">
    <form class="well">
      <div class="form-group shiny-input-container">
        <label class="control-label" for="bins">Number of bins:</label>
        <input class="js-range-slider" id="bins" data-min="1" data-max="50" data-from="30" data-step="1" data-grid="true" data-grid-num="9.8" data-grid-snap="false" data-keyboard="true" data-keyboard-step="2.04081632653061" data-drag-interval="true" data-data-type="number" data-prettify-separator=","/>
      </div>
    </form>
  </div>
  <div class="col-sm-8">
    <div id="distPlot" class="shiny-plot-output" style="width: 100% ; height: 400px"></div>
   </div>
  </div>
</div>

Yep, it’s nothing but a hunk of HTML code. If you already know HTML, you may find it easier to just create an HTML string on your own and stuff it into the ui variable yourself! Shiny provides a bunch of commands that write HTML for those who don’t know HTML, but if you already know HTML you may find it easier to skip the Shiny commands.

However, in any case you won’t already know the HTML for setting up a Shiny slider input like the one in the sample app. But you can capture or insert that HTML by running the sliderInput() function in the sample code. Select just that function now and run it and you’ll see what I mean.

The ui section of a Shiny app only runs one time and just holds the initial HTML for setting up the browser window. In other words, all the interesting stuff is in the server section, which, unlike the ui section, isn’t a variable, but an R function with two parameters, input and output. (There’s also a third parameter you’ll see in newer apps, session, but the sample app doesn’t use it.)

It’s Shiny itself that calls the server function and passes in lists of input and output variables. You’re allowed to read the input variables, which are all buzzers, but you’ll get an error if you try to write to them (if you also need a list of buzzers you can write to, you’ll set up a buzzer variable list yourself with a Shiny function called reactiveValues()). Likewise, you’re allowed to write to the output variables, but you’ll get an error if you try to read them.

You use reactiveValues() like this:

rv = reactiveValues()
rv$buzzt = 0                    # set up a buzzer

rv$buzzt = rv$buzzt + 1         # make it buzz

The reactiveValues() function creates a list-like object to which you can add any number of buzzers. However, one caution: if you add a list of buzzers to rv, like this, every buzzer in rv$pgn will buzz when you change any one of them.

pgn = list()
pgn$buzz1 = 0                   # buzzers in a list
pgn$buzz2 = 0
rv$pgn = pgn

rv$pgn$buzz1 = rv$pgn$buzz1 + 1 # buzz2 will also buzz!

This is almost certainly not what you want, so don’t try to group buzzers in lists inside a reactive variable. The easy solution is to just create a different reactive values variable, as Shiny will let you have any number of them.

pgn = reactiveValues()
pgn$buzz1 = 0
pgn$buzz2 = 0

Back in our sample code, inside the server function that Shiny calls, there’s nothing but a function called renderPlot() assigning a value to an output called distPlot. If you’ve never seen Shiny code before but you know R well, you’ll be surprised that renderPlot() takes a parameter that is a code block, designated using brackets, like this: renderPlot({ code }). I’d never used this kind of function parameter in R before using Shiny, but Shiny uses this construct all over the place.

As we learned earlier, all render… functions are butlers that respond to a buzzer. So you just found your butler, the renderPlot() function. Now where’s the buzzer? All inputs are buzzers, so it’s probably that input$bins variable. Whenever the value of that variable changes, Shiny will re-execute the renderPlot() butler. That butler draws a new plot and assigns it to output$distPlot.

bins <- seq(min(x), max(x), length.out = input$bins + 1)

Back up in the ui, note that the first parameter of the sliderInput() function is “bins”. This is the input id for that function. In other words, when the user moves the slider, the web browser sends the slider’s new value to Shiny, which assigns it to input$bins. There’s your buzzer. Still in the ui, note the line plotOutput(“distPlot”). Again, “distPlot” is the first parameter of the function, which is its id. But this time it’s an output id. Back in the server, note the renderPlot() function is assigning its value to output$distPlot. I’ve also highlighted these ids in the HTML code.

So in the sample app, when the user moves the slider, Shiny JavaScript code you never see gets the new value of the slider and sends it to Shiny on your computer (or, depending, on a server) with the id “bins.” Shiny assigns it to the input$bins variable and notes that the value of input$bins has changed, so it buzzes all the butlers that use it. In this case the buzzer wakes up the renderPlot() butler, who redraws the plot, using the new value for input$bins, and assigns it to output$distPlot. Shiny sees the new output, which is often also just a hunk of HTML, and sends it to the JavaScript running on the web browser, using the id “distPlot.” That JavaScript then replaces the HTML currently at the distPlot id with the new HTML. This all happens at computer speed and that’s how Shiny uses buzzers and butlers to create interaction and reactivity in a web browser.

As you start to write your own Shiny code, however, you’ll quickly run into problems with code that won’t run or won’t do what you expect. Next up I’ll show you a simple way to start debugging Shiny apps.

Leave a Reply

Your email address will not be published. Required fields are marked *