【问题标题】:conditionally output different colored text in Shiny有条件地在 Shiny 中输出不同颜色的文本
【发布时间】:2016-01-27 16:19:12
【问题描述】:

我希望 Shiny 根据矢量的大小打印出一些不同颜色的文本。我在想这样的事情:

  output$some_text <- renderText({ 
    if(length(some_vec) < 20){
      paste("This is red text")
      <somehow make it red>
    }else{
    paste("This is blue text")
      <somehow make it blue>

...但后来我意识到,我是在服务器中执行此操作,而不是 UI。

而且,据我所知,我无法将此条件逻辑移到 UI 中。

例如,这样的内容在 UI 中不起作用:

    if(length(some_vec)< 20){
         column(6, tags$div(
         HTML(paste("This text is ", tags$span(style="color:red", "red"), sep = ""))
      )}
    else{
         tags$div(HTML(paste("This text is ", tags$span(style="color:blue", "blue"), sep = ""))
)}

有人有什么创意吗?

【问题讨论】:

    标签: r shiny shinyjs


    【解决方案1】:

    jenesaisquoi's answer 的启发,我尝试了以下方法,它对我有用。它是反应式的,不需要额外的包。特别看output$text3

    library(shiny)
    
    ui <- shinyUI(fluidPage(
      titlePanel("Reactive"),
      sidebarLayout(
        sidebarPanel(
          helpText("Variables!"),
          selectInput("var", 
                      label = "Choose Variable",
                      choices = c("red", "blue",
                                  "green", "black"),
                      selected = "Rojo"),
          sliderInput("range", 
                      label = "Range:",
                      min = 0, max = 100, value = c(0, 100))
        ),
        mainPanel(
          textOutput("text1"),
          textOutput("text2"),
          htmlOutput("text3"),
          textOutput("text4")
        )
      )
    ))
    
    server <- function(input, output) {
      output$text1 <- renderText({ 
        paste("You have selected variable:", input$var)
      })
    
      output$text2 <- renderText({ 
        paste("You have selected range:", paste(input$range, collapse = "-"))
      })
    
      output$text3 <- renderText({
        paste('<span style=\"color:', input$var, 
              '\">This is "', input$var, 
              '" written ', input$range[2], 
              ' - ', input$range[1], 
              ' = ', input$range[2] - input$range[1], 
              ' times</span>', sep = "")
      })
    
      output$text4 <- renderText({ 
        rep(input$var, input$range[2] - input$range[1])
      })
    }
    
    # Run the application 
    shinyApp(ui = ui, server = server)
    

    【讨论】:

      【解决方案2】:

      来寻找类似问题的答案。尝试了一种适合我需要的简单方法。它使用内联 html 样式和 htmlOutput。

      library(shiny)
      
      ui <- fluidPage(
       mainPanel(
       htmlOutput("some_text")
       )
      )
      

      server <- function(input, output) {
      
         output$some_text <- renderText({ 
      
           if(length(some_vec) < 20){
           return(paste("<span style=\"color:red\">This is red text</span>"))
      
           }else{
           return(paste("<span style=\"color:blue\">This is blue text</span>"))
           }
         })
       }
      

      条件在服务器端运行——从开放的问题中我并不清楚作者需要在 UI 中运行的条件。我没有。也许是在常见情况下解决问题的简单方法。

      【讨论】:

      • 这很有帮助。我试图对用户读入的数据运行条件,然后将该变量传递给
        标记的class。我从来没有完全让它工作,但你的方法使我能够将整个标签作为文本传递。
      • 更进一步,还可以考虑任何其他样式属性,
        ,标题类等。你想要的,所以组成一些更有趣的条件输出。我现在已经在应用程序中使用了很多次,现在它很明显是一个选项。
      • 如果有人在他们的应用程序中打印&lt;span style... 而不是解释为颜色,那么请记住 UI 中的输出对象需要是 htmlOutput 而不是 textOutput!答案很明显且正确,但对于新手来说很容易错过,我花了很长时间才意识到这个问题!
      【解决方案3】:

      嗯,我有一个想法的核心,但我对任何与 HTML/CSS/JavaScript 相关的东西都很陌生,所以我确信它可以改进很多。也就是说,就目前而言,这似乎运作良好。

      关键函数是removeClass()addClass(),在shinyjs中各自的帮助文件中有详细记录:

      library(shiny)
      library(shinyjs)
      
      shinyApp(
          ui = fluidPage(
              useShinyjs(),  ## Set up shinyjs
              ## Add CSS instructions for three color classes
              inlineCSS(list(.red   = "color: red",
                             .green = "color: green",
                             .blue  = "color: blue")),
              numericInput("nn", "Enter a number",
                           value=1, min=1, max=10, step=1),
              "The number is: ", span(id = "element", textOutput("nn", inline=TRUE))
              ),
          server = function(input, output) {
              output$nn <- renderText(input$nn)
              observeEvent(input$nn, {
                  nn <- input$nn
                  if(is.numeric(as.numeric(nn)) & !is.na(as.numeric(nn))) {
                      ## Clean up any previously added color classes
                      removeClass("element", "red")
                      removeClass("element", "green")
                      removeClass("element", "blue")
                      ## Add the appropriate class
                      cols <- c("blue", "green", "red")
                      col <- cols[cut(nn, breaks=c(-Inf,3.5, 6.5, Inf))]
                      addClass("element", col)
                  } else  {}
              })
          })
      

      【讨论】:

        【解决方案4】:

        听起来您正试图将其全部保留在客户端,因此您可以只使用几个conditionalPanels,它们接受 javascript 作为条件代码。例如,根据 ID 为“len”的numericInput 框中的当前值对文本进行着色,

        library(shiny)
        ui <- shinyUI(
            fluidPage(
                fluidRow(
                    numericInput('len', "Length", value=19),
                    conditionalPanel(
                        condition = "$('#len').val() > 20",
                        div(style="color:red", "This is red!")),
                    conditionalPanel(
                        condition = "$('#len').val() <= 20",
                        div(style="color:blue", "This is blue!"))
                )
            )
        )
        
        server <- function(input, output, session) {}
        shinyApp(ui = ui, server=server)
        

        您还可以添加一个事件侦听器来使用 javascript 更新文本。这有点难看(而且我不太了解 javascript),但您可以将脚本移动到 wwww/ 中的文件并使用 includeScript。和前面的例子一样,server 什么都不做。

        ui <- shinyUI(bootstrapPage(
            numericInput('len', "Length", value=19),
            div(id="divvy", style="color:blue", "This is blue!"),
            tags$script(HTML("
                var target = $('#len')[0];
                target.addEventListener('change', function() {
                    var color = target.value > 20 ? 'red' : 'blue';
                    var divvy = document.getElementById('divvy');
                    divvy.style.color = color;
                    divvy.innerHTML = divvy.innerHTML.replace(/red|blue/g, color);
                });
            "))
        ))
        

        【讨论】:

          【解决方案5】:

          这是一个更灵活的答案,它使用shinyjs::extendShinyjs() 为 R 提供了一种生成一些参数化 JavaScript 代码的方法。与我的其他答案相比,这个答案的优点是可以使用相同的函数对多个数字输出进行反应性着色。

          library(shiny)
          library(shinyjs)
          
          jsCode <-
          "shinyjs.setCol = function(params){
               var defaultParams = {
                   id: null,
                   color : 'red'
               };
               params = shinyjs.getParams(params, defaultParams);
               $('.shiny-text-output#' + params.id).css('color', params.color);
           }"
          setColor <- function(id, val) {
              if(is.numeric(as.numeric(val)) & !is.na(as.numeric(val))) {
                  cols <- c("green", "orange", "red")
                  col <- cols[cut(val, breaks=c(-Inf,3.5, 6.5, Inf))]
                  js$setCol(id, col)
              }
          }
          
          shinyApp(
              ui = fluidPage(
                  useShinyjs(),  ## Set up shinyjs
                  extendShinyjs(text = jsCode),
                  numericInput("n", "Enter a number", 1, 1, 10, 1),
                  "The number is: ", textOutput("n", inline=TRUE),
                  br(),
                  "Twice the number is: ", textOutput("n2", inline=TRUE)
                  ),
              server = function(input, output) {
                  output$n  <- renderText(input$n)
                  output$n2 <- renderText(2 *  input$n)
                  observeEvent(input$n, setColor(id = "n", val = input$n))
                  observeEvent(input$n, setColor(id = "n2", val = 2 * input$n))
              })
          

          【讨论】:

          • 啊……这是很棒的代码。它太接近为我工作了!!我有一个小(好的,大)问题,我试图根据服务器中数据框的大小(行数)更改颜色。当数据框太小时,“样本量”文本应变为红色,以警告用户样本量过小。我不得不删除observeEvent 电话...造成各种痛苦...
          • 当您指向一个新的(和更小的)data.frame 时,您是否在更新颜色时遇到问题?如果是这样,我想知道在您计算从 data.frame 读入并作为元素分配给 input 对象的中间对象(包含行数)时,您是否打破了“反应链”(这可能取决于“反应性导体/反应性表达式”described here。)您是否查看过reactive() 和/或(取决于您如何执行此操作)reactiveValues() 函数?
          猜你喜欢
          相关资源
          最近更新 更多
          热门标签