首页 > 解决方案 > R Shiny 能否为两个同时进行的用户显示不同的视图并相互交互?

问题描述

通常,Shiny 服务器会为单个用户生成单独的实例,以便多个人可以同时单独使用同一个应用程序。这个答案展示了如何使用 Shiny 创建一个简单的多用户聊天室,这个答案解释了多个用户如何通过直接 IP 连接到同一个会话。我得到了聊天示例,两个用户在发送消息时都会立即看到消息,因此可以互相聊天。

我想知道是否有可能将 Shiny 用于(实验)场景,其中两个用户相互交互,会在各自的屏幕上看到不同的 GUI 元素和不同的输出,这取决于轮到谁来“玩”。例如,如果 user1 是“起始玩家”,他会看到三个按钮并单击其中一个,会弹出 user2 的相关图像(不是 user1),user2 单击一个按钮(他认为与图像匹配),然后弹出user1的相关图片,user1点击“正确”/“错误”按钮发送反馈;他们不应该看到谁点击了哪个按钮,也不应该看到另一个人看到的图像(如果实际上隐藏 GUI 元素很棘手,那么间歇性地变灰/禁用它们也可以,只要他们不这样做'

或者更形象地说:

round 1
user1                   user2
director                guesser

what they see, step by step:
1. [three buttons]      [ (blank) ]
2. [clicks one]         [ ]
3  [ ]                  [sees an image & 3 buttons]
4. [ ]                  [clicks a button]
5. [sees image,2 butns] [ ]
6. [clicks button]      [ ]
7. [ ]                  [sees the message "correct" or "incorrect"]

round 2
user1                   user2
guesser                 director
1. [ ]                  [three buttons]
...
...

并且在下一轮中,他们会互换角色,以此类推,进行多轮。

我已经看到使用 Javascript(jsPsych,nodegame)和 Python(psychopy,oTree)实现的类似实验场景,但我希望了解是否可以在 Shiny 中实现,如果可以,如何实现。

标签: ruser-interfaceshinyinteractive

解决方案


几年前,当我将“风险”(棋盘游戏)作为一个闪亮的应用程序实现时,我也遇到了同样的挑战。

快速概述我当时是如何处理它的:

如果您session在服务器函数中使用该参数,您可以reactiveValue()在该会话中为该用户创建一个本地/秘密。

接下来,您可以reactiveValues()在服务器功能之外设置可跨会话访问的“全局信息”。

后一种方法可能更令人惊讶,因为我们通常“被迫”reactive在服务器函数中定义行为。但它有效,请参见下面的示例。

可重现的例子:

library(shiny)

ui <- fluidPage({
  uiOutput("moreControls")
})

global <- reactiveValues(info  = "public info: I can be seen by everyone", amountUser = 0, userIdToPlay = 1)

server <- function(input, output, session) {
  local <- reactiveValues(secret = paste0("My secret number is ", sample(6, 1)))
  
  observe({
    isolate(global$amountUser <-  global$amountUser + 1)
    isolate(local$userId <- global$amountUser)
  })
  
  observeEvent(input$finish,{
    global$userIdToPlay <- 3 - global$userIdToPlay # assumes two players (for MVE)
  })
  
  output$moreControls <- renderUI({
    global$userIdToPlay
    isolate({
      if(local$userId == global$userIdToPlay){
        return(
          tagList(
            h2("my time to play"),
            selectInput("a", "b", letters),
            actionButton("finish", "finish")
          )
        )
      }else{
        return(
          h2("not my time to play")
        )
      }
    })
  })
  
}
shinyApp(ui, server)

推荐阅读