【问题标题】:Delay downloadHandler until plots are generated延迟 downloadHandler 直到生成图
【发布时间】:2021-11-25 05:45:13
【问题描述】:

我想使用 Shiny 应用程序中各个选项卡中的图表生成 R Markdown 报告。
只有在用户激活选项卡后,才会生成每个选项卡中的图。如果之前未激活选项卡,则该图将不存在,因此将无法用于报告。
因此,我尝试在调用 downloadHandler 之前使用 updateTabsetPanel 连续打开所有选项卡。然而,与手动单击选项卡相反,空选项卡会打开,并且文件下载对话框会在绘图开始渲染之前立即出现。只有保存报告文件后,Shiny 才会开始渲染绘图,报告为空。
有没有办法强制 downloadHandler 等到所有选项卡中的所有绘图完成渲染?

这是一个例子:

app.R

library(shiny)
library(ggplot2)

ui <- fluidPage(
  mainPanel(
    tabsetPanel(id = "tabs",
                tabPanel("Plot1",
                         downloadButton("report", "create report"),
                         plotOutput("plot1")
                ),
                tabPanel("Plot2", plotOutput("plot2")),
                tabPanel("Plot3", plotOutput("plot3"))     
    )
  )
)
server <- function(input, output, session) {
  plots <<- list()
  
  output$plot1 <- renderPlot({
    plots[[1]] <<- ggplot(mtcars, aes(wt, mpg)) + geom_point()
    plot(plots[[1]])
  })  
  
  output$plot2 <- renderPlot({
    plots[[2]] <<- ggplot(mtcars, aes(wt, cyl)) + geom_point()
    plot(plots[[2]])
  })  
  
  output$plot3 <- renderPlot({
    plots[[3]] <<- ggplot(mtcars, aes(wt, disp)) + geom_point()
    plot(plots[[3]])
  })  
  
  
  output$report <- downloadHandler(
    filename = ("report.html"),
    content = function(file) {
      updateTabsetPanel(session, "tabs", selected = "Plot1")
      updateTabsetPanel(session, "tabs", selected = "Plot2")
      updateTabsetPanel(session, "tabs", selected = "Plot3")
      tempReport <- file.path(tempdir(), "report.rmd")
      file.copy("report.rmd", tempReport, overwrite = TRUE)
      params <- list(plots = plots)
      rmarkdown::render(tempReport, output_file = file, params = params, envir = new.env(parent = globalenv()))
    }
  )  
}

shinyApp(ui, server)

report.rmd

---
title: "report"
output: html_document
params:
  plots: NA
---

```{r}
  for (i in params$plots)
    plot(i)
```

在这种情况下,为了简单起见,我将 tabPanel 调用放在了 downloadHandler 中。他们应该打开所有选项卡,以便渲染绘图,然后,rmarkdown 可以绘制它们。
如果单击“报告”按钮,将打开第 3 个选项卡,并且在呈现选项卡 2 和 3 的图之前出现文件对话框,因此报告仅包含图 1。但是如果您手动单击所有 3 个选项卡,然后单击“报告”,所有绘图都将被渲染并包含在报告中。

【问题讨论】:

  • 欢迎来到 SO,Houndmux!大多数关于 SO(特别是 r 标签)的问题都受益于或确实需要可重现,以包括示例数据、最少代码和预期输出。请参阅stackoverflow.com/q/5963269minimal reproducible examplestackoverflow.com/tags/r/info,了解如何在这种情况下很好地提出问题。谢谢!
  • 一种方法可能是使用shinyjs 包禁用下载按钮,仅在您需要的条件下启用它。
  • 这仍然需要用户手动打开所有选项卡,以便首先渲染所有绘图。如果标签是自动打开的,我会更喜欢。但是在所有选项卡上连续使用 updateTabsetPanel 甚至不会实际呈现所有选项卡。它只呈现最后一个,即使在所有 updateTabsetPanel 调用之后调用的 downloadHandler 之后也是如此。所以没有给出执行顺序。
  • 好的,我知道这不仅仅是在所有情节完成之前禁用downloadHandler。请制作一个可重复的小例子来清楚地证明这一点。请不要包含与基本流程无关的小部件、控件或代码;我们不需要花哨的情节,只需plot(1);plot(2);plot(3); 就足够了。祝你好运!
  • 添加了一个例子。我将 ggplots 保存在“绘图”列表中而不是在 rmd 文件中重新定义绘图的原因是,在我的实际应用程序中,绘图需要很多不同的数据,我不想转移到 rmd文件。这些也是交互式 ggplotly 对象,用户可以在其中选择图中的数据,我希望这些选择包含在报告中。

