仅在用户查看输出时计算闪亮

时间:2017-01-12 20:55:02

标签: r shiny

我遇到了Shiny的问题,我不知道这是一个错误还是我错过了什么。我试图找到一个解决方案,但我似乎无法在线找到对此问题的引用。

我有一个按下按钮时应该运行的功能,但它只在用户按下按钮后查看输出时才会运行。

这有点奇怪,所以我创建了一个最小的例子。我有两个标签。在第一个中,我选择一个数字(平均值),在第二个选项卡中,我绘制了该值的值。该按钮位于第一个选项卡中,但“create.plot”功能仅在转到tab2时才开始运行。为了清楚起见,我创建了这个'loading.div',它在函数运行时出现。

library(shiny)
library(shinyjs)

create.plot <- function(mean)
{
  shinyjs::show('loading.div')
  y <- rep(NA,10)
  for(i in 1:10)
  {
    y[i] <- rnorm(1,mean=mean)
    Sys.sleep(0.5)
  }
  shinyjs::hide('loading.div')

  return(list(y=y, test='Now it runs from tab1'))
}

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

  run.function <- eventReactive(input$run,create.plot(input$mean))

  output$plot.y <- renderPlot({
    plot(run.function()[['y']])
  })

  output$test <- renderText({
    run.function()[['test']]
  })
}

ui <- fluidPage(shinyjs::useShinyjs(),
  hidden(div(id='loading.div',h3('Loading..'))),
  tabsetPanel(

  tabPanel('tab1',
    numericInput(inputId = 'mean',label='Mean', value=1),
    actionButton(inputId = 'run', label='Run')
    #,textOutput('test')   ## Uncommenting this line, it works.
    ),

  tabPanel('tab2',
    plotOutput('plot.y'))
  )
)

shinyApp(server=server,ui=ui)

为了确保问题是用户在tab1时没有查看输出,我创建了一个测试字符串。如果取消注释该行,则问题是“修复”:该功能在用户点击按钮后直接运行。但是,这只能起作用,因为他正在查看tab1中的输出。

这是一个严重的问题,因为我有一个闪亮的仪表板,有很多标签。有带输入的选项卡和带输出的选项卡。我希望在单击按钮时运行该功能,但只有在用户进入“输出”选项卡时才会运行。

这是一个已知问题吗?有解决方法吗?

2 个答案:

答案 0 :(得分:10)

不可见的反应元素被视为“已暂停”,并且在取消暂停之前不会更新其值。默认情况下,只有在屏幕上呈现它们才会发生这种情况。

即使不可见,也可以通过更改其暂停状态来设置要更新的元素。您可以使用outputOptions函数执行此操作。

对于output$test元素,您需要运行

outputOptions(output, "test", suspendWhenHidden = FALSE)

如果您想要更新多个元素,即使不可见,也可以使用

lapply(c("test1", "test2", "test3"),
       function(x) outputOptions(output, x, suspendWhenHidden = FALSE)

但请注意,这意味着所有计算都将立即执行。根据计算的大小,这可能会降低应用程序的性能。例如,如果有一个生成的图并且需要花费一些时间来渲染,并且只有一小部分用户会看到该图,那么在用户请求它以防止放慢速度之前将其保持暂停可能是谨慎的。计算更常用的元素。

编辑

事实证明“由于没有宽度和高度,情节不能提前渲染。”(https://github.com/rstudio/shiny/issues/1409)。所以我们必须做一些诡计才能提前渲染情节:

我们可以选择情节的尺寸,而不是让shiny自动适应它。这意味着如果屏幕放大,情节不会增长。

library(shiny)
library(shinyjs)

create.plot <- function(mean)
{
  print("working.....")
  shinyjs::show('loading.div')
  y <- rep(NA,10)
  for(i in 1:10)
  {
    y[i] <- rnorm(1,mean=mean)
    Sys.sleep(0.5)
  }
  shinyjs::hide('loading.div')

  y
}

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

  run.function <- eventReactive(input$run,create.plot(input$mean))

  output$plot.y <- renderPlot({
      plot(run.function())
    },
    width = 72 * 5,
    height = 72 * 3
  )

  outputOptions(output, "plot.y", suspendWhenHidden = FALSE)
}

ui <- fluidPage(shinyjs::useShinyjs(),
                hidden(div(id='loading.div',h3('Loading..'))),
                tabsetPanel(

                  tabPanel('tab1',
                           numericInput(inputId = 'mean',label='Mean', value=1),
                           actionButton(inputId = 'run', label='Run')
                  ),

                  tabPanel('tab2',
                           plotOutput('plot.y'))
                )
)

shinyApp(server=server,ui=ui)

答案 1 :(得分:2)

Actuall就是它的工作方式。这就是“反应性”的全部意义 - 它通过缓存进行惰性评估(所以你不必这样做)。如果要强制计算某些内容,请将其放在reactiveValue中并使用observeEvent进行计算(这会执行急切),然后使用reactive块显示值。

我在这里修改了你的例子:

library(shiny)
library(shinyjs)

create.plot <- function(mean) {
  shinyjs::show('loading.div')
  y <- rep(NA,10)
  for (i in 1:10) {
    y[i] <- rnorm(1,mean = mean)
    Sys.sleep(0.5)
  }
  shinyjs::hide('loading.div')

  return(list(y = y,test = 'Now it runs from tab1'))
}

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

  rv <- reactiveValues(plotvals=NULL)

  observeEvent(input$run,{rv$plotvals <- create.plot(input$mean) })

  output$plot.y <- renderPlot({
    req(input$run)
    plot(rv$plotvals[['y']])
  })

  output$test <- renderText({
    rv$plotvals[['test']]
  })
}

ui <- fluidPage(shinyjs::useShinyjs(),
  hidden(div(id = 'loading.div',h3('Loading..'))),
  tabsetPanel(

  tabPanel('tab1',
    numericInput(inputId = 'mean',label = 'Mean',value = 1),
    actionButton(inputId = 'run',label = 'Run')
#,textOutput('test')   ## Uncommenting this line, it works.
    ),

  tabPanel('tab2',
    plotOutput('plot.y'))
  )
)

shinyApp(server = server,ui = ui)

产生这个(按下运行按钮后):

enter image description here

然后当该文本消失时,您可以立即在tab2中看到该情节。

enter image description here

为了更好地理解这些东西,以及被动的惰性和observe的热切性质,请参阅Joe Cheng在此处的介绍。值得花时间:

https://www.rstudio.com/resources/webinars/shiny-developer-conference/

事实上,Shiny的反应性正是使它变得如此富有成效的原因,一旦你习惯了它,它就会变得非常不同。