Reputation: 31
I’ve written an RStudio addin that launches a Shiny gadget in the RStudio Viewer. However, once the gadget is running, my R console is locked until I close the gadget. Here’s a minimal example of my code:
library(rstudioapi)
library(shiny)
# Simplified function to fetch history
get_r_history <- function() {
# Simulates fetching history (real function fetches R or terminal history)
return(c("plot(1:10)", "summary(mtcars)", "lm(y ~ x, data = df)"))
}
# Simplified addin UI and server
run_addin <- function() {
history <- get_r_history()
ui <- fluidPage(
titlePanel("History Selector"),
selectInput("selected_line", "Select a line:", choices = history),
actionButton("insert", "Insert into Script")
)
server <- function(input, output, session) {
observeEvent(input$insert, {
selected_line <- input$selected_line
if (!is.null(selected_line)) {
context <- rstudioapi::getActiveDocumentContext()
if (!is.null(context)) {
rstudioapi::insertText(location = context$selection[[1]]$range, text = selected_line)
}
}
})
}
shiny::runGadget(ui, server, viewer = shiny::paneViewer())
}
run_addin()
When the gadget is displayed in the RStudio Viewer, I cannot execute code in the console simultaneously. Is there a way to keep the gadget in the RStudio Viewer but still have access to the console? I have seen that by using browserViewer() to open it externally might work, but I specifically want to display it in the RStudio Viewer.
Any suggestions or workarounds would be greatly appreciated.
Upvotes: 2
Views: 128
Reputation: 33520
Please check the below workaround using my earlier answer here.
The shiny app is started in a backgound R process via callr::r_bg
.
Once the user clicks the Insert
button a txt file is filled with the selected line.
The main process checks this text file continuously for new content without blocking itself by using a recursive call to later::later
:
library(rstudioapi)
library(shiny)
library(callr)
library(later)
# Simplified function to fetch history
get_r_history <- function() {
# Simulates fetching history (real function fetches R or terminal history)
return(c("plot(1:10)", "summary(mtcars)", "lm(y ~ x, data = df)"))
}
history <- get_r_history()
ui <- fluidPage(
titlePanel("History Selector"),
selectInput("selected_line", "Select a line:", choices = history),
actionButton("insert", "Insert into Script")
)
server <- function(input, output, session) {
observeEvent(input$insert, {
selected_line <- input$selected_line
if (length(selected_line) > 0L && !selected_line == "") {
writeLines(selected_line, con = "selected_line.txt")
}
})
}
app <- shinyApp(ui, server)
viewer("http://localhost:8080")
shiny_bg_process <- callr::r_bg(function(app, viewer){shiny::runGadget(app, viewer, port = 8080)}, args = list(app, viewer))
# shiny_bg_process$is_alive()
# shiny_bg_process$kill()
recursive_check = function(interval = 1L) {
if(file.exists("selected_line.txt")){
selected_line <- readLines(con = "selected_line.txt")
writeLines("", con = "selected_line.txt") # reset
} else {
selected_line <- NULL
}
if(length(selected_line) > 0L && !selected_line == ""){
context <- rstudioapi::getActiveDocumentContext()
if (!is.null(context)) {
rstudioapi::insertText(location = context$selection[[1]]$range, text = selected_line)
}
}
later::later(recursive_check, interval)
}
recursive_check()
PS: as mentioned in my above comment you might be able to realize a similar behaviour via library(ipc). Ideally, polling the selected_line
should be avoided (signal the main process from the sub process).
PPS:
Apart from some predefined options runGadget
and runApp
more or less do the same thing: starting a shiny app.
Upvotes: 3