标签: r shiny


【解决方案1】:

试试:

  output$plot1 <- renderPlot({
    plots[[1]] <<- ggplot(mtcars, aes(wt, mpg)) + geom_point()
    plot(plots[[1]])
  })  
  outputOptions(output, "plot1", suspendWhenHidden = FALSE)

  output$plot2 <- renderPlot({
    plots[[2]] <<- ggplot(mtcars, aes(wt, cyl)) + geom_point()
    plot(plots[[2]])
  })  
  outputOptions(output, "plot2", suspendWhenHidden = FALSE)
  
  output$plot3 <- renderPlot({
    plots[[3]] <<- ggplot(mtcars, aes(wt, disp)) + geom_point()
    plot(plots[[3]])
  })  
  outputOptions(output, "plot3", suspendWhenHidden = FALSE)

编辑

以上行不通。以下工作。

library(shiny)
library(ggplot2)

ui <- fluidPage(
  mainPanel(
    tabsetPanel(id = "tabs",
                tabPanel("Plot1",
                         downloadButton("report", "create report"),
                         plotOutput("plot1")
                ),
                tabPanel("Plot2", plotOutput("plot2")),
                tabPanel("Plot3", plotOutput("plot3"))     
    )
  )
)
server <- function(input, output, session) {
  plots <- vector("list", length = 3L)
  
  plots[[1L]] <- reactive({
    ggplot(mtcars, aes(wt, mpg)) + geom_point()
  })
  plots[[2L]] <- reactive({
    ggplot(mtcars, aes(wt, cyl)) + geom_point()
  })
  plots[[3L]] <- reactive({
    ggplot(mtcars, aes(wt, disp)) + geom_point()
  })
  
  
  output$plot1 <- renderPlot({
    plots[[1L]]()
  })  
  
  output$plot2 <- renderPlot({
    plots[[2L]]()
  })  
  
  output$plot3 <- renderPlot({
    plots[[3L]]() 
  })  
  
  
  output$report <- downloadHandler(
    filename = "report.html",
    content = function(file) {
      tempReport <- file.path(tempdir(), "report.rmd")
      file.copy("report.rmd", tempReport, overwrite = TRUE)
      params <- list(plots = plots)
      rmarkdown::render(
        tempReport, output_file = file, params = params, 
        envir = new.env(parent = globalenv())
      )
    }
  )  
}

shinyApp(ui, server)

report.Rmd:

---
title: "report"
output: html_document
params:
  plots: NA
---

```{r}
for(plot in params$plots)
  print(plot())
```

【讨论】:

  • 不幸的是,这也不起作用。文件下载对话框仍然会在处理任何绘图之前立即打开,并且在对话框打开时不会在后台处理绘图。
  • @Houndmux 好的。您想要使用 3 个地块的东西,还是打算做一个具有任意数量地块的应用程序?
  • 我需要任意数量的图,所以在我的应用程序中,它们是使用 tagList 动态创建的。我需要的是一种让所有选项卡中的所有绘图都可以在 R 降价报告中使用的方法,即使用户之前没有打开这些选项卡。如果有比在下载对话框出现之前强制 Shiny 打开所有选项卡更好的方法,我也会尝试。
  • @Houndmux 请看我的编辑。
  • 非常感谢,成功了!您是否有理由将数组索引明确指定为整数?没有它也可以工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多