【问题标题】:How to prevent user from doing anything on shiny app when app is busy应用程序繁忙时如何防止用户在闪亮的应用程序上做任何事情
【发布时间】:2022-05-04 16:22:50
【问题描述】:

我有一个复杂的闪亮应用程序,其中包含大量输入、传单地图等...... 我遇到的问题是当应用程序忙于进行一些计算时,用户不断点击应用程序上的任何位置,有时应用程序崩溃。

我想阻止用户在应用程序繁忙时进行任何点击。重要的是微调器保持微调器而不是像waiter 包中的整页服务员。也许有可能将微调器和服务员结合起来?但我还没找到怎么做。

我在这里有一个小代表: 当我单击一个按钮“忙碌的应用程序”时,有一个 5 秒的微调器让用户等待。但在此期间,用户仍然可以点击“增量”按钮。在微调器结束时,输出会按点击次数增加。 我想立即将整个应用程序阻止给用户,而不仅仅是在应用程序繁忙时在按钮上放置“禁用”(因为我的应用程序中有很多输入,并且需要进行太多修改)

library(shiny)

ui <- fluidPage(

  # spinner css
  tags$head(
    tags$style(HTML("
  #loadmessage {
  position:fixed; z-index:8; top:50%; left:50%; padding:10px;
  text-align:center; font-weight:bold; color:#000000; background-color:#CCFF66;
  }
  
  .loader {
  position:fixed; z-index:8; border:16px solid #999999;
  border-top: 16px solid #8B0000; border-radius: 50%;
  width: 120px; height: 120px; top:45%; left:45%;
  animation: spin 2s linear infinite;
  }

  @keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
  }"))
  ),
  
  # display load spinner when shiny is busy
  conditionalPanel(
    condition = "$(\'html\').hasClass(\'shiny-busy\')",
    tags$div(class = "loader")
  ),
  actionButton(
    inputId = "increment",
    label = "Increment"
  ),
  textOutput("result"),
  actionButton(
    inputId = "busy",
    label = "Busy app"
  )
)

server <- function(input, output, session) {
  
  rv <- reactiveValues(counter = 0)

  #increment counter
  observeEvent(input$increment,{
    rv$counter = rv$counter + 1
  })
  
  #display incremented counter
  output$result <- renderText({
    rv$counter
  })
  
  observeEvent(input$busy, {
    Sys.sleep(5)
    # during this time, the user should not be able to do anything on the app
  })
}

shinyApp(ui = ui, server = server)

【问题讨论】:

  • 也许this 可以提供帮助。
  • 我看到了,但我正在尝试寻找更全球化的东西,而不仅仅是输入。我有地图、多边形、标记……也可以点击。我在想也许只是在闪亮忙时用一些 JS 禁用左键
  • 使用弹出窗口?
  • 太干扰了,它会每秒弹出一次。当用户在地图上移动时,地图上会加载数据,因此每秒会弹出一个弹出窗口,持续 0.1 秒

标签: r shiny


【解决方案1】:

我们可以尝试使用shinyjs 库结合walk 函数来禁用所有输入。

  observeEvent(input$busy, {
    names(input) %>% walk(disable)
    Sys.sleep(3) 
    # during this time, the user should not be able to do anything on the app
    names(input) %>% walk(enable)
  })
}

应用程序:

library(shiny)
library(shinyjs)
library(tidyverse)

ui <- fluidPage(
  useShinyjs(),
  # spinner css
  tags$head(
    tags$style(HTML("
  #loadmessage {
  position:fixed; z-index:8; top:50%; left:50%; padding:10px;
  text-align:center; font-weight:bold; color:#000000; background-color:#CCFF66;
  }
  
  .loader {
  position:fixed; z-index:8; border:16px solid #999999;
  border-top: 16px solid #8B0000; border-radius: 50%;
  width: 120px; height: 120px; top:45%; left:45%;
  animation: spin 2s linear infinite;
  }

  @keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
  }"))
  ),
  
  # display load spinner when shiny is busy
  conditionalPanel(
    condition = "$(\'html\').hasClass(\'shiny-busy\')",
    tags$div(class = "loader")
  ),
  actionButton(
    inputId = "increment",
    label = "Increment"
  ),
  textOutput("result"),
  actionButton(
    inputId = "busy",
    label = "Busy app"
  )
)

