Leone
Leone

Reputation: 31

Access RStudio R console when running a shiny app

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

Answers (1)

ismirsehregal
ismirsehregal

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

Related Questions