shiny icon indicating copy to clipboard operation
shiny copied to clipboard

Allow to manually call output render functions

Open dipterix opened this issue 3 years ago • 4 comments

Dear devs,

I was wondering if shiny can provide functions to call output renderers manually without knowing the expressions inside of the render calls. For example, something like shiny::renderOutput as follows:

# server:
function(input, output, session) {

  output$plot1 <- shiny::renderPlot({ ... })

  output$download <- shiny::downloadHandler(
    ...,
    content = function(con) {
        # Re-render plot1 without knowing the expression
        pdf(...)
        shiny::renderOutput(output, "download")
        dev.off()
    }
  )

}

Also it would be nice if shiny::outputOptions can add arbitrary (customized) key-value pairs, so we can store additional information needed (for example the arguments needed by pdf() function.

I understand that there could be lots of things to consider (such as potential side effects). However, it would be neat if there is such function, as we can programmatically write widget functions for outputs. For example,

  • downloading graphs, saving HTML widgets (without writing handlers),
  • embed an output in another session/output zoomed in or as a sub-plot,
  • allow a third person to add additional processing/extract information from output results (from ggplot2 object, or htmlwidget)

My attempts

  1. For some outputs, I am able to extract the expression from cacheHint attribute of shiny.render.function objects. However, if cacheHint is set to false in markRenderFunction (such as DT::renderDT), I couldn't obtain any expressions.

  2. Another attempt is to directly call shiny.render.function with a given session and output. However, that only gives me processed results instead of what's generated from expr. For example, renderPlot returns the base64-encoded image, and I cannot set the width nor height.

dipterix avatar Jul 14 '22 21:07 dipterix

I think you may be able to achieve your intended goal with adding an extra level of indirection. In your example there is a mystery expression inside of a renderPlot({}); I suggest that however you have architected that to come about, have it populate a reactive() instead, then this reactive can itself be used as a parameter into a renderPlot as well as to a downloadHandler.

nir4most avatar Aug 01 '22 10:08 nir4most

@dipterix : When I have different types of Output I use shiny::renderUI() / shiny::uiOutput().

philibe avatar Aug 01 '22 15:08 philibe

Thanks @nir4most that's exactly what I was doing. The reason why I posted this issue was because I think it would be easier if the render functions can store the expression as one of its attributes, such that the add-ons can work without having users to change their existing code.

Also I can render directly with shiny.render.function. However, the render functions might not return the results that I want. For example, renderPlot returns base64 encoded images, and I cannot capture the image with graphic devices such as pdf, nor can I add pre/post-processing to the images (add lines, legend, change palettes)

dipterix avatar Aug 03 '22 17:08 dipterix

@philibe , That's not gonna solve my issues. I don't need multiple output support. I need ways to get renderers' expression and program on the context so I can provide add-ons to any shiny outputs without knowing the details of implementation.

dipterix avatar Aug 03 '22 18:08 dipterix