server <- function(input, output, session) {
  
  rv <- reactiveValues(counter = 0)
  
  #increment counter
  observeEvent(input$increment,{
    rv$counter = rv$counter + 1
  })
  
  #display incremented counter
  output$result <- renderText({
    rv$counter
  })
  
  observeEvent(input$busy, {
    names(input) %>% walk(disable)
    Sys.sleep(3) 
    # during this time, the user should not be able to do anything on the app
    names(input) %>% walk(enable)
  })
}

shinyApp(ui = ui, server = server)

【讨论】:

  • 感谢您的回答。我看不到如何将其调整为独立于计时器并仅在应用程序繁忙时才禁用元素。然而,我找到了一个简单的 CSS 解决方案,它适合我需要的一切。我刚刚发布了答案。
【解决方案2】:

我也会使用shinyjs,但不是通过所有输入,而是使用一些 CSS 并更改所有输入和按钮的pointer-events。您可以根据需要调整 CSS 选择器。

  observeEvent(input$busy, {
    shinyjs::runjs('$("input, button").css("pointer-events", "none")')
    Sys.sleep(5)
    # during this time, the user should not be able to do anything on the app
    shinyjs::runjs('$("input, button").css("pointer-events", "unset")')
  })

【讨论】:

  • 感谢您的回答。我看不到如何将其调整为独立于计时器并仅在应用程序繁忙时才禁用元素。然而,我找到了一个简单的 CSS 解决方案,它适合我需要的一切。我刚刚发布了答案。
【解决方案3】:

在分析你的答案并仔细考虑之后,我想我找到了最简单的解决方案。

在“闪亮的忙碌”事件中,我在条件面板中显示一个 div,它是屏幕的 100% 并且在第一个计划中,因此它可以防止对其后面的输入/输出进行任何点击。当应用程序不再忙时,面板消失。该面板是透明的,因此用户看不到它。

此外,它使我能够在不依赖计时器的情况下禁用所有输入和输出,仅在应用程序繁忙与否时才启用。

library(shiny)

ui <- fluidPage(

  # spinner css
  tags$head(
    tags$style(HTML("
  #loadmessage {
  position:fixed; z-index:8; top:50%; left:50%; padding:10px;
  text-align:center; font-weight:bold; color:#000000; background-color:#CCFF66;
  }
  
  .loader {
  position:fixed; z-index:8; border:16px solid #999999;
  border-top: 16px solid #8B0000; border-radius: 50%;
  width: 120px; height: 120px; top:45%; left:45%;
  animation: spin 2s linear infinite;
  }

  .prevent_click{
  position:fixed; 
  z-index:9;
  width:100%;
  height:100vh;
  background-color: transpare'nt;   
  }

  @keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
  }"))
  ),
  
  # display load spinner when shiny is busy
  conditionalPanel(
    condition = "$(\'html\').hasClass(\'shiny-busy\')",
    tags$div(class = "loader"),
    tags$div(class = "prevent_click")
  ),
  actionButton(
    inputId = "increment",
    label = "Increment"
  ),
  textOutput("result"),
  actionButton(
    inputId = "busy",
    label = "Busy app"
  )
)

server <- function(input, output, session) {
  
  rv <- reactiveValues(counter = 0)

  #increment counter
  observeEvent(input$increment,{
    rv$counter = rv$counter + 1
  })
  
  #display incremented counter
  output$result <- renderText({
    rv$counter
  })
  
  observeEvent(input$busy, {
    Sys.sleep(5)
    # during this time, the user should not be able to do anything on the app
  })
}

shinyApp(ui = ui, server = server)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-07-02
    • 2020-09-21
    • 2020-06-02
    • 2014-11-18
    • 2013-07-08
    • 2011-01-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多