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?
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';