Resize shiny app to fit every screen size - r

Consider the toy app below. I built this app using a 22 inch monitor but when I open it with my 14inch screen laptop the graphics are too big and I am able to see only half of it. Question: Is there a way to make the app fit different screen sizes and make the graphics and tables resize to fit the screen size?
library(shiny)
library(shinyjs)
mytest <- c("first","second")
# Define UI for application that draws a histogram
ui <- fluidPage(
useShinyjs(), # to initialise shinyjs
# Application title
titlePanel("disable"),
sidebarLayout(
sidebarPanel(
selectInput(inputId = "test",
label = "Test",
choices = mytest,
selected = "first"),
disabled( # start as disabled
checkboxInput("checkbox","check this box", FALSE))
),
mainPanel(
plotOutput("distPlot")
)))
server <- function(input, output) {
output$distPlot <- renderPlot({
switch(input$test,
"first" = {
plot(rnorm(100))
disable("checkbox")
},
{
enable("checkbox")
if(input[["checkbox"]] == FALSE){
"second" = plot(rnorm(1000), col="blue")
} else{
"second" = plot(rnorm(10000), col="red")
}
}
)
})
}
shinyApp(ui = ui, server = server)

The plotOutput function has a height argument and a width argument. You can use the CSS relative units vh and vw for these arguments: percentage of the height of the viewport and percentage of the width of the viewport. For example height = "50vh" to take 50% of the viewport.

Related

How to set height and width parameter in renderCachedPlot()?

I have a Shiny App which can display graphs based on user input (ie. years selected, type of dataset, etc.)
This is similar to what I have in the original code, reactively takes the user input and the display figure size is controlled by height and width parameters.
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("plotType", "Plot type:",
c("Scatter Plot" = "scatter", "Line Plot" = "line"))
),
mainPanel(
plotOutput("plot")
)
)
)
server <- function(input, output, session) {
output$plot <- renderPlot({
plot(mtcars$mpg, mtcars$wt)}, height = 1000, width = 1000)
}
shinyApp(ui, server)
However, since there's so much data and the graph can take a bit of time to load, I wanted to have a cache method so that once a graph version loaded, it's can be retrieved without reloading. However, "width, height
not used. They are specified via the argument sizePolicy." - https://mpn.metworx.com/packages/shiny/1.6.0/reference/renderCachedPlot.html
But as the two code examples show, sizePolicy does not expand the height as much as the original code. In fact, in the code I have, it shrinks the plot incredibly small. I'm very confused about this and could not find similar questions/answers on the web. Am I using the renderCachedPlot wrong? Am I supposed to combine it with renderPlot? Or does renderCachedPlot is unable to set figure size? If so, what's better way to save on graph loading time?
server <- function(input, output, session) {
output$plot <- renderCachedPlot({
if (input$plotType == "scatter") {
plot(mtcars$mpg, mtcars$wt)
} else if (input$plotType == "line") {
plot(mtcars$wt, type = "l")
}
}, cacheKeyExpr = reactive({
input$plotType
}), sizePolicy = sizeGrowthRatio(width = 1000, height = 1000, growthRate = 1.2))
}
2/1 Update: So I realize that in the within the ui code, I could specify the plotOutput("plot", width = 1000, height = 1000) and see a change. This brings about another issue where in the server code, there are calculations done dependent on the number of graphs selected to give plotwidth and plotheight. How and should I call upon a variable in the server to the UI?
Thanks in advance! Please direct me to any resources I missed.

How do I draw a circle using JavaScript in R Shiny?

I am looking to draw simple shapes in R Shiny. (The goal is to draw a static legend using HTML instead of loading a png.)
I can't get the canvas tag to work. It simply does not draw anything.
library(shiny)
ui <- fluidPage(
tags$script(HTML(
"function draw_legend() {",
"var canvas = document.getElementById('simple_legend');",
"const canvas = document.getElementById('canvas');",
"const ctx = canvas.getContext('2d');",
"ctx.fillStyle = 'green';",
"ctx.fillRect(10, 10, 150, 100);",
"}"
)),
sidebarLayout(
sidebarPanel(
),
mainPanel(
tags$body(onload="draw_legend();"),
tags$canvas(id="simple_legend", height = "30"),
tags$div("Some text")
)
)
)
server <- function(input, output, session) {
}
shinyApp(ui = ui, server = server)
Expanding on my comment, here is an app showing two techniques for drawing a circle in Shiny, one using CSS, the other using a data frame. You can make obvious adjustments to size, colour, position etc to get the effect you want.
library(shiny)
library(tidyverse)
ui <- fluidPage(
tags$head(
tags$style("#circleText {color: red; font-size: 20px; }")
),
uiOutput("circleText"),
plotOutput("circleGraph")
)
server <- function(input, output, session) {
output$circleText <- renderUI({
HTML("⬤")
})
output$circleGraph <- renderPlot({
tibble(theta=seq(0, 2*pi, 0.025), x=sin(theta), y=cos(theta)) %>%
ggplot(aes(x, y)) +
geom_path() +
coord_fixed() +
theme_void()
},
height=75,
width=75)
}
shinyApp(ui = ui, server = server)
Giving
You have to
remove the line const canvas = document.getElementById('canvas');
set a width to the canvas element, not only a height

