2

I would like to download PowerPoint slides from Shiny with the package officer. I made an PowerPoint example that contains a plot. How to update the slide if you change the inputs of the plot? Because when I changed the inputs, it didn't update the plot but it added a new slide with the modified plot. That's not what I want. I want to update the plot based on the inputs. How to achieve that? Here is a reproducible example:

  • Import libraries and define useful functions
# Import packages ---------------------------------------------------------

library(shiny)
library(tidyr)
library(dplyr)
library(ggplot2)
library(officer)

# Useful functions --------------------------------------------------------

IsNumeric <- function(x){return(is.numeric(x) == TRUE)}
IsNotNumeric <- function(x){return(is.numeric(x) == FALSE)}
  • Define the user interface
# Define user interface ---------------------------------------------------

ui <- fluidPage(
    titlePanel("Dataset analysis"),

    sidebarLayout(
        sidebarPanel(

            # Select categorical variables:
            selectInput(inputId = "CatVar"
                        , label = "Select categorical variables:"
                        , choices = diamonds %>% select_if(IsNotNumeric) %>% colnames()
                        , multiple = TRUE
                        ),

            selectInput(inputId = "NumVar"
                        , label = "Select categorical variables:"
                        , choices = diamonds %>% select_if(IsNumeric) %>% colnames()
                        , multiple = FALSE
                        )
                    ),

       mainPanel(

           plotOutput(outputId = "plot_id"),

           downloadButton(outputId = "pptx_id"
                          , label = "Download analysis to PowerPoint"
           )

       )
    )

)
  • Define the server function
mypptx <- read_pptx()

server <- function(session, input, output){

    selectCatVar <- reactive({

        validate(

            need(is.null(input$CatVar) == FALSE, "Please select at least one categorical variable.")
        )

        input$CatVar

    })

    # selectNumVar <- reactive({input$NumVar})

    myplot <- reactive({

        dat <- diamonds %>% select(selectCatVar(), input$NumVar) %>% 
            gather(MyVar, MyValue, -input$NumVar)

        ggplot(data = dat, mapping = aes(x = MyValue, y = !!sym(input$NumVar), fill = MyValue)) +
            geom_boxplot() +
            facet_wrap(MyVar ~ ., scales = "free_x") +
            labs(y = input$NumVar) +
            theme(legend.position = "none"
            )


    })


    output$plot_id <- renderPlot({

        myplot()

    })

    output$pptx_id <- downloadHandler(
        filename = function(){"test_pptx.pptx"},
        content = function(file){

            mypptx  %>% add_slide(layout = "Title and Content", master = "Office Theme") %>%
                            ph_with(value = myplot(), location = ph_location_type(type = "body")) %>%
                            print(target = file)
        }
    )

}
  • Run the application
shinyApp(ui = ui, server = server)
Quynh-Mai Chu
  • 165
  • 1
  • 9
  • thanks for the nice reprex. I don't really understand, when I change inputs, the plot is updated and if I click on download pptx, I get the exact same plot in a single slide powerpoint file. – David Gohel Apr 01 '20 at 10:41
  • I understand your point. I think I didn't explain in details how I get this "error". First, I run the app. Secondly, I choose the inputs. Thirdly, I download the powerpoint. Fourthly, I changed the inputs WITHOUT closing the app. Finally, I download the powerpoint. From that, I get a new slide with the modified plot. From the explanation, do you end up with the same problem? – Quynh-Mai Chu Apr 01 '20 at 10:52
  • I reproduced the example but instead of downloading a powerpoint, I download a CSV. It works well. So, I don't think it's a Shiny problem. – Quynh-Mai Chu Apr 01 '20 at 10:59
  • No sorry. Fourthly, you got a new slide with the modified plot. And what were you expecting? – David Gohel Apr 01 '20 at 11:11
  • I expect to have one slide with the modified plot instead of 2 slides. Do you understand better? Maybe I can send you an example what I expect to have. – Quynh-Mai Chu Apr 01 '20 at 11:53
  • Yes now, thanks for taking time to explain me. That sounds crazy. With your code on my computer, I get the correct behavior, your code is exactly what it should be - I do the same for my customers or colleagues! It looks like `read_pptx()` is pointing to an existing object - as if memoise was around that. – David Gohel Apr 01 '20 at 12:26
  • I redo the example and now, it works. I don't understand. I gave here an simplified example. From my main development, I still have duplicated slides. Very strange the behaviour... Thanks for having a look. – Quynh-Mai Chu Apr 01 '20 at 13:58
  • ok, have a look at `read_pptx` and make sure it is always new when `downloadHandler` is called. That may be because the object is in a reactive value that does not change so slides are added sequentially to this object. – David Gohel Apr 01 '20 at 14:07
  • @DavidGohel: I think I found the problem. Basically, I stored `read_pptx()` in a variable, which is `mypptx <- read_pptx()`. Like that, the slides are added sequentially. Due to the fact I used a PowerPoint template, I have to explicitly write, `read_pptx("template.pptx")` instead of storing in a variable. Like that, it works but it is not elegant. I don't understand how `downloadHandler` keeps the content of a variable in memory. – Quynh-Mai Chu Apr 01 '20 at 15:14
  • By the way, I updated the post so that the "error" can be reproduced. – Quynh-Mai Chu Apr 01 '20 at 15:17

2 Answers2

0

I found the solution. Do not store read_pptx() in a variable. So, the change will be the following:

read_pptx() %>% add_slide(layout = "Title and Content", master = "Office Theme") %>%
                            ph_with(value = myplot(), location = ph_location_type(type = "body")) %>%
                            print(target = file)
Quynh-Mai Chu
  • 165
  • 1
  • 9
0
####  Library  ####
install.packages("flextable")
install.packages("rvg")
library(flextable)
library(rvg)
library(officer)
library(ggplot2)
library(magrittr)

# Creat a path
path_out <- "."

#### prep ggplot ####
p1 <- iris %>% 
  ggplot() +
  geom_point(aes(Sepal.Length,Petal.Length,color = Species), size = 3) +
  theme_minimal()

#### prep editable graph (rvg) ####
p2 <- dml(ggobj = P1)

#### Producing ppt file ####
my_pres <- read_pptx() %>%
  #slide 1 # for dummy graph
  add_slide(layout = "Title and Content", master = "Office Theme") %>%
  ph_with(value = p1, location = ph_location("body", left = 1, top = 1, width = 5, height = 5)) %>%
  #slide 2 # for Editable graph / vector graph
  add_slide() %>%
  ph_with(value = p2, location = ph_location("body", left = 1, top = 1, width = 5, height = 5)) %>%
  print(target = file.path(path_out,"example_v1.pptx"))