How to save multiple figures from bokeh html file as separate png figures but download just as one zip file?

Question:

This question is based on How to save the multiple figures in a bokeh gridplot into separate png files?, where the accepted answer already provides working code to save the multiple figures in a bokeh grid plot into separate PNG files.

However, with that answer, a number of png-figures are downloaded, and each png-figure is a separate file. It is more convenient for my users to download all the png-figures just as one zip file.
Could you help me to do it?

Asked By: aura

||

Answers:

Using the accepted answer in the linked post as a starting point, you can modify the javascript to download a zip instead of individual pngs. Here’s one way to do that:

let script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js';
script.onload = function () {
  let shadow_root1 = document.querySelector(".bk-GridPlot");
  let shadow_root2 = shadow_root1.shadowRoot.querySelector(".bk-GridBox");
  let shadow_root3 = shadow_root2.shadowRoot.querySelectorAll(".bk-Figure");

  let urls = [];

  shadow_root3.forEach(figure => {
    let shadow_root4 = figure.shadowRoot.querySelector(".bk-Canvas");
    let canvas = shadow_root4.shadowRoot.querySelector("canvas");
    let url = canvas.toDataURL("image/png");
    urls.push(url);
  });

  let zip = new JSZip();

  let count = 0;
  urls.forEach(url => {
    let xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.responseType = 'blob';
    xhr.onload = function() {
      if (xhr.status === 200) {
        let filename = `CanvasAsImage${++count}.png`;
        zip.file(filename, xhr.response, { binary: true });
        if (count === urls.length) {
          zip.generateAsync({ type: 'blob' }).then(function(content) {
            let downloadLink = document.createElement('a');
            downloadLink.download = 'CanvasAsImage.zip';
            downloadLink.href = URL.createObjectURL(content);
            downloadLink.click();
          });
        }
      }
    };
    xhr.send();
  });
};
document.head.appendChild(script);

EDIT:

To name the pngs according to their titles, a couple small modifications are necessary. First, pass in both p1 and p2 into the callback:

button = Button(label="Save all figures")
callback = CustomJS(
    args=dict(p1=p1, p2=p2),
    code="""

Then, change the filename variable:

let filename = [p1.title.text, p2.title.text][count++] + '.png';
Answered By: Plonetheus
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.