1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
#' Edit a Data Frame.
#'
#' Interactively edit a \code{data.frame} or \code{data.table}. The resulting
#' code will be emitted as a call to reload the data from a temp RDS file.
#'
#' This addin can be used to interactively edit.
#' The intended way to use this is as follows:
#'
#' 1. Highlight a symbol naming a \code{data.frame} or \code{data.table}
#' in your R session, e.g. \code{mtcars}.
#' 2. Execute this addin, to interactively edit it.
#'
#' When you're done, the code performing this operation will be emitted
#' at the cursor position.
#'
#' This function borrows heavily from \href{rstudio/addinexamples/subsetAddin}{https://github.com/rstudio/addinexamples/blob/master/R/subsetAddin.R}
#'
#' @export
editAddin <- function() {
# Get the document context.
context <- rstudioapi::getActiveDocumentContext()
# Set the default data to use based on the selection.
text <- context$selection[[1]]$text
defaultData <- text
# create a temp file
fname = gsub("\\\\", "/", tempfile())
# Generate UI for the gadget.
ui <- miniUI::miniPage(
miniUI::gadgetTitleBar("Edit a data.frame"),
miniUI::miniContentPanel(
stableColumnLayout(
shiny::textInput("data", "Data", value = defaultData),
shiny::radioButtons("outType", "Output type",
choices = c("Update original data" = "update",
"Print updates to script (no update)" = "print")
)
),
shiny::uiOutput("pending"),
rHandsontableOutput("hot")
)
)
# Server code for the gadget.
server <- function(input, output, session) {
values = shiny::reactiveValues()
setHot = function(x) values[["hot"]] = x
reactiveData <- shiny::reactive({
# Collect inputs.
dataString <- input$data
# Check to see if there is data called 'data',
# and access it if possible.
if (!nzchar(dataString))
return(errorMessage("data", "No dataset available."))
if (!exists(dataString, envir = .GlobalEnv))
return(errorMessage("data", paste("No dataset named '", dataString, "' available.")))
data <- get(dataString, envir = .GlobalEnv)
data
})
output$pending <- shiny::renderUI({
data <- reactiveData()
if (isErrorMessage(data))
htmltools::h4(style = "color: #AA7732;", data$message)
})
output$hot <- renderRHandsontable({
data <- reactiveData()
if (isErrorMessage(data))
return(NULL)
if (is.null(input$hot))
DF = data
else
DF = hot_to_r(input$hot)
setHot(DF)
rhandsontable(DF) %>%
hot_table(highlightCol = TRUE, highlightRow = TRUE)
})
# Listen for 'done'.
shiny::observeEvent(input$done, {
if (input$outType == "print") {
rslt <- capture.output(dput(values[["hot"]]))
rstudioapi::insertText(Inf, paste0(input$data, " = ",
paste(rslt, collapse = "\n")))
} else {
if (nzchar(input$data) && !is.null(values[["hot"]])) {
saveRDS(values[["hot"]], fname)
code <- paste(input$data, " = readRDS('", fname, "')", sep = "")
rstudioapi::sendToConsole(code)
}
}
invisible(shiny::stopApp())
})
}
# Use a modal dialog as a viewr.
viewer <- shiny::dialogViewer("Edit", width = 1000, height = 800)
shiny::runGadget(ui, server, viewer = viewer)
}
# these functions come from rstudio/addinexamples
stableColumnLayout <- function(...) {
dots <- list(...)
n <- length(dots)
width <- 12 / n
class <- sprintf("col-xs-%s col-md-%s", width, width)
shiny::fluidRow(
lapply(dots, function(el) {
shiny::div(class = class, el)
})
)
}
isErrorMessage <- function(object) {
inherits(object, "error_message")
}
errorMessage <- function(type, message) {
structure(
list(type = type, message = message),
class = "error_message"
)
}
|