闪亮 - 保持一个对象稍后在服务器中回忆,而不使用`<< -``

时间:2016-11-25 09:41:41

标签: r shiny

我创建了一个闪亮的应用程序,用于将图像分类为几个选项之一。我们的想法是收集每个图像中出现的数据(如果有的话)。

我的应用程序运行良好,但为了让它按照我想要的方式运行,我在一些地方使用了<<-调用来改变全局环境中的值,这样我可以稍后在脚本中再次调用它们。我已尝试使用<-,但输出错误,图像(每次按下按钮时应更新为新图像)保持不变,这意味着image.source变量不是正确更新。全局变量方法的问题在于,所有用户都会更改这些变量,这意味着如果多个用户同时工作,他们会互相干扰关键变量(纠正我)如果我错了)。

我已经在下面的应用中包含了(一个剥离后的版本),但这里是关键部分,我已经使用了<<-

# when a button is pressed:
observeEvent( input$Shearwater, {

    # print some data to the output file
    cat( paste0( image.source, ",Shearwater\n" ), file = output.file, append = T )

    # remove the image we just looked at from the file list
    file.list <<- file.list[ file.list != image.source ]

    # get the next image to work on
    image.source <<- file.list[1]

    # and render that next image
    output$image <- renderUI(
        img( src = image.source, width = 600 )
    )
})

以下是我现在的应用程序的剥离版本,以显示我正在做的事情(请告诉我,如果有更多方法可以最大限度地减少此示例;这在我看来就像就像我可以在不丢失要点的情况下削减它一样):

library( shiny )

# define the inputs and outputs
file.list <- list.files( path = "www", pattern = ".jpg", ignore.case = T )
output.file <- "classifications.txt"

# check for images already classified, and remove them (to avoid duplicating analysis)
if( file.exists( output.file ) && length( readLines( output.file ) ) > 0 ) {
    already.classified <- try( read.csv( output.file,
                                         header = F,
                                         stringsAsFactors = F ),
                               silent = TRUE )
    if( class( already.classified ) != "try-error" ) {
        already.classified <- unique( already.classified[,1] )
        file.list <- file.list[ !file.list %in% already.classified ]
    }
}

# get the first image to display
image.source <- file.list[1]

ui <- shinyUI( fluidPage(

    titlePanel( "Image classification" ),

    # define multiple submit buttons, one for each classification type
    sidebarLayout(
        sidebarPanel(

            actionButton( inputId = "Shearwater", label = "Shearwater" ),

            br(),br(),
            actionButton( inputId = "Penguin", label = "Penguin" ),

            br(),br(),
            actionButton( inputId = "Other.bird", label = "Other.bird" )

        ),

        # Display the image
        mainPanel( uiOutput( 'image' ) )
    )
) )


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

    # display the first image to the user
    output$image <- renderUI(
        img( src = image.source, width = 600 )
    )

    # when a button is pressed:
    observeEvent( input$Shearwater, {

        # print some data to the output file
        cat( paste0( image.source, ",Shearwater\n" ), file = output.file, append = T )

        # remove the image we just looked at from the file list
        file.list <<- file.list[ file.list != image.source ]

        # get the next image to work on
        image.source <<- file.list[1]

        # and render that next image
        output$image <- renderUI(
            img( src = image.source, width = 600 )
        )
    })

    # repeat the above for each button type
    observeEvent( input$Penguin, {
        cat( paste0( image.source, ",Penguin\n" ), file = output.file, append = T )
        file.list <<- file.list[ file.list != image.source ]
        image.source <<- file.list[1]
        output$image <- renderUI(
            img( src = image.source, width = 600 )
        )
    })
    observeEvent( input$Other.bird, {
        cat( paste0( image.source, ",Other.bird\n" ), file = output.file, append = T )
        file.list <<- file.list[ file.list != image.source ]
        image.source <<- file.list[1]
        output$image <- renderUI(
            img( src = image.source, width = 600 )
        )
    })


})
shinyApp(ui = ui, server = server)
  • 如果您想自己运行该应用,只需输入一个名为&#34; www&#34;的文件夹。与app.R文件一起包含几个jpgs,它应该为你运行。

我的问题是,我如何才能更改image.sourcefile.list个对象,并在用户按下下一个按钮时引用它们,而不必回复更改全局变量环境,这样每个用户都维护自己的变量?

谢谢!

2 个答案:

答案 0 :(得分:1)

(我在这里回复,因为评论中没有限制)

即使在你上一次评论之后,我对你的计划有点困惑:

  • 一旦图像被一个用户分类后,是否需要从所有其他用户撤回该图像,或者仅为当前用户撤销该图像
  • 您打算如何保存输出数据(不存在于 代码)
  • 真正需要在所有用户之间共享的内容以及需求 仅相对于一个用户
  • 如何停止&amp;你打算如何保存输出数据(可能是将结果写入文件)

为了说明的目的,我将用代码澄清(只有server部分 - 如果这符合您的需求,您可以轻松地找出如何更改其余部分)其中一个替代方案。

我认为用户需要对所有图像进行分类(仅一次)。

server <- shinyServer( function( input, output, session ) {
#-------------------------------------------------------------
# The following chunk of code will be executed first at the start of each shiny session, 
# i.e. for each user (but to be clear, it will be executed as well if the user will refresh
# the browser, which triggers a new session)

# define the inputs and outputs
file.list <- list.files( path = "www", pattern = ".jpg", ignore.case = T )
output.file <- "classifications.txt"

# check for images already classified, and remove them (to avoid duplicating analysis)
if( file.exists( output.file ) && length( readLines( output.file ) ) > 0 ) {
    already.classified <- try( read.csv( output.file,
                                         header = F,
                                         stringsAsFactors = F ),
                               silent = TRUE )
    if( class( already.classified ) != "try-error" ) {
        already.classified <- unique( already.classified[,1] )
        file.list <- file.list[ !file.list %in% already.classified ]
    }
}

# get the first image to display
image.source <- file.list[1]
    # display the first image to the user
#
# end of the chunk of code
#-------------------------------------------------------------

    output$image <- renderUI(
        img( src = image.source, width = 600 )
    )

    # when a button is pressed:
    observeEvent( input$Shearwater, {

        # print some data to the output file
        cat( paste0( image.source, ",Shearwater\n" ), file = output.file, append = T )

        # remove the image we just looked at from the file list
        file.list <<- file.list[ file.list != image.source ]

        # get the next image to work on
        image.source <<- file.list[1]

        # and render that next image
        output$image <- renderUI(
            img( src = image.source, width = 600 )
        )
    })

    # repeat the above for each button type
    observeEvent( input$Penguin, {
        cat( paste0( image.source, ",Penguin\n" ), file = output.file, append = T )
        file.list <<- file.list[ file.list != image.source ]
        image.source <<- file.list[1]
        output$image <- renderUI(
            img( src = image.source, width = 600 )
        )
    })
    observeEvent( input$Other.bird, {
        cat( paste0( image.source, ",Other.bird\n" ), file = output.file, append = T )
        file.list <<- file.list[ file.list != image.source ]
        image.source <<- file.list[1]
        output$image <- renderUI(
            img( src = image.source, width = 600 )
        )
    })


})

根据R作用域规则,闪亮函数调用中的每个file.list <<-赋值都将更新全局file.list值。 同时,由于file.list值未在服务器功能之外定义,因此每个会话(通常对应于用户会话,除非用户确实刷新浏览器实际上重新启动会话)将重新初始化它

您可以使用此概念来决定在server函数调用范围内放置什么以及在外部管理什么(即真正全局)。 当不需要reactiveValues时,我觉得这个解决方案更可取(这是非常有用的,但只有当你以某种方式利用他们的reactivity时才会这样做。)

如果您的要求不同,请澄清/修改您的问题,我将修改我的答案:)

答案 1 :(得分:-1)

执行您想要做的事情的方法是将image.source(可能更多)指定为被动,然后将其包装在isolate语句中。所以像这样:

image.source <- reactive({
    isolate({output <- file.list[input$Shearwater] })
})

 output$image <- renderUI(
        img( src = image.source(), width = 600 )
抱歉,我还没有这样做。但是这个想法是每次按下Shearwater按钮,input$Shearwater增加1.您可以使用它来索引图像矢量。

“反应”允许您在不将其作为全局变量的情况下引用它。

隔离意味着它不会重新计算,除非input$Shearwater发生变化(我认为)