How can I give a Shiny screen object flexible dimensions?

I am a Shiny novice with what seems to me to be a simple question. I have a test app.R that reads a data frame, asks the user for a start date and displays a time series area chart starting from that date. The app.R works, but when I expand the size of my screen window, the chart is fixed in the top-to-bottom dimension. The left-to-right dimension is flexible. How can I make the top-to-bottom dimension flexible too? I want me graphical object to fill the window. Here is my reprex:
#library(Shiny)
#library(ggplot2)
a <- c(seq.Date(as.Date("2019-01-01"),as.Date("2019-06-01"),by="month"))
b <- c(4,7,2,9,13,6)
df <- data.frame(a=a,b=b)
ui <- fluidPage(
titlePanel("Test example"),
fluidRow(
sidebarPanel(
dateInput(inputId="StartDate",label="Please enter a start date:",
value="2019-01-01",min="2019-01-01",max="2019-06-01")
)
),
fluidRow(
column(12),
mainPanel(
plotOutput("distPlot")
)
)
)
server <- function(input, output) {
output$distPlot <- renderPlot({
FirstDate <- input$StartDate
ggplot(filter(df,a>=as.Date(FirstDate)))+
geom_area(aes(x=a,y=b,fill="red"),alpha=0.4)+
geom_line(aes(x=a,y=b,colour="red"),size=1)+
theme(legend.position="none")
})
}
shinyApp(ui = ui, server = server)
Thank you.
Philip
plotOutput("distPlot",
width ="80vw",
height="60vw")
(with help from nirgrahamuk on the RStudio Community forum).

In RShiny, using actionButton values within conditionalPanel causing many headaches

I have spent the last half hour reading about conditionalPanel, as well as actionButtons and how their value increments by 1 each time the button is pressed. With that said, here's what I'm trying to do below and what problem I am facing:
The problem
I am using conditionalPanel to toggle between two different renderPlots. The reason I am using two different renderPlots, rather than both plots within the same renderPlot, is because it is important that the two plots have different height / width dimensions. I am using the actionButton's value within the conditionalPanel, which IS BAD.
What I want
I want movement.chart to be displaying if togglemovement actionButton was more recently pressed of the 2 buttons. I want shot.chart to be displaying if toggleshotchart was more recently pressed.
What I wish I could do
If I could only use plotData$value within the conditional, I would be all set. plotData$value is being used to enter if statements in the renderPlots to determine which plots should be plotted, but I can't use them in conditionalPanel.
With all of that said, here's a shortened version of my project.
library(shiny)
# 2. UI layout
# ============
ui <- fluidPage(
fluidRow(
column(width = 4, align = 'center',
actionButton(inputId = 'toggleshotchart', label = 'Launch Shots'),
actionButton(inputId = 'togglemovement', label = 'Launch Movements')
),
# This displays either the shot or movement chart
# ===============================================
column(width = 8, align = 'left',
conditionalPanel("input.togglemovement > input.toggleshotchart",
plotOutput('movement.chart', height = 650, width = 1128)
),
conditionalPanel("input.togglemovement <= input.toggleshotchart",
plotOutput('shot.chart', height = 846, width = 900)
)
)
)
)
# 3. Server Layout
# ================
server <- shinyServer(function(input, output) {
# Create some reactive stuff to toggle charts
plotData <- reactiveValues(value = NULL)
observeEvent(input$toggleshotchart, {
plotData$value <- 0
})
observeEvent(input$togglemovement, {
plotData$value <- 1
})
# create the first chart
output$shot.chart <- renderPlot({
# this chart is displayed at launch
if (is.null(plotData$value)) {
plot(c(1,2,3,4,5), c(1,2,3,4,5))
}
# this chart SHOULD BE displayed after clicking toggleshotchart
else if(plotData$value == 0) {
hist(rnorm(10))
}
# Do nothing (prev displayed motion chart here)
else {
# do nothing
}
})
# this chart SHOULD BE displayed after clicking togglemovementchart
output$movement.chart <- renderPlot({
if(plotData$value == 1) {
hist(c(1,2,3,2,1))
}
})
})
shinyApp(ui = ui, server = server)
I read a lot about resetting the actionButton's value, but couldn't find anything that would fix my problem. It seems like resetting actionButton values to 0 is not easy / cannot be done. Any thoughts would be greatly appreciated on this task of mine!
EDIT - I asked this related question earlier - In RShiny, change plot width / height for separate plots within same renderPlot() - where the solution works, but is for a slightly different question. The more I worked on this issue, the more I realized that my initial question did not address my actual problem.
What about using radioButtons to toggle between two plots?
library(shiny)
# 2. UI layout
# ============
ui <- fluidPage(
fluidRow(
column(width = 4,
radioButtons("choice_plot","Launch",choices=list("Shots","Movements"), selected="Shots")),
# This displays either the shot or movement chart
# ===============================================
column(width = 8, align = 'left', uiOutput("plot_ui"))
)
)
# 3. Server Layout
# ================
server <- shinyServer(function(input, output) {
output$plot_ui <- renderUI({
if(input$choice_plot == 'Shots') {
plot.width = 1128
plot.height = 650
}else{
plot.width = 900
plot.height = 846
}
plotOutput('plot', width = plot.width, height = plot.height)
})
output$plot <- renderPlot({
if(input$choice_plot == 'Shots'){
hist(rnorm(10))
}else{
hist(c(1,2,3,2,1))
}
})
})
shinyApp(ui = ui, server = server)
Here is as well my try with actionButton:
library(shiny)
# 2. UI layout
# ============
ui <- fluidPage(
fluidRow(
column(width = 4,
actionButton("button1", "Shots"),
actionButton("button2", "Movements")),
# This displays either the shot or movement chart
# ===============================================
column(width = 8, align = 'left', uiOutput("plot_ui"))
)
)
# 3. Server Layout
# ================
server <- shinyServer(function(input, output) {
output$plot_ui <- renderUI({
if(input$button1 == 1) {
plot.width = 1128
plot.height = 650
}else{
plot.width = 900
plot.height = 846
}
plotOutput('plot', width = plot.width, height = plot.height)
})
v <- reactiveValues(data = NULL)
observeEvent(input$button1, {
v$data <- rnorm(10)
})
observeEvent(input$button2, {
v$data <- c(1,2,3,2,1)
})
output$plot <- renderPlot({
if (is.null(v$data)) return()
hist(v$data)
})
})
shinyApp(ui = ui, server = server)

