链式调用Shiny Gadget:如何基于返回值触发运行另一个Gadget
解决Shiny Gadget间基于返回值自动启动的问题
问题场景
我有两个可返回值的Shiny Gadget,期望实现:关闭第一个Gadget后,根据其返回值自动启动第二个;关闭第二个Gadget后,根据返回值再启动第一个。但当前递归调用的代码无法实现该需求,且需要不依赖rstudioapi::sendToConsole()的通用方案,触发逻辑必须放在Gadget外部。
原代码如下:
library(shiny) app1 <- shinyApp( fluidPage( "app1", checkboxInput("launch", "launch app2"), actionButton("done", "done") ), function(input, output, session) { observeEvent(input$done, { stopApp(input$launch) }) } ) app2 <- shinyApp( fluidPage( "app2", checkboxInput("launch", "launch app1"), actionButton("done", "done") ), function(input, output, session) { observeEvent(input$done, { stopApp(input$launch) }) } ) run_app1 <- function() { ret <- runGadget(app1, viewer = dialogViewer("app1")) if (ret) run_app2() } run_app2 <- function() { ret <- runGadget(app2, viewer = dialogViewer("app2")) if (ret) run_app1() }
问题原因
runGadget()是阻塞式函数,直接在启动函数内递归调用另一个启动函数时,当前Gadget的运行上下文未完全释放,导致新的Gadget无法正常初始化启动。
解决方案
使用later包的later()函数延迟启动下一个Gadget,确保当前Gadget的运行环境完全释放后再执行启动逻辑。这是不依赖RStudio的通用方案,且触发逻辑完全在Gadget外部。
修改后的代码:
library(shiny) library(later) app1 <- shinyApp( fluidPage( "app1", checkboxInput("launch", "launch app2"), actionButton("done", "done") ), function(input, output, session) { observeEvent(input$done, { stopApp(input$launch) }) } ) app2 <- shinyApp( fluidPage( "app2", checkboxInput("launch", "launch app1"), actionButton("done", "done") ), function(input, output, session) { observeEvent(input$done, { stopApp(input$launch) }) } ) run_app1 <- function() { ret <- runGadget(app1, viewer = dialogViewer("app1")) if (ret) { # 延迟0.1秒启动,确保当前Gadget上下文完全释放 later(run_app2, delay = 0.1) } } run_app2 <- function() { ret <- runGadget(app2, viewer = dialogViewer("app2")) if (ret) { later(run_app1, delay = 0.1) } }
运行run_app1()后,勾选"launch app2"并点击done关闭app1,会自动启动app2;在app2中勾选"launch app1"并关闭后,也会自动启动app1,完全符合需求。
内容的提问来源于stack exchange,提问作者DeanAttali
相关产品推荐
相关产品推荐

