Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use website 'inside' a shiny app? #20

Open
vnijs opened this issue Mar 14, 2016 · 20 comments
Open

use website 'inside' a shiny app? #20

vnijs opened this issue Mar 14, 2016 · 20 comments

Comments

@vnijs
Copy link

vnijs commented Mar 14, 2016

Is it feasible to run webshot from a shiny app? Ideally, I'd like to press a button to take a screenshot of the current page in a shiny app, store the image, and then put a reference to the screenshot into an knitr document in the app or into Rstudio. I tried a few things it but it locked up the session but I want the app to continue working after the screenshot is taken. I think I could get this (mostly) working assuming webshot can be called from a shiny app.

Alternatively, could an Rstudio addin be used to create a screen-shot of the active app and put the result into the Rstudio Viewer? As far as I can tell, you can only run one addin at a time unfortunately.

@yihui
Copy link
Collaborator

yihui commented Mar 15, 2016

You should not call webshot::appshot() to screenshot the app, since that will lauch an app in another R session. Instead, just pass the URL of the running app to webshot::webshot(). Then the question is how to access the URL of the current app in R, and I don't know if there is a straightforward answer...

@vnijs
Copy link
Author

vnijs commented Mar 15, 2016

@yihui I did try that. Passing a url to an external site (e.g., google) works fine. In a shiny app you can use clientData to find out the app url. Works fine. However, when you pass it to webshot the shiny app locks up. See reproducible example below.

server <- function(session, input, output) {
  output$distPlot <- renderPlot({
    hist(rnorm(input$obs), col = 'darkgray', border = 'white')
  })

  observeEvent(input$screenshot, {
    cdat <- session$clientData
    url <- paste0(cdat$url_protocol,"//",cdat$url_hostname,":", cdat$url_port, cdat$url_pathname,cdat$url_search)
    print(url)
    # url <- "http://www.google.com/"
    webshot::webshot(url, "~/Desktop/webshot.png")
  })
}

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      actionButton("screenshot","Take a screenshot"), br(),
      sliderInput("obs", "Number of observations:", min = 10, max = 500, value = 100)
    ),
    mainPanel(plotOutput("distPlot"))
  )
)

shinyApp(ui = ui, server = server)

@yihui
Copy link
Collaborator

yihui commented Mar 15, 2016

Okay, I have no idea about how it could lock up the app then...

@wch
Copy link
Owner

wch commented Mar 15, 2016

Hm, I'm able to run the app and then run the webshot.js script from the command line to get a screenshot. For example:

/home/winston/R/3.2/webshot/webshot.js http://127.0.0.1:7714/ webshot.png --vwidth=992 --vheight=744 --delay=0.2

This is the exact same command line that webshot runs.

However, if I try to call webshot() from inside the app, it hangs. I wonder if the problem is that the child process also inherits the open port (7714), and it blocks as a result of that somehow. Here's something in that direction: http://stackoverflow.com/a/9133957

That said, I don't think this will do what you want, @vnijs. It would take a screenshot of a new connection to the same app, not a screenshot of your existing session. To do the latter, I think you'd need a browser extension or a regular screenshot program.

@wch
Copy link
Owner

wch commented Aug 5, 2016

I got an email from Uwe Ligges about a related issue that appears to be Windows-specific.

package webshot hangs on winbuilder from time to time in a way, that I cannot even kill it appropriately.

It hangs when rebuilding vignettes and the log file from that is:

Quitting from lines 96-98 (intro.Rmd)
Error: processing vignette 'intro.Rmd' failed with diagnostics:
cannot open the connection
Execution halted
Loading required package: shiny

Listening on http://127.0.0.1:7845

@p0bs
Copy link

p0bs commented Dec 3, 2018

Has anything on this issue changed recently, with the advent of appshot?

Specifically, is it now possible to take a shot of the screen with the current state of a shiny app (i.e. once all user inputs have been made)? I ask because I'm trying to find a good way to save outputs generically as image files and this looks like a good bet.

Many thanks to you all.

@vnijs
Copy link
Author

vnijs commented Dec 3, 2018

I don't think so no. I believe you can take screenshots with shinytest however.

https://github.com/rstudio/shinytest

@ManuHamel
Copy link

ManuHamel commented Jul 16, 2019

I had the same problem on Windows! If we create the subprocess that calls phantomjs with the callr package , there is a "bug" in the phantom_run function when you call the function webshot in a shinyApp on the shinyApp that contains the webshot call.

However, if you use the function spawn_process in the R package subprocess to create a subprocess that calls phantomjs, then there is no bug.

In sum, we simply have to modify the phantom_run function to make it work by using the R package subprocess instead of callr.

@leungi
Copy link

leungi commented Jul 23, 2019

Test @vnijs sample app above on Linux; same result.

@ManuHamel
Copy link