Get the size of the window in Shiny

I Would like to determine the size of the browser window in Shiny to help me layout my plot divs better. Specifically I would like to determine the aspect ratio of the window to see how many divs I should spread across the screen and it still look nice. My initial thought would be that the number of plots would be floor(width/(height-navbar_height)).
I did some looking for this and I am currently unable to locate a possible solution and am currently lead to believe that this feature is simply not present in the clientData structure. Any thoughts?
See the example below. It uses Javascript to detect the browser window size (initial size and any resize), and use Shiny.onInputChange to send the data to the server code for processing. It uses shiny:connected event to get the initial window size, as Shiny.onInputChange is not ready for use until shiny is connected.
library(shiny)
# Define UI for application that draws a histogram
ui <- shinyUI(fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
tags$head(tags$script('
var dimension = [0, 0];
$(document).on("shiny:connected", function(e) {
dimension[0] = window.innerWidth;
dimension[1] = window.innerHeight;
Shiny.onInputChange("dimension", dimension);
});
$(window).resize(function(e) {
dimension[0] = window.innerWidth;
dimension[1] = window.innerHeight;
Shiny.onInputChange("dimension", dimension);
});
')),
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show a plot of the generated distribution
mainPanel(
verbatimTextOutput("dimension_display"),
plotOutput("distPlot")
)
)
))
# Define server logic required to draw a histogram
server <- shinyServer(function(input, output) {
output$dimension_display <- renderText({
paste(input$dimension[1], input$dimension[2], input$dimension[2]/input$dimension[1])
})
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)
There is a new and much simpler way to do this since 2021: using the {shinybrowser} package. Example:
library(shiny)
ui <- fluidPage(
shinybrowser::detect(),
"Window size:",
textOutput("size")
)
server <- function(input, output, session) {
output$size <- renderText({
paste(
shinybrowser::get_width(),
"x",
shinybrowser::get_height()
)
})
}
shinyApp(ui, server)
Note that {shinybrowser} is currently on GitHub only and not yet on CRAN (should be in the near future). Note also that {shinybrowser} only gives you the initial dimensions, but will not update if the browser is resized.
Shorter version for getting window dimensions in shiny with JS from package htmlwidgets:
window_height <- JS('window.innerHeight')
window_width <- JS('window.innerWidth')

Resources