I'm desperately trying to change the default colors of the rpivotTable package. I have also posted an issue in the github of the creator of the package, but no one has answered yet, so if someone has an idea how to fix this problem, I'd be more than grateful.
My main problem is changing the blue colors in the rectangles below the variable choice: Example
With this function that I have found on the internet, I manage to change the whole background, but not the specific thing I want (outside of Shiny only so far):
style_widget <- function(hw=NULL, style="", addl_selector="") {
stopifnot(!is.null(hw), inherits(hw, "htmlwidget"))
# use current id of htmlwidget if already specified
elementId <- hw$elementId
if(is.null(elementId)) {
# borrow htmlwidgets unique id creator
elementId <- sprintf(
'htmlwidget-%s',
htmlwidgets:::createWidgetId()
)
hw$elementId <- elementId
}
htmlwidgets::prependContent(
hw,
htmltools::tags$style(
sprintf(
"#%s %s {%s}",
elementId,
addl_selector,
style
)
)
)
}
pivot_graph<-rpivotTable(mtcars)
browsable(
tagList(
style_widget(hw=pivot_graph, "background-color: rgb(245, 245, 245);", "table td")
)
)
However, when I try to do it Shiny, I can't figure out what to put where and how to do it (or even if it's at all possible with this function). Any help is appreciated. My Shiny code so far:
library(shiny)
library(rpivotTable)
library(rvest)
ui <- fluidPage(
titlePanel("Cars"),
sidebarLayout(
sidebarPanel(
fileInput('file1', 'Choose CSV File',
accept=c('text/csv','text/comma-separated-values,text/plain','.csv')),
actionButton("save1","Save Table 1")
# actionButton("save2","Save Table 2")
),
mainPanel(
tabsetPanel(
tabPanel("Pivot Table 1",
rpivotTableOutput("table")),
tabPanel("Pivot Table 2",
rpivotTableOutput("table2"))
)
)
)
)
server <- function(input, output,session)
{
session$onSessionEnded(stopApp)
observe({
file1 = input$file1
if (is.null(file1)) {
return(NULL)
}
st_data <<- read.csv(file1$datapath)
output$table <- renderRpivotTable({
rpivotTable(mtcars,
rendererName="Table",
onRefresh = htmlwidgets::JS("function(config) {Shiny.onInputChange('myData',
document.getElementById('table').innerHTML); }")
)
})
output$table2 <- renderRpivotTable({
rpivotTable(mtcars,aggregatorName="Average",
rendererName="Table",
onRefresh = htmlwidgets::JS("function(config) {Shiny.onInputChange('myData',
document.getElementById('table').innerHTML); }")
)
})
summarydf <- eventReactive(input$myData,{
input$myData %>%
read_html %>%
html_table(fill = TRUE) %>%
.[[2]]
})
observeEvent(input$save1, {
if(nrow(summarydf() )<1) return()
write.csv(summarydf(), file="./cars1.csv")
})
# summarydf1 <- eventReactive(input$myData1,{
# input$myData1 %>%
# read_html %>%
# html_table(fill = TRUE) %>%
# .[[4]]
# })
#
# observeEvent(input$save2, {
# if(nrow(summarydf1() )<1) return()
# write.csv(summarydf1(), file="./cars2.csv")
# })
})
}
shinyApp(ui = ui, server = server)
The following code will change the light blue to a very deep blue.
You can change any pivot.min.css code in a similar way: the only challenge is to identify which is the right element!
To change the colour, search for a JavaScript colour map and change #000080 to whatever you need.
library(shiny)
df <- iris
ui <- fluidPage(
tags$style(type="text/css",".pvtRows, .pvtCols { background: #000080 none repeat scroll 0 0; }" ),
fluidRow(
column(width=10, rpivotTableOutput("pivot"))
)
)
server <- function(input, output, session) {
output$pivot<-renderRpivotTable({
rpivotTable(df,
rendererName="Heatmap",
cols=c("Species"),
rows=c("Petal.Width"),
aggregatorName="Count"
)
})
}
shinyApp(ui = ui, server = server)
Please let me know if this is addressing your requirement.
Related
I'm hoping to insert an rclipboard::rclipButton() into a DataTable in RShiny and am having trouble figuring out how to do it. Have tried the following (based on: Using renderDataTable within renderUi in Shiny):
library(shiny); library(tidyverse); library(rclipboard)
ui <- fluidPage(
mainPanel(
rclipboardSetup(),
uiOutput('myTable')
)
)
server <- function(input, output) {
output$myTable <- renderUI({
output$myTable <- renderUI({
iris <- iris %>% filter(row_number()==1:2)
iris$button <- rclipButton(
inputId = "clipbtn",
label = "Copy",
clipText = "test",
icon = icon("clipboard")
)
output$aa <- renderDataTable(iris)
dataTableOutput("aa")
})
})
}
shinyApp(ui, server)
But looks like this:
"[object Object]"
Have also tried paste0()'ing the rclipButton() into the DataTable but that just renders as a long string of HTML.
Any suggestions much appreciated!
Well, rclipButton() call will generate shiny.tag objects, and you need to change it to string so DT can parse it. Then the key is to use escape = F in datatable.
I also rewrite the way to generate the DT table.
library(shiny); library(tidyverse); library(rclipboard)
ui <- fluidPage(
mainPanel(
rclipboardSetup(),
DT::dataTableOutput("aa")
)
)
server <- function(input, output) {
output$aa <- DT::renderDataTable({
iris2 <- iris %>% filter(row_number()==1:2)
iris2$button <- rclipButton(
inputId = "clipbtn",
label = "Copy",
clipText = "test",
icon = icon("clipboard")
) %>% as.character()
DT::datatable(iris2, escape = F)
})
}
shinyApp(ui, server)
I am relatively new to shiny and trying to add a download button to download the table in the app. I am using the following code:
ui <- fluidPage(
theme = shinytheme("cerulean"),
downloadButton("downloadData", "Download"),
titlePanel("Title"),
selectInput("state", "Select State", unique(ma_pdp$state)),
selectInput('year', "Select Year", unique(ma_pdp$year)),
tabPanel("Table", DT::DTOutput('table')))
#Server
server <- function(input, output) {
output$table <- DT::renderDT({
ma_pdp %>%
filter(year == input$year) %>%
filter(state == input$state)
output$downloadData <- downloadHandler(
filename = function() {
paste(input$ma_pdp, ".csv", sep = "")
},
content = function(file){
write.csv(datasetInput, file, row.names=FALSE)
}
)
})
}
Am I not referencing the function correctly? Or is something else wrong?
Assuming your intention is to create a CSV file containing the data currently displayed in output$table, this will - I think - give you what you want.
library(shiny)
library(tidyverse)
ui <- fluidPage(
downloadButton("downloadData", "Download"),
titlePanel("Title"),
selectInput("state", "Select State", unique(mtcars$cyl)),
selectInput('year', "Select Year", unique(mtcars$am)),
tabPanel("Table", DT::DTOutput('table'))
)
server <- function(input, output) {
filteredData <- reactive({
mtcars %>% filter(am == input$year, cyl == input$state)
})
output$table <- DT::renderDT({
filteredData()
})
output$downloadData <- downloadHandler(
filename = "test.csv",
content = function(file){
write.csv(filteredData(), file, row.names=FALSE)
}
)
}
shinyApp(ui = ui, server = server)
In addition to the observation I made in my comment, I have moved the filtering you do in the renderTable function to a separate reactive. This ensures that the data saved by the download handler is consistent with the data displayed in the table without the need for any code duplication.
Please read the readily available advice on how to ask a good question and provide a minimal reproducible example. This post may help. In the absence of test data, I've used the mtcars dataset and made appropriate changes in the code.
I'm making an app in which the user can create as many tables as he/she wants and display the code necessary to remake each individual table using shinymeta. I can generate the code for each of these tables but I have a problem when I want to create a complete modal that shows every code for each table.
To be clearer, here's a reproducible example:
library(shiny)
library(dplyr)
library(shinymeta)
module_ui <- function(id){
ns <- NS(id)
tagList(
fluidRow(
actionButton(ns("show_table"), "Show table"),
actionButton(ns("show_code"), "Show code"),
tableOutput(ns("table"))
)
)
}
module_server <- function(input, output, session){
data <- metaReactive2({
req(input$show_table)
isolate(metaExpr({
mtcars
}))
})
data2 <- metaReactive({
..(data()) %>%
select(mpg)
})
output$table <- renderTable({
data2()
})
observeEvent(input$show_code, {
showModal(modalDialog(
renderPrint({
expandChain(data(), data2())
})
))
})
return(data())
}
ui <- fluidPage(
actionButton("launch", "Launch"),
actionButton("show_full_code", "Show the full code (at least 2 'launch' before)")
)
server <- function(input, output, session) {
count <- reactiveValues(value = 0)
observeEvent(input$launch, {
count$value <- count$value + 1
insertUI(selector = "#show_full_code",
where = "afterEnd",
ui = module_ui(paste0("x", count$value)))
callModule(module_server, paste0("x", count$value))
})
#### "Merge" the single code modals in one big
observeEvent(input$show_full_code, {
showModal(modalDialog(
renderPrint({
expandChain(x1_data)
})
))
})
}
shinyApp(ui, server)
When you click on "Launch", two buttons are generated and you can display a table ("Show table") and the code to remake this table ("Show code"). You can click on "Launch" indefinitely and the table will be named x1_data, x2_data, etc.
However, when I try to generate the code that unites every individual code (by clicking on "Show the full code"), x1_data is not found. Using x1_data() does not work either. I'm not a fan of asking two questions in one post but I will do this now:
How can I access the reactive elements created inside modules?
How can I "merge" every individual code in a big one?
Also asked on RStudio Community
Edit: following a comment, I add a second reactive expression in my example, so that I can't use return on both of them.
Ok, I came up with an answer that has the module return the expandChain() results rather than trying to render them again in the server:
library(shiny)
library(dplyr)
library(shinymeta)
module_ui <- function(id){
ns <- NS(id)
tagList(
fluidRow(
actionButton(ns("show_table"), "Show table"),
actionButton(ns("show_code"), "Show code"),
tableOutput(ns("table"))
)
)
}
module_server <- function(input, output, session){
data <- metaReactive2({
req(input$show_table)
isolate(metaExpr({
mtcars
}))
})
data2 <- metaReactive({
..(data()) %>%
select(mpg)
})
output$table <- renderTable({
data2()
})
observeEvent(input$show_code, {
showModal(modalDialog(
renderPrint({
expandChain(data(), data2())
})
))
})
########################################
### create list of reactive objects ####
########################################
return(list(
expandChain(data(), data2())
)
)
}
ui <- fluidPage(
actionButton("launch", "Launch"),
actionButton("show_full_code", "Show the full code (at least 2 'launch' before)")
)
server <- function(input, output, session) {
count <- reactiveValues(value = 0)
observeEvent(input$launch, {
count$value <- count$value + 1
insertUI(selector = "#show_full_code",
where = "afterEnd",
ui = module_ui(paste0("x", count$value)))
callModule(module_server, paste0("x", count$value))
})
#### "Merge" the single code modals in one big list object
my_data <- reactive({
req(count$value)
my_set <- 1:count$value
### lapply through the different name spaces so all are captured ###
final <- lapply(my_set, function(x){
temp <- callModule(module_server, paste0("x", x))
return(temp)
})
return(final)
})
#### "Merge" the single code modals in one big
observeEvent(input$show_full_code, {
showModal(modalDialog(
renderPrint({
temp <- sapply(unlist(my_data()), function(x){
print(x)
})
})
))
})
}
shinyApp(ui, server)
I have asked this question in the RStudio community and didn't get help, so I try it here:
I am trying to add the following functionality to my app: When the user inserts a plot, a remove button should appear that specifically removes the plot that was inserted at the same time. The app is based on insertUI and removeUI.
This would be the example app.
library(shiny)
library(shinydashboard)
# Example data
a<-(letters)
b<-rnorm(length(letters), 4,2)
c<-rnorm(length(letters), 10,15)
d<-c(1:10,20:30,45:49)
data<-data.frame(a,b,c,d)
names(data)<-c("name","v1","v2","v3")
# UI
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(
actionButton("add", "Add"),
radioButtons("add_elements","", c("Element1", "Element2"))
),
dashboardBody(
fluidRow( tags$div(id="placeholder")
)
))
# Server Logic
server <- function(input, output, session) {
# Initialize empty vector
inserted<- c()
# Observer
observeEvent(input$add, {
id_add <- paste0(input$add, input$add_elements)
insertUI(selector = '#placeholder', where = "afterEnd",
ui= switch(input$add_elements,
'Element1'= plotOutput(id_add),
'Element2' = plotOutput(id_add))
)
output[[id_add]] <-
if (input$add_elements == "Element1")
renderPlot({
plot(data[,1],data[,2])
}) else if (input$add_elements == "Element2")
renderPlot({
plot(data[,1],data[,4])
})
inserted <<- c(id_add,inserted)
insertUI(
selector = "#placeholder",
where = "afterEnd",
ui = tags$div(actionButton("remove_button", "Remove"))
)})
## Remove Elements ###
observeEvent(input$remove_button, {
removeUI(
selector = paste0('#', inserted[length(inserted)])
)
inserted <<- inserted[-length(inserted)]
})
}
shinyApp(ui = ui, server = server)
When a plot is inserted, it gets an ID, such as 1Element1 or 2Element2. I am now wondering how could a remove button only refer to a plot with this exact ID?
So far, I have worked with a single remove button that removes the last inserted plot by defining a vector that stores the IDs.
selector = paste0('#', inserted[length(inserted)])
This is not very useful when a user needs to compare many plots. I have a limited understanding in using these selectors and absolutely no idea how could incorporate a remove button for every plot that only removes the respective plot. Any help would be highly appreciated.
Also, this link may help since it shows a similar functionality (that I was obviously not able to implement).
In this kind of situation I always use 'list' with 'reactiveValues' like below:
library(shiny)
library(shinydashboard)
# Example data
a<-(letters)
b<-rnorm(length(letters), 4,2)
c<-rnorm(length(letters), 10,15)
d<-c(1:10,20:30,45:49)
data<-data.frame(a,b,c,d)
names(data)<-c("name","v1","v2","v3")
# UI
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(
actionButton("add", "Add"),
radioButtons("add_elements","", c("Element1", "Element2"))
),
dashboardBody(
uiOutput("myUI")
))
# Server Logic
server <- function(input, output, session) {
alld <- reactiveValues()
alld$ui <- list()
output$myUI <- renderUI({
alld$ui
})
# Observer
observeEvent(input$add, {
id_add <- length(alld$ui)+1
alld$ui[[id_add]] <- list(
plotOutput(paste0("plt",id_add)),
actionButton(paste0("remove_button", id_add), "Remove")
)
if (input$add_elements == "Element1"){
output[[paste0("plt",id_add)]] <- renderPlot(plot(data[,1],data[,2]))
} else {
output[[paste0("plt",id_add)]] <- renderPlot(plot(data[,1],data[,4]))
}
})
## Remove Elements (for all plots) ###
observe({
lapply(seq_len(length(alld$ui)), function(i){
id_add <- i
observeEvent(input[[paste0("remove_button", id_add)]], {
alld$ui[[id_add]][1] <- NULL
})
})
})
}
shinyApp(ui = ui, server = server)
I would like to change the layer of a ggvis plot using a selectInput widget using a dynamic interface. The problem is that when I choose a different layer after creating the plot, it changes but it just disappear really quick. Below is a simplified version of the code to show the problem that omit all the extra dynamic content. I just plot some number of values from a dataset. I added a couple of selectInput widgets to let the user choose what type of plot and when to show the plot. Please note that I need to have all the elements inside of a renderUI.
library(shiny)
library(ggvis)
runApp(list(
ui = shinyUI(
fluidPage(
sidebarLayout(
sidebarPanel( uiOutput("controls") ),
mainPanel( uiOutput("Plot_UI" )
)
)
)
),
server = function(input, output, session) {
dat <- reactive(iris[sample(nrow(iris),input$numbers),])
buildPlot <- function(layer = 'points'){
if (layer=='points'){
dat %>%
ggvis(~Sepal.Width, ~Sepal.Length) %>%
layer_points() %>%
bind_shiny("ggvis1")
} else {
dat %>%
ggvis(~Sepal.Width, ~Sepal.Length) %>%
layer_bars() %>%
bind_shiny("ggvis1")
}
}
output$controls <- renderUI({
div(
sliderInput("numbers", label = "Number of values to plot?", min = 1, max = 150, value = 75),
selectInput('plot_type', 'Plot Type', c("points","bars")),
selectInput("show", 'Show plot?', c('No','Yes'))
)
})
output$Plot_UI <- renderUI({
if (!is.null(input$show) && input$show == 'Yes'){
cat("Plot_UI -> Build plot\n")
buildPlot(input$plot_type)
div(
uiOutput("ggvis_ui"),
ggvisOutput("ggvis1")
)
}
})
}
))
The only way to see the plot again is by selecting to not show the plot and later select show the plot again using the "Show plot" selectInput.
I don't know if this is a bug or I'm doing it incorrectly.
I think the problem is that your trying to render and update the div at the same time.
library(shiny)
library(ggvis)
runApp(list(
ui = shinyUI(
fluidPage(
sidebarLayout(
sidebarPanel( uiOutput("controls") ),
mainPanel( uiOutput("Plot_UI" )
)
)
)
),
server = function(input, output, session) {
dat <- reactive(iris[sample(nrow(iris),input$numbers),])
buildPlot <- function(layer = 'points'){
if (layer=='points'){
dat %>%
ggvis(~Sepal.Width, ~Sepal.Length) %>%
layer_points() %>%
bind_shiny("ggvis1")
} else {
dat %>%
ggvis(~Sepal.Width, ~Sepal.Length) %>%
layer_bars() %>%
bind_shiny("ggvis1")
}
}
output$controls <- renderUI({
div(
sliderInput("numbers", label = "Number of values to plot?", min = 1, max = 150, value = 75),
selectInput('plot_type', 'Plot Type', c("points","bars")),
selectInput("show", 'Show plot?', c('No','Yes'))
)
})
observeEvent(input$show,{
if (!is.null(input$show) && input$show == 'Yes'){
output$Plot_UI <- renderUI({
cat("Plot_UI -> Build plot\n")
div(
uiOutput("ggvis_ui"),
ggvisOutput("ggvis1")
)
})
}
if (!is.null(input$show) && input$show == 'No'){
output$Plot_UI <- renderUI({ div() })
}
})
observe({
if (!is.null(input$show) && input$show == 'Yes'){
invalidateLater(100,session)
renderPlot()
}
})
renderPlot <- function(){
if(is.null(input$plot_type)) return(NULL)
buildPlot(input$plot_type)
}
} #
))