posit-dev / great-tables

Make awesome display tables using Python.
https://posit-dev.github.io/great-tables/
MIT License
1.76k stars 56 forks source link

Using Javascript html2canvas to save the plot instead of selenium. #402

Closed Filhomn closed 1 month ago

Filhomn commented 1 month ago

Prework

Using Javascript html2canvas is a better solution to .save() the plots… It is faster, it is frontend…. Works better with servers…. Does not require to install any webdrivers…. Easily exports to any format .png .jpeg .svg

A simple example using Shiny

This could be adapted to Great_tables

`` from shiny import App, ui, reactive, render

app_ui = ui.page_fluid( ui.layout_columns( ui.card( ui.tags.div("Your content here",id="rr"), # Content to be captured ui.input_action_button('btn22', "Capture Canvas"), ui.output_text("tt") ), ui.card( "Other content", ui.tags.div("Some other content here", id="data_table3") ) ),

Include html2canvas library

ui.tags.script(src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.3.2/html2canvas.min.js"),
# Include a script to define the captureCanvas function
ui.tags.script(
"""
function captureCanvas() {
    html2canvas(document.getElementById('rr')).then(function(canvas) {
        var imgData = canvas.toDataURL('image/png');
        Shiny.setInputValue('imgData', imgData);

        // Create a link element to download the image
        var link = document.createElement('a');
        link.href = imgData;
        link.download = 'canvas_image.png';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    });
}
document.getElementById('btn22').addEventListener('click', captureCanvas);
"""

) )

def server(input, output, session): @output @render.text def tt(): return input.imgData() if input.imgData() else "No image data captured yet."

Create the Shiny app

app = App(app_ui, server)

if name == "main": app.run() ``

jrycw commented 1 month ago

I reformatted the code to make it easier to read on GitHub.

from shiny import App, reactive, render, ui

app_ui = ui.page_fluid(
    ui.layout_columns(
        ui.card(
            # Content to be captured
            ui.tags.div("Your content here", id="rr"),
            ui.input_action_button("btn22", "Capture Canvas"),
            ui.output_text("tt"),
        ),
        ui.card(
            "Other content", ui.tags.div("Some other content here", id="data_table3")
        ),
    ),
    # Include html2canvas library
    ui.tags.script(
        src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.3.2/html2canvas.min.js"
    ),
    # Include a script to define the captureCanvas function
    ui.tags.script(
        """
        function captureCanvas() {
            html2canvas(document.getElementById('rr')).then(function(canvas) {
                var imgData = canvas.toDataURL('image/png');
                Shiny.setInputValue('imgData', imgData);

                // Create a link element to download the image
                var link = document.createElement('a');
                link.href = imgData;
                link.download = 'canvas_image.png';
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
            });
        }
        document.getElementById('btn22').addEventListener('click', captureCanvas);
        """
    ),
)

def server(input, output, session):
    @output
    @render.text
    def tt():
        return input.imgData() if input.imgData() else "No image data captured yet."

# Create the Shiny app
app = App(app_ui, server)

if __name__ == "__main__":
    app.run()
machow commented 1 month ago

Thanks for raising @Filhomn (and for the formatting @jrycw!), and for the illustration of html2canvas. Can you say more about how this might work? Users of Great Tables could be in these places:

Do you have a sense for how it might save tables to e.g. pngs from these environments?

Filhomn commented 1 month ago

The output from great_table is an html table right? So it is possible to call JavaScript from Jupyter or simply write directly into the html with Githubissues.

  • Githubissues is a development platform for aggregating issues.