在使用Shiny实现“运行”按钮时遇到问题

时间:2014-08-25 00:21:40

标签: r shiny

我知道这个问题已被多次解决,我已经研究过我发现的那些实例,但还没有找到有效的解决方案。

我正在使用actionButton()作为Run按钮 - 除非单击actionButton,否则目的是不重新运行模拟。我已经使用了observe({})和isolate({}),正如我在这里讨论的那样,但仍有几个问题。我将讨论这些问题,然后包括下面的代码。该应用程序目前处于早期阶段,只有下拉菜单中的第二个模拟部分实现,因此在启动应用程序后,从selectInput()菜单中选择“Dorriefish growth / reproduction tradeoffs”。此时不要担心任何其他粗糙的地方。

当应用程序开始时,运行按钮的行为符合预期,因为移动前两个滑块(S和p)不会导致模拟运行或在单独的图形设备中显示输出(reps.df == 1是需要dev.new()的特殊情况。此时,输入$ runSim(运行按钮)== 0因为尚未点击它。

第一个问题:单击运行按钮时,图形设备打开,模拟按预期运行,然后打开另一个图形设备并再次显示相同的模拟。在第一次模拟显示完成时保持耐心 - 第二次模拟显示将打开。当它们关闭时(通过单击它们的窗口控件),下次按下运行按钮时,第三次显示相同的模拟,然后打开另一个图形设备并显示新的模拟运行。如果关闭这些图形设备,则后续单击运行按钮会按预期运行,打开一个图形设备并每次显示一个独特的模拟运行。为什么前两次点击运行按钮会产生两个图形设备并重复第一次模拟运行三次? (磅头放在桌子上。)

第二个问题:在第一次按下运行按钮之后,输入$ runSim增量,此后S和p滑块的任何移动都会导致新的图形设备打开并显示新的模拟运行。我不希望这种行为 - 我需要模拟仅在单击运行按钮时显示,而不是在其他输入参数更改时自动显示。我想要submitButton()的名义行为,但我在网上看到一个带有observe和isolate的actionButton是更好的做法。对于我的生活,当输入$ runSim大于零时,即在第一次点击之后,我就无法将其与其他滑块隔离。在显示模拟运行后,似乎没有任何明显的方法可以将该actionButton变量的值重置为零,因此在第一次单击该按钮后,无论何时更新任何其他输入变量,该按钮都不再阻止新模拟。 (还有一些人。)

总结:1)为什么前两次点击运行按钮会导致两个图形设备窗口打开,为什么它们会显示第一次模拟运行三次? 2)如何在第一次单击运行按钮后单击运行并显示模拟?

这是我的(非常初步的)代码:

ui.r

# filename: ui.r
# Michael Camann, Humboldt State University, 2014

################################################################################
#
# Define the user interface
#
################################################################################

library(shiny)

