Reputation: 26323
Plots in shiny support click and brush handlers. Is it possible to "clear"/"remove"/"delete" the brushed rectangle without having the user click elsewhere on the plot? For example, if I wanted to just store the brushed coordinates once the brush is finished and then clear the plot, this is the code I would use but I don't know how to do the clearing bit.
library(ggplot2)
library(shiny)
runApp(shinyApp(
ui = fluidPage(
plotOutput("plot",
brush = brushOpts("plotBrush", delay = 5000)),
actionButton("clear", "Clear")
),
server = function(input, output, session) {
values <- reactiveValues(brush = NULL)
output$plot <- renderPlot({
ggplot(cars, aes(speed, dist)) + geom_point()
})
brush <- reactive({
input$plotBrush
})
observeEvent(input$clear, {
cat(str(brush()))
# clear the brushed area
})
}
))
Upvotes: 9
Views: 4071
Reputation: 1234
As of Shiny version 0.14, it is possible to use the session
object to reset a plot's brush.
Below is a simple Shiny app demoing the use of session$resetBrush(<BRUSH_ID>)
to clear away a brushed region. The app allows one to highlight a region of points or remove the brushed region while keeping the points highlighted or remove the brushed region and reset the color of the points.
See about halfway down at https://shiny.rstudio.com/reference/shiny/latest/session.html for the official documentation.
library(shiny)
library(ggplot2)
shinyApp(
ui = fluidPage(
plotOutput(
outputId = "plot",
brush = brushOpts(
id = "plotBrush",
delay = 5000
)
),
actionButton("clearBrush", "Clear brush"),
actionButton("resetPlot", "Reset plot")
),
server = function(input, output, session) {
output$plot <- renderPlot({
ggplot(mtcars, aes(wt, mpg)) +
geom_point() +
geom_point(
data = brushedPoints(mtcars, brush),
color = "#79D8CB",
size = 2
)
})
brush <- NULL
makeReactiveBinding("brush")
observeEvent(input$plotBrush, {
brush <<- input$plotBrush
})
observeEvent(input$clearBrush, {
session$resetBrush("plotBrush")
})
observeEvent(input$resetPlot, {
session$resetBrush("plotBrush")
brush <<- NULL
})
}
)
Upvotes: 10
Reputation: 2757
I find myself in a similar situation where I have multiple brushes and need a button to "clear the world". I haven't found an official way to remove the brush div with R code, but it turns out there is this awesome package called shinyjs ;)
library(shiny)
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
actionButton("clear", "Clear brush"),
fluidRow(
column(
width = 6,
plotOutput("p1", brush = brushOpts("b1"))
),
column(
width = 6,
plotOutput("p2", brush = brushOpts("b2"))
)
),
fluidRow(
column(
width = 6,
verbatimTextOutput("brush1")
),
column(
width = 6,
verbatimTextOutput("brush2")
)
)
)
server <- function(input, output) {
values <- reactiveValues(
brush1 = NULL,
brush2 = NULL
)
# update reactive values when input values change
observe({
values$brush1 <- input$b1
values$brush2 <- input$b2
})
# display brush details
output$brush1 <- renderPrint({
values$brush1
})
output$brush2 <- renderPrint({
values$brush2
})
# clear brush values and remove the div from the page
observeEvent(input$clear, {
values$brush1 <- NULL
values$brush2 <- NULL
runjs("document.getElementById('p1_brush').remove()")
runjs("document.getElementById('p2_brush').remove()")
})
output$p1 <- renderPlot({
input$clear
m <- brushedPoints(mtcars, values$brush1, allRows = TRUE)
qplot(data = m, wt, mpg, colour = selected_) +
theme(legend.position = "none")
})
output$p2 <- renderPlot({
input$clear
m <- brushedPoints(mtcars, values$brush2, allRows = TRUE)
qplot(data = m, wt, mpg, colour = selected_) +
theme(legend.position = "none")
})
}
shinyApp(ui, server)
IMO, shiny should really provide something like:
clearBrush <- function(id) {
shinyjs::runjs(sprintf("document.getElementById('%s_brush').remove()", id))
}
Upvotes: 5
Reputation: 261
First a note on your server arguments. To assign reactiveValues you have to do it inside a reactive expression. So to capture the brush coordinates you need to use this
observeEvent(input$plotBrush,{
if(is.null(values$brush)){
values$brush <- input$plotBrush}
})
instead of this
brush <- reactive({
input$plotBrush
})
The second version creates a function called brush that you would call with brush().
One way to clear the plot upon brush is to test if values$brush is null, and alter what you do based on that. In this case if values$brush is not null then a blank plot is displayed and new points cannot be selected.
library(ggplot2)
library(shiny)
runApp(list(
ui = fluidPage(
plotOutput("plot",
brush = brushOpts("plotBrush",
delay = 5000,
resetOnNew = TRUE)
# resetOnNew = TRUE clears the brush
# each time a new plot is displayed.
),
p("Brushed Points:"),
verbatimTextOutput("brushedPoints")
),
server = function(input, output, session) {
values <- reactiveValues(brush = NULL)
output$plot <- renderPlot({
if(is.null(values$brush)){
ggplot(cars, aes(speed, dist)) + geom_point()
} else {
ggplot(cars, aes(speed, dist)) + geom_blank()
}
})
observeEvent(input$plotBrush,{
#Run this whenever points are brushed
if(is.null(values$brush)){
values$brush <- input$plotBrush}
})
output$brushedPoints <- renderPrint({
values$brush
})
}
))
A second option is also available, see https://stackoverflow.com/a/35066532/3229332 for the explanation
library(ggplot2)
library(shiny)
runApp(list(
ui = fluidPage(
plotOutput("plot",
brush = brushOpts("plotBrush",
delay = 5000,
resetOnNew = TRUE)
# resetOnNew = TRUE clears the brush
# each time a new plot is displayed.
),
p("Brushed Points:"),
verbatimTextOutput("brushedPoints")
),
server = function(input, output, session) {
values <- reactiveValues(brush = NULL)
output$plot <- renderPlot({
if(is.null(values$brush)){
ggplot(cars, aes(speed, dist)) + geom_point()
} else {
ggplot(cars, aes(speed, dist)) + geom_blank()
}
})
observeEvent(input$plotBrush,{
#Run this whenever points are brushed
output$plot <- renderPlot({
if(is.null(values$brush)){
ggplot(cars, aes(speed, dist)) + geom_point()
values$brush <- input$plotBrush
} else {
ggplot(cars, aes(speed, dist)) + geom_blank()
}
})
}
)
output$brushedPoints <- renderPrint({
values$brush
})
}
))
Upvotes: 0