Search code examples
rrstudio

RStudio data.frame Viewer turns data.frames to lists on user-defined `View` methods


Does anyone have an idea (or a 'solution') that in case you define a View() method for a S4 class in which you would like to access a certain slot in the RStudio data.frame Viewer it just won't return it as expected?
What I mean is if you start a fresh R session eg

View(mtcars)

will work as expected and deliver RStudio-DataFrame-Viewer (works as expected including Filters etc in the Viewer)

But then if you define some S4 class eg

foo <- setClass("foo", slots = c(df = "data.frame"))
myfoo <- new('foo', df = mtcars) 

and then a View method

setMethod("View", "foo", function(x, title) View(x@df, title))

You will suddenly face one of these (two differing) messages:

Creating a generic function for 'View' from package 'stats' in the global environment

OR this one

Creating a generic function for 'View' from package 'utils' in the global environment

Which imo is already "disturbing" since afaik the stats package does not seem to even have a View function.

But lets continue and get to what I really wonder about. Which is why I do not get the expected same result in RStudio's data.frame Viewer but some form of list view?

enter image description here

Can this somehow be avoided and made to produce the expected ie same type of "View" as for the standalone data.frame?
And to top it all off once you do that within a R package you end up with this (final pic below) where in the background you see View(mtcars) output before devtools::load_all() was run and in the foreground you see the result of View(mtcars) after load_all() picked up all methods/functions in the package? Is that a RStudio bug, or am I doing something wrong here?

enter image description here


Solution

  • Here is a (not-so-nice) workaround until a better solution comes along.

    foo <- setClass("foo", slots = c(df = "data.frame"))
    myfoo <- new('foo', df = mtcars) 
    
    old <- View
    View <- function(...) {
        if(isS4(...)) {
            unclass(...)@df |> format.data.frame() |> old()
        } else {
            old(...)
        }
    }
    

    The viewer with the red lines is the default utils::View,

    function (x, title) 
    {
        check <- Sys.getenv("_R_CHECK_SCREEN_DEVICE_", "")
        msg <- "View() should not be used in examples etc"
        if (identical(check, "stop")) 
            stop(msg, domain = NA)
        else if (identical(check, "warn")) 
            warning(msg, immediate. = TRUE, noBreaks. = TRUE, domain = NA)
        if (missing(title)) 
            title <- paste("Data:", deparse(substitute(x))[1])
        x0 <- as.data.frame(x)
        x <- as.list(format.data.frame(x0))
        rn <- row.names(x0)
        if (any(rn != seq_along(rn))) 
            x <- c(list(row.names = rn), x)
        if (!is.list(x) || !length(x) || !all(sapply(x, is.atomic)) || 
            !max(lengths(x))) 
            stop("invalid 'x' argument")
        if (grepl("darwin", R.version$os)) 
            check_for_XQuartz()
        invisible(.External2(C_dataviewer, x, title))
    }
    

    RStudio modifies View somewhere1, maybe here to a more user friendly interface, via

    function (...) 
    .rs.callAs(name, hook, original, ...)
    

    where original is utils::View, ( see e <- environment(View); e$original).


    1 I have not figured out (yet) where exactly.