shinyUI(
    fluidPage(
        titlePanel("BIOL 330 ecological simulations"),
        sidebarLayout(
            sidebarPanel("",
                helpText(HTML("<h3 style='text-align:center;'>Control Panel</h3>"), align='center'),
                tags$hr(style='height:2px; border-width:0; color:gray; background-color:gray'),

                # choose a simulation from a drop down menu
                selectInput("sim", HTML("<b>Select a simulation to explore:</b>"),
                    # list the simulations available
                    c("No simulation selected (Introduction)" = "none",
                    "Dorriefish growth/reproduction trade-offs" = "dorriefish",
                    "Optimal foraging" = "optfor")
                ),
                tags$hr(style='height:2px; border-width:0; color:gray; background-color:gray'),

                conditionalPanel(condition="input.sim=='dorriefish'",
                    helpText(HTML("<b>Simulation model parameters:</b>")),
                    sliderInput("S.df", label=div(HTML("Specify <em>S</em>, the switch point mass (g) for
                        transition from somatic growth to reproduction:")),
                        min = 1, max = 50, value = 10, step=5),
                    sliderInput("p.df", label=div(HTML("Specify <em>p</em>, the probability of mortality
                    by predation in any given time step:")),
                        min = 0, max = 1, value = 0.12, step=0.01),
                    sliderInput("reps.df", label=div(HTML("Specify the number of full simulations to
                        run:")), min = 1, max = 100, value = 1, step=1)
                ),

                # bottom controls common to all panels
                conditionalPanel(condition="input.sim!='none'",
                    tags$hr(style='height:2px; border-width:0; color:gray; background-color:gray'),
                    fluidRow(column(4, actionButton("runSim", "Run simulation")),
                        column(4, actionButton("saveSim", "Save output file")),
                        column(4, actionButton("printSim", "Save/print plot"))),
                    tags$hr(style='height:2px; border-width:0; color:gray; background-color:gray')
                )
            ),

            mainPanel("",

                # no model selected-- show information page
                conditionalPanel(condition="input.sim=='none'",
                    includeHTML("www/simNoneText.html")
                ),

                tabsetPanel(id="outTabs", type="tabs",
                    tabPanel("Plot", plotOutput("modl.plot")
                    ),
                    tabPanel("Summary"
                    ),
                    tabPanel("R Code"

                    )
                )
            )
        )
    )
)

server.r

# filename: server.r
# Michael Camann, Humboldt State University, 2014

library(shiny)

################################################################################
#
# Dorrie fish reproduction/growth trade-offs
#
################################################################################

# This function implements the Doriefish simulation exercise distributed in Java
# with Cain et al Ecology, 2nd ed.

# Arguments:
#
# S       The switch point in grams for transitioning from growth to reproduction.
#         Default = 10 g.
#
# p       Probability of predation.  Default = 0.12.
#
# show    Logical.  If TRUE (default), print and plot single simulation output. Set
#         to FALSE when running multiple simulations in order to average the number
#         of offspring across multiple simulations, in which case viewing the
#         results of individual simulations isn't necessary.

df.sim <- function(S=10, p=0.12, show=TRUE)
{
     if (S < 10 | S > 50) stop("S must be between 10 and 50 grams.")
     doriefish <- 10                                                  # cohort size
     imass <- 5                                                       # initial mass
     mass <- rep(imass, doriefish)                                    # init vector of dorrie fish mass
     dead <- rep(FALSE, doriefish)                                    # init vector of mortality status
     mhistory <- matrix(dead, nrow=1, byrow=TRUE)                     # init a matrix of mortality history
     offspring.t <- 0                                                 # init offspring count
     total.offspring <- vector()                                      # storage vector

     while(any(dead==FALSE))                                          # continue until all are dead
     {
          repro.t <- mass >= S & !dead                                # who reproduces during this time step?
          offspring.t <- offspring.t + sum(mass[repro.t==TRUE]/5)     # one offspring per 5 g body mass
          total.offspring <- c(total.offspring, offspring.t)          # keep track of offspring each time step
          mass[!dead & !repro.t] <- mass[!dead & !repro.t] + 5        # incr mass by 5 g if not reproducing
          die.t <- sapply(1:length(dead), function(x) runif(1) <= p)  # stochastic selection of some for predation
          dead <- dead | die.t                                        # update the mortality vector
          mhistory <- rbind(mhistory, dead)                           # update the mortality history matrix
     }
     rownames(mhistory) <- c(1:nrow(mhistory))

     rval <- list(S=S, p=p, mass=mass, mhistory=mhistory, offspring.t=total.offspring,
          offspring=total.offspring[length(total.offspring)], show=show)
     class(rval) <- "df.sim"
     return(rval)
}


print.df.sim <- function(sim)                                         # print function for df.sim()
{
     if(sim$show)
     {
          cat("\n\tDoriefish simulation\n\n")
          cat("Switch point from growth to reproduction:", sim$S, "g.\n\n")
          cat("Probability of predation:", sim$p, "per time step.\n\n")
          cat("Time to cohort extinction:", length(sim$offspring.t), "time steps.\n\n")
          cat("Total adult Doriefish biomass:", sum(sim$mass), "g.\n\n")
          cat("Total offspring produced:", sim$offspring, "\n\n")
          plot(sim)
     }
}

plot.df.sim <- function(sim, sstep=FALSE, s=0.75, ...)                 # plot function for df.sim()
{
    opar <- par(no.readonly=TRUE)                                     # save graphics parameters
    txt <- paste("Total offspring:", sim$offspring,
        "     Time to cohort extinction:", length(sim$offspring.t), "time steps.")

    step.through <- function(sim, s=0.5, txt=txt)
    {
        len <- length(sim$offspring.t)
        par(fig=c(0,1,0.1,0.9), xpd=NA)
        plot(sim$offspring.t, type="n", xlab="Time steps", ylab="Cumulative Doriefish offspring",
            xlim=c(1, max(len, length(sim$mass))))
        mtext("Dorriefish living per time step (green = alive, red = dead):", side=3, at=(max(len)/2)+0.5,
            line=4.2)
        for(i in 1:nrow(sim$mhistory))
        {
            z <- rep("green", length(sim$mass))
            z[sim$mhistory[i,]] <- "red"
            points(seq(1,len, length=length(sim$mass)), rep(max(1.18*sim$offspring.t), length(sim$mass)),
                pch=21, col="black", bg=z, cex=2.5)
            lines(sim$offspring.t[1:i], type="h")
            Sys.sleep(s)
        }
        mtext(txt, side=1, at=0, line=5, adj=0)
    }

    if(sstep) step.through(sim, s, txt)
    else
    {
        par(fig=c(0,1,0.1,1), xpd=NA)
        plot(sim$offspring.t, type="h", xlab="Time steps",
          ylab="Cumulative Doriefish offspring", ...)
        mtext(txt, side=1, at=0, line=5, adj=0)
    }

    par(opar)
}


################################################################################
#
# Define the shiny server
#
################################################################################


shinyServer(function(input, output) {

    observe({
        if(input$runSim == 0) return()
        isolate({
            sim <- reactive({
                switch(input$sim,
                       dorriefish = {
                            df.sim(input$S.df, input$p.df, show=FALSE)
                       }    # end case dorriefish
                )   # end switch(input$sim)
            })  # end reactive()

            output$modl.plot <- renderPlot({
                switch(input$sim,
                    dorriefish = {
                        if (input$reps.df == 1)
                        {
                            dev.new()
                            plot(sim(), sstep=TRUE)
                        }
                    },  # end case dorriefish
                )   # end switch(input$sim)
            })  # end renderPlot()

        })  # end isolate()
    })  # end observe()

})  # end shinyServer()

提前感谢您提供的任何建议。我对Shiny很新,显然需要更多时间。

2 个答案:

答案 0 :(得分:3)

我查看了您的代码,并亲自尝试过。

我认为主要问题在于您的ShinyServer方法。 “观察”中“隔离”内的“反应”内的“renderPlot”似乎是一个坏主意。当然,“renderPlot”和“observe”不应该封装在其他反应式表达式中,它们是反应性端点。在这里,您可以清楚地了解如何使用对我有帮助的反应式表达式:http://rstudio.github.io/shiny/tutorial/#reactivity-overview

我尝试重写您的ShinyServer代码以遵循这一理念:

shinyServer(function(input, output) {

        sim <- reactive({
                    switch(input$sim,
                            dorriefish = {
                                df.sim(input$S.df, input$p.df, show=FALSE)
                            }    # end case dorriefish
                    )   # end switch(input$sim)
                })  # end reactive()

        output$modl.plot <- renderPlot({
                    if(input$runSim == 0) return()
                    isolate({
                        switch(input$sim,
                            dorriefish = {
                                if (input$reps.df == 1)
                                {
                                    plot(sim(), sstep=TRUE)
                                }
                            },  # end case dorriefish
                        ) # end switch(input$sim)
                    })  # end isolate
                })  # end renderPlot()
    })  # end shinyServer()

这似乎工作正常。

注意我也删除了

  

dev.new()

我认为不需要在闪亮的情况下打开绘图设备,这就是renderPlot功能所需要的。

作为最后的提示,我建议你看一下有光泽的可视化调试器(以及一般的R),这样你就可以跟踪每一段代码:在RStudio中有一个代码,或者你可以为eclipse安装StatET插件。当看到反应性表达在工作中时,它可以帮助一群人看到被调用的内容。

答案 1 :(得分:1)

我建议actionButton使用observeEvent。最小的例子如下:

server.R文件中,您需要以下内容:

shinyServer(
    function(input, output) {
        observeEvent(input$run, {
            # simulation code
        })
    })

observeEventactionButton中的ui.R触发:

actionButton("run", label = "Run Simulation")

按钮上标有ID run。点击后,服务器端的input$run注册了observeEvent。一旦触发,就会执行observeEvent中的函数(示例中为# simulation code)。

我希望以上是你问题的解决方案2)。它没有解释问题1)但它应该解决它。