If you use the spawn_process function instead of the callr R package in the function phantom_run function for the sample app that is given in this page, it works on Windows (at least on my computer).

@vnijs
Copy link
Author

vnijs commented Jul 29, 2019

That sounds very interesting @ManuHamel. Could you post a reproducible example?

@ManuHamel
Copy link

Hi,

Here is an example that is working on my computer on Windows : https://github.com/ManuHamel/callWebshotInShinyapp
Also, some functions in the package are from the webshot R package. I do not claim to be the author.

Best regards,

Emmanuel Hamel

@leungi
Copy link

leungi commented Aug 3, 2019

Tested and working locally, but can't get in working on Shiny server 😕

Checked logs and can't seem to find out what's wrong - phantomjs found, saving to a writable folder by shiny user.

Kudos and thanks to @ManuHamel 🙇

@ManuHamel
Copy link

When I will have time, I will test it on shiny server and I will let you know If I find a solution.

@leungi
Copy link

leungi commented Aug 4, 2019

Thanks @ManuHamel.

In case this helps:

In Linux (no error reported in Shiny logs, but no output file):

url = "http://www.google.com"
path_to_file <- '"/home/shiny/webshot-js.png"'
lien_vers_package <- system.file(package = "webshot")
path_to_webshot_js <- '"/usr/lib64/R/library/webshot/webshot_modif.js"'
arg1 <- paste0("[{\'url\':\'", url, "\'", ",\'file\':\'", path_to_file, "\'}]")
path_To_Phantom <- "/usr/lib64/R/library/webshot/PhantomJS/phantomjs"
args <- c(path_to_webshot_js, arg1)
subprocess::spawn_process(command = path_To_Phantom, arguments = args)

# resulting subprocess call
# /usr/lib64/R/library/webshot/PhantomJS/phantomjs-2.1.1-linux-x86_64/bin/phantomjs "/usr/lib64/R/library/webshot/webshot_modif.js" [{'url':'http://www.google.com','file':'"/home/shiny/webshot-js.png"'}]

In Windows (works as designed):

url = "http://www.google.com"
path_to_file <- '"./callWebshotInShinyapp/Save Screenshot PNG/webshot5.png"'
lien_vers_package <- system.file(package = "webshot")
path_to_webshot_js <- '"C:/Users/Public/Data/R/R-3.5.1/library/webshot/webshot_modif.js"'
arg1 <- paste0("[{\'url\':\'", url, "\'", ",\'file\':\'", path_to_file, "\'}]")
path_To_Phantom <- find_phantom()
args <- c(path_to_webshot_js, arg1)
subprocess::spawn_process(command = path_To_Phantom, arguments = args)

# resulting subprocess call
# C:\Users\leungi\AppData\Roaming\PhantomJS\phantomjs.exe "C:/Users/Public/Data/R/R-3.5.1/library/webshot/webshot_modif.js" [{'url':'http://www.google.com','file':'"./callWebshotInShinyapp/callWebshotInShinyapp/Save Screenshot PNG/webshot3.png"'}]

@wch
Copy link
Owner

wch commented Aug 6, 2019

@vnijs With https://github.com/rstudio/chromote, you should be able to achieve your original goal. The idea is that you launch headless chrome, then open a viewer into that headless browser, interact via the viewer, and then take a screenshot.

You'd do something like this:

library(shiny)
runExample("01_hello", port = 1234)

Then in another R session:

library(chromote)
b <- ChromoteSession$new()
b$view() # Opens a viewer to the headless browser, in a regular browser window
b$Page$navigate("http://localhost:1234")

Now you can interact with the app via the viewer. Once you're done interacting, you can do:

b$screenshot("1.png")

@leungi
Copy link

leungi commented Aug 8, 2019

@wch, I presume this is similar to your webshot2.

If I understand it correctly, to get a screenshot of a particular user's active session (with all the input/output), we'll need to bookmark the session for this solution to work. This is because we won't know ahead of time how the particular user will interact with app.

Screen-shooting a fresh app will work with this solution, of course.

@wch
Copy link
Owner

wch commented Aug 8, 2019

@leungi It's not possible to get a screenshot of any arbitrary user's active session. What I was describing was how to get a screenshot of your own active session.

@leungi
Copy link

leungi commented Aug 10, 2019

@wch: as I suspected, even after relaunching a fresh headless browser after each update of user input in Shiny app, the captured image is always the original.

@vnijs
Copy link
Author

vnijs commented Jun 23, 2020

FYI snapper (https://github.com/yonicd/snapper) does pretty much exactly what I was looking for, i.e., create a screenshot of a shiny app while you are using it). There are, unfortunately some important edge cases (e.g., yonicd/snapper#4) that don't work yet. This particular problem stems from an issue in html2canvas (niklasvh/html2canvas#2166).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants