Related
This is a follow-up question to this Color an area with a sliderInput in a shiny app
Suppose I have this image:
How could I apply this solution by #ismirsehregal to this picture. I think I have to put the x and y from the esophagus to the code, but I don't know how to get the x and y of the esophagues (green in the picture):
Code from Color an area with a sliderInput in a shiny app
library(shiny)
library(plotly)
library(shinyWidgets)
DF <- data.frame(
x = c(cos(seq(0.01, 10, 0.01)) * 1000:1 + 1000, cos(seq(0.01, 10, 0.01)) * 1000:1 + 1500),
y = rep(1:1000, 2),
id = c(rep("trace_1", 1000), rep("trace_2", 1000))
)
ui <- fluidPage(
br(),
column(
2,
noUiSliderInput(
inputId = "noui2",
label = "Slider vertical:",
min = 0,
max = 1000,
step = 50,
value = c(100, 400),
margin = 100,
orientation = "vertical",
direction = c("rtl"),
width = "100px",
height = "350px"
)
),
column(4, plotlyOutput("plot")),
verbatimTextOutput(outputId = "res2")
)
server <- function(input, output, session) {
output$res2 <- renderPrint(input$noui2)
plotDF <- reactive({
plotDF <- DF[DF$y %in% input$noui2[1]:input$noui2[2], ]
plotDF$id <- paste0("filtered_", plotDF$id)
plotDF
})
output$plot <- renderPlotly({
fig <- plot_ly(
rbind(DF, plotDF()),
x = ~ x,
y = ~ y,
split = ~ id,
type = "scatter",
mode = "lines",
color = I("black"),
fillcolor = 'red',
showlegend = FALSE
) |> style(fill = 'tonexty', traces = 2)
})
}
shinyApp(ui, server)
The following approach doesn't meet the title of your question, but it shows the procedure I mentioned in your previous post.
You will need to save a modified png file (transparent esophagus - edited with gimp's "fuzzy select tool") in your apps www folder for this to work (please find it below).
I'm now using plotlyProxyInvoke to update the data without re-rendering the plot:
library(shiny)
library(plotly)
library(shinyWidgets)
slider_min <- 0
slider_max <- 45
lower_slider_value <- 5
upper_slider_value <- 18
x_position_trace_1 <- 40
x_position_trace_2 <- 50
DF <- data.frame(
x = c(rep(x_position_trace_1, 2), rep(x_position_trace_2, 2)),
y = rep(c(lower_slider_value, upper_slider_value), 2),
id = c(rep("trace_1", 2), rep("trace_2", 2))
)
ui <- fluidPage(
br(),
column(
2,
noUiSliderInput(
inputId = "noui2",
label = "Slider vertical:",
min = slider_min,
max = slider_max,
step = 1L,
value = c(lower_slider_value, upper_slider_value),
margin = 1,
orientation = "vertical",
width = "100px",
height = "350px"
)
),
column(4, plotlyOutput("myPlot", height = "800px")),
verbatimTextOutput(outputId = "res2")
)
server <- function(input, output, session) {
output$res2 <- renderPrint(input$noui2)
output$myPlot <- renderPlotly({
fig <- plot_ly(
DF,
x = ~ x,
y = ~ y,
split = ~ id,
type = "scatter",
mode = "lines",
color = I("white"),
fillcolor = 'red',
showlegend = FALSE
) |> layout(
images = list(
list(
source = "/esophagus.png",
xref = "x",
yref = "y",
x = 0,
y = -16,
sizex = 93,
sizey = 93,
sizing = "stretch",
opacity = 1,
layer = "above"
)
),
plot_bgcolor = "rgba(0, 0, 0, 0)",
paper_bgcolor = "rgba(0, 0, 0, 0)",
xaxis = list(
zerolinecolor = '#ffff',
zerolinewidth = 2,
gridcolor = 'ffff',
range = list(0, 100)),
yaxis = list(
zerolinecolor = '#ffff',
zerolinewidth = 2,
gridcolor = 'ffff',
range = list(80, -20)
# or autorange = "reversed"
)
) |> style(fill = 'tonexty', traces = 2)
})
myPlotProxy <- plotlyProxy("myPlot", session)
observe({
plotlyProxyInvoke(myPlotProxy, "restyle", list(x = list(rep(x_position_trace_1, 2), rep(x_position_trace_2, 2)), y = list(input$noui2, input$noui2)), list(0, 1))
})
}
shinyApp(ui, server)
Image for the www folder - save as "esophagus.png".
To visualize the transparent area (grey) open the image in a new browser tab (chrome):
Edit: Here is another lightweight approach without using {plotly}.
This isn't perfectly aligned yet and it might make sense to work wit % instead of px, however it shows the principle:
We can simply provide the esophagus image with a red background image and modify the css properties background-size and background-position-y:
library(shiny)
library(shinyjs)
library(shinyWidgets)
ui <- fluidPage(
useShinyjs(),
br(),
column(
2,
noUiSliderInput(
inputId = "noui2",
label = "Slider vertical:",
min = 0,
max = 1000,
step = 50,
value = c(100, 400),
margin = 100,
orientation = "vertical",
direction = c("rtl"),
width = "100px",
height = "350px"
)
),
column(
4,
tags$img(
id = "esophagus",
height = 1000,
width = "100%",
src = "/esophagus.png",
style = "background-image: url(red_background.png); background-repeat: no-repeat; background-size: 100% 30%; background-position-y: 40%;"
)
),
verbatimTextOutput(outputId = "res2")
)
server <- function(input, output, session) {
output$res2 <- renderPrint(input$noui2)
observeEvent(input$noui2, {
runjs(paste0('$("#esophagus").css("background-size", "100% ', input$noui2[2] - input$noui2[1], 'px");'))
runjs(paste0('$("#esophagus").css("background-position-y", "', 1000 - input$noui2[2], 'px");'))
})
}
shinyApp(ui, server)
Save as "red_background.png" in your www folder:
I have a simple app as shown below. How can I modify the fluidRow statement to include a blank space between A1 and C1 so that all selectors align properly? In this instance, I do not want a 'B1' selector at all.
library(shiny)
inputs <- c("A0", "B0", "C0")
ui <- fluidPage(
fluidRow(column(width = 2, inputs %>% map(~numericInput(.x, .x, min = 1, max = 10, value = 3))),
column(width = 2, numericInput("A1", "A1", min = 1, max = 10, value = 3),
numericInput("C1", "C1", min = 1, max = 10, value = 3)))
)
server <- function(input, output, session) {
}
shinyApp(ui, server)
For this particular case, something like the following works
library(shiny)
library(magrittr)
library(purrr)
inputs <- c("A0", "B0", "C0")
ui <- fluidPage(
fluidRow(column(width = 2, inputs %>% map(~numericInput(.x, .x, min = 1, max = 10, value = 3))),
column(width = 2,
numericInput("A1", "A1", min = 1, max = 10, value = 3),
div(style = "height:73.5px"),
numericInput("C1", "C1", min = 1, max = 10, value = 3))
)
)
server <- function(input, output, session) {
}
shinyApp(ui, server)
You can modify the height of the div if you want.
However, in my opinion, a better approach is to use a row wise approach to insert the inputs. Something like the following
fluidRow(
column(2, numericInput("A0", "A0", min = 1, max = 10, value = 3)),
column(2, numericInput("A1", "A1", min = 1, max = 10, value = 3))
),
fluidRow(
column(2, numericInput("B0", "B0", min = 1, max = 10, value = 3))
),
fluidRow(
column(2, numericInput("C0", "C0", min = 1, max = 10, value = 3)),
column(2, numericInput("C1", "C1", min = 1, max = 10, value = 3))
)
but as you can see it is not compatible with the map() function as being used now.
I have the following piece of code:
library(shiny)
library(shinydashboard)
library(shinydashboardPlus)
library(shinyjs)
library(shinycssloaders)
library(plotly)
library(treemap)
library(viridisLite)
library(highcharter)
library(V8)
library(dplyr)
library(RColorBrewer)
library(openxlsx)
library(quantmod)
library(shinyWidgets)
library(caret)
library(lubridate)
library(tidyr)
library(flexdashboard)
xheader<-dashboardHeaderPlus( title = "ABC")
xsidebar<- dashboardSidebar(
sidebarUserPanel("USER",
subtitle = "Test",
image = "XXX"
),
sidebarMenu(id = "left_sidebar",
menuItem("DashBoard",
tabName = "ID_DashBoard",
icon = icon("desktop"))
)
)
x1body<-dashboardBody(
tabItems(
tabItem(tabName = "ID_DashBoard",
fluidRow(
column(width = 4,
fluidRow(style = "height:1000px; background-color: #E8E9EC;font-weight:bold",
column(width = 4, style = "color:green;font-weight:bold;",
gaugeOutput("gauge1", width = "100px", height = "200px")
),
column(width = 4,
gaugeOutput("gauge2", width = "100px", height = "200px")
),
column(width = 4,
gaugeOutput("gauge3", width = "100px", height = "200px")
)
)
),
column(width = 4,
fluidRow("MIDDLE", style = "height:1000px; background-color: #F2F3F5;")
),
column(width = 4,
fluidRow("RIGHT", style = "height:1000px; background-color: #E8E9EC;")
)
)
)
)
)
ui<- dashboardPagePlus(
shinyjs::useShinyjs(),
header = xheader,
sidebar = xsidebar,
body = x1body,
skin = "black",
sidebar_background = "light",
collapse_sidebar = TRUE
)
server <- function(input,output,session){
output$gauge1 = renderGauge({
gauge(0.5,
min = 0,
max = 1,
sectors = gaugeSectors(success = c(0.5, 1),
warning = c(0.3, 0.5),
danger = c(0, 0.3)),
label = "Total Commits"
)
})
output$gauge2 = renderGauge({
gauge(0.1,
min = 0,
max = 1,
sectors = gaugeSectors(success = c(0.5, 1),
warning = c(0.3, 0.5),
danger = c(0, 0.3)),
label = "Total Executed"
)
})
output$gauge3 = renderGauge({
gauge(0.1,
min = 0,
max = 1,
sectors = gaugeSectors(success = c(0.5, 1),
warning = c(0.3, 0.5),
danger = c(0, 0.3)),
label = "Total Executed"
)
})
}
shinyApp(ui, server)
The GUI looks like this currently:
as you can see in the image, the labels are not displayed properly on the grey background. I want to change the font size/color/make it bold for the following texts - Total Commits, Total Executed, Total Executed on the Gauge display and the min/max numbers on the gauge. I am unable to figure it out through CSS styling. Can someone please help me? Thanks in advance!
As per object, I get a very small plot in Firefox when using sankeyNetwork() from networkd3 in shiny but not in Chrome or RStudio.
I have not included any CSS or JS in the script - the code below produces this result for me.
Is there any CSS property I have missed?
I am using R 3.4.1, shiny 1.1.0, networkD3 0.4 and Firefox 52.9.0.
Firefox:
Chrome:
library(shiny)
library(magrittr)
library(shinydashboard)
library(networkD3)
labels = as.character(1:9)
ui <- tagList(
dashboardPage(
dashboardHeader(
title = "appName"
),
##### dasboardSidebar #####
dashboardSidebar(
sidebarMenu(
id = "sidebar",
menuItem("plots",
tabName = "sPlots")
)
),
##### dashboardBody #####
dashboardBody(
tabItems(
##### tab #####
tabItem(
tabName = "sPlots",
tabsetPanel(
tabPanel(
"Sankey plot",
fluidRow(
box(title = "title",
solidHeader = TRUE, collapsible = TRUE, status = "primary",
sankeyNetworkOutput("sankeyHSM1")
)
)
)
)
)
)
)
)
)
server <- function(input, output, session) {
HSM = matrix(rep(c(10000, 700, 10000-700, 200, 500, 50, 20, 10, 2,40,10,10,10,10),4),ncol = 4)
sankeyHSMNetworkFun = function(x,ndx) {
nodes = data.frame("name" = factor(labels, levels = labels),
"group" = as.character(c(1,2,2,3,3,4,4,4,4)))
links = as.data.frame(matrix(byrow=T,ncol=3,c(
0, 1, NA,
0, 2, NA,
1, 3, NA,
1, 4, NA,
3, 5, NA,
3, 6, NA,
3, 7, NA,
3, 8, NA
)))
links[,3] = HSM[2:(nrow(links)+1),] %>% {rowSums(.[,(ndx-1)*2+c(1,2)])}
names(links) = c("source","target","value")
sankeyNetwork(Links = links, Nodes = nodes, Source = "source", Target = "target", Value = "value", NodeID = "name",NodeGroup = "group",
fontSize=12,sinksRight = FALSE)
}
output$sankeyHSM1 = renderSankeyNetwork({
sankeyHSMNetworkFun(values$HSM,1)
})
}
# Run the application
shinyApp(ui = ui, server = server)
------------------ EDIT --------------------
Thanks to #CJYetman for indicating onRender() as a possible solution - however this fails when there are two plots generated side by side as in the MRE below (note in addition to the second sankey plot I have also added javascript code to re-draw the figures when the window size changes as the plot does not appear to do it automatically).
library(shiny)
library(magrittr)
library(shinydashboard)
library(networkD3)
library(htmlwidgets)
labels = as.character(1:9)
ui <- tagList(
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);
});
')
),
dashboardPage(
dashboardHeader(
title = "appName"
),
##### dasboardSidebar #####
dashboardSidebar(
sidebarMenu(
id = "sidebar",
menuItem("plots",
tabName = "sPlots")
)
),
##### dashboardBody #####
dashboardBody(
tabItems(
##### tab #####
tabItem(
tabName = "sPlots",
tabsetPanel(
tabPanel(
"Sankey plot",
fluidRow(
box(title = "title",
solidHeader = TRUE, collapsible = TRUE, status = "primary",
sankeyNetworkOutput("sankeyHSM1")
),
box(title = "plot2",
solidHeader = TRUE, collapsible = TRUE, status = "primary",
sankeyNetworkOutput("sankeyHSM2"))
)
)
)
)
)
)
)
)
server <- function(input, output, session) {
HSM = matrix(rep(c(10000, 700, 10000-700, 200, 500, 50, 20, 10, 2,40,10,10,10,10),4),ncol = 4)
sankeyHSMNetworkFun = function(x,ndx) {
nodes = data.frame("name" = factor(labels, levels = labels),
"group" = as.character(c(1,2,2,3,3,4,4,4,4)))
links = as.data.frame(matrix(byrow=T,ncol=3,c(
0, 1, NA,
0, 2, NA,
1, 3, NA,
1, 4, NA,
3, 5, NA,
3, 6, NA,
3, 7, NA,
3, 8, NA
)))
links[,3] = HSM[2:(nrow(links)+1),] %>% {rowSums(.[,(ndx-1)*2+c(1,2)])}
names(links) = c("source","target","value")
sankeyNetwork(Links = links, Nodes = nodes, Source = "source", Target = "target", Value = "value", NodeID = "name",NodeGroup = "group",
fontSize=12,sinksRight = FALSE)
}
output$sankeyHSM1 = renderSankeyNetwork({
req(input$dimension)
sankeyHSMNetworkFun(values$HSM,1) %>%
onRender('document.getElementsByTagName("svg")[0].setAttribute("viewBox", "")')
})
output$sankeyHSM2 = renderSankeyNetwork({
req(input$dimension)
sankeyHSMNetworkFun(values$HSM,2) %>%
onRender('document.getElementsByTagName("svg")[0].setAttribute("viewBox", "")')
})
}
# Run the application
shinyApp(ui = ui, server = server)
------------------ EDIT2 --------------------
Second problem above solved - either by referring to the second svg item on the page as per #CJYetman's comment below using document.getElementsByTagName("svg")[1].setAttribute("viewBox",""), or by going into the objects themselves selecting their first svg element with document.getElementById("sankeyHSM2").getElementsByTagName("svg")[0].setAttribute("viewBox","").
This seems to be the result of Firefox reacting to the viewbox svg property differently than other browsers. It might be worthwhile to submit this as an issue here https://github.com/christophergandrud/networkD3/issues
In the meantime, you could work around this by resetting the viewbox attribute using some JavaScript and htmlwidgets::onRender(). Here's an example using a minimized version of your example. (Resetting the viewbox attribute may have other consequences)
library(htmlwidgets)
library(networkD3)
library(magrittr)
nodes = data.frame("name" = factor(as.character(1:9)),
"group" = as.character(c(1,2,2,3,3,4,4,4,4)))
links = as.data.frame(matrix(byrow = T, ncol = 3, c(
0, 1, 1400,
0, 2, 18600,
1, 3, 400,
1, 4, 1000,
3, 5, 100,
3, 6, 40,
3, 7, 20,
3, 8, 4
)))
names(links) = c("source","target","value")
sn <- sankeyNetwork(Links = links, Nodes = nodes, Source = "source",
Target = "target", Value = "value", NodeID = "name",
NodeGroup = "group", fontSize = 12, sinksRight = FALSE)
htmlwidgets::onRender(sn, 'document.getElementsByTagName("svg")[0].setAttribute("viewBox", "")')
UPDATE (2019.10.26)
This is probably a safer implementation of removing the viewBox...
htmlwidgets::onRender(sn, 'function(el) { el.getElementsByTagName("svg")[0].removeAttribute("viewBox") }')
UPDATE 2020.04.02
My currently preferred method to do this is to use htmlwidgets::onRender to target specifically the SVG contained by the passed htmlwidget, like this...
onRender(sn, 'function(el) { el.querySelector("svg").removeAttribute("viewBox") }')
That can then be done specifically to as many htmlwidgets on your page as necessary, for instance...
onRender(sn, 'function(el) { el.querySelector("svg").removeAttribute("viewBox") }')
onRender(sn2, 'function(el) { el.querySelector("svg").removeAttribute("viewBox") }')
Within shiny how would I go about updating values within a DT table without it repainting the entire table and thus flickering on each update.
The following example compares both the standard tableOutput with DT::dataTableOutput.
Note the flickering on each update of dataTableOutput.
Is there away to avoid this and have a smoother user interaction? ui.R and server.R example below.
require(shiny);require(DT)
shinyUI(fluidPage(
titlePanel("Sliders"),
sidebarLayout(
sidebarPanel(
sliderInput(
"integer", "Integer:",
min = 0, max = 1000, value = 500
),
sliderInput(
"decimal", "Decimal:",
min = 0, max = 1, value = 0.5, step = 0.1
),
sliderInput(
"range", "Range:",
min = 1, max = 1000, value = c(200,500)
),
sliderInput(
"format", "Custom Format:",
min = 0, max = 10000, value = 0, step = 2500,
pre = "$", sep = ",", animate = TRUE
),
sliderInput(
"animation", "Looping Animation:", 1, 2000, 1,
step = 10, animate =
animationOptions(
interval = 300, loop = TRUE,
playButton = "PLAY", pauseButton = "PAUSE"
)
)
),
mainPanel(tableOutput("values"),
DT::dataTableOutput('DTtable'))
)
))
shinyServer(function(input, output) {
sliderValues <- reactive({
data.frame(
Name = c("Integer",
"Decimal",
"Range",
"Custom Format",
"Animation"),
Value = as.character(
c(
input$integer,
input$decimal,
paste(input$range, collapse = ' '),
input$format,
input$animation
)
),
stringsAsFactors = FALSE
)
})
output$values <- renderTable({
sliderValues()
})
output$DTtable = DT::renderDataTable(rownames = FALSE,
{
sliderValues()
},
options = list(processing = FALSE))
})
It looks like the ideal solution would be to implement the reload functionality:
https://datatables.net/reference/api/ajax.reload()
Any advice on how to do this?