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

Save tool in gridplot initiates multiple downloads #8531

Closed
kernc opened this issue Jan 1, 2019 · 6 comments · Fixed by #12219
Closed

Save tool in gridplot initiates multiple downloads #8531

kernc opened this issue Jan 1, 2019 · 6 comments · Fixed by #12219

Comments

@kernc
Copy link
Contributor

kernc commented Jan 1, 2019

ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)

Bokeh 1.0.3
Python 3.5

Description of expected behavior and the observed behavior

Clicking 'save' tool on gridplot() downloads each subplot separately. Preferably, if the tools are merged, so would the plots be merged into a single image.

Complete, minimal, self-contained example code that reproduces the issue

import numpy as np
from bokeh.io import show
from bokeh.layouts import gridplot
from bokeh.plotting import figure


plots = []
for i in range(10):
    p = figure(plot_height=70,
               tools='save')
    p.line(np.arange(10), np.random.random(10))
    plots.append(p)

p = gridplot(plots, ncols=1)
show(p)

Screenshots or screencasts of the bug in action

screenshot_2019-01-01_05-02-18

@bryevdv
Copy link
Member

bryevdv commented Jan 1, 2019

Unfortuntely, it's questionable whether it is feasible to do anything about this. Bokeh renders individual plots on separate HTML canvases, and that is not going to change. The built-in JavaScript APIs only support capturing individual canvases, which is why they all save separately, as you have experienced.

The only other possible avenue I am aware of is html2canvas which can apparently render DOM arbitrary elements to a single canvas/image. However, html2canvas evidently has several limitations, these would need to be surveyed to determine whether it is actually useful for Bokeh's use case or not.

Much more problematic, however, is it's size. It is 162kb minified. That would increase the size of BokehJS core (which is already on the "very large" spectrum of JS libraries) by another full 25 percent. It's simply not justifiable to unconditionally increase the payload by that amount for a single tool that is arguably only used by a relatively small number of users. So any solution would have to find a way to make the save tool behave more like a custom model, so that the payload cost is only paid by those who explicitly ask for it.

In fact, I would say a good first step is for someone interested in this to create a custom extension that uses html2canvas as a proof-of-concept:

https://bokeh.pydata.org/en/latest/docs/user_guide/extensions.html

And, assuming that works out, we might then just decide that the best course is simply to document the custom extension for those that want to use it, rather than adding something directly in the library.

@kernc
Copy link
Contributor Author

kernc commented Jan 1, 2019

If plots are canvases and subplots positions are available (xpos() and ypos() assumed), couldn't something much simpler work, like:

for (let canvas of canvases) {
    data = canvas.getContext().getImageData(0, 0, canvas.width, canvas.height);
    mergedCanvas.getContext().putImageData(data, xpos(canvas), ypos(canvas));
}
// Now download mergedCanvas

@mattpap
Copy link
Contributor

mattpap commented Jan 1, 2019

In principle this could be done this way. One issue is to get grid plot's save tool to exec this code instead of proxying individual save tools. Currently there is no support for grid tools, only for proxied figures' tools.

@bryevdv
Copy link
Member

bryevdv commented Jan 7, 2019

One issue is to get grid plot's save tool to exec this code instead of proxying individual save tools.

This really only affects "actions" so that circumscribes the problem somewhat. An offhand idea would be to allow do to accept an optional argument that allows it to condition on the circumstance. i.e. the grid plot toolbar proxy could pass a callback to do the causes the individual save tools to write to the merged canvas as the correct location, rather than opening a URL with the image data. That would allow the gridplot to coordinate things like the positions at the appropriate level.

@tony-bony
Copy link

It is actually working (at least on Mac) using the export_png() method so I guess it's been already implemented in 1.0.4... See this post

@mattpap
Copy link
Contributor

mattpap commented Feb 12, 2019

export_png() and save tool are two different things. There was no progress on this issue so far.

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

Successfully merging a pull request may close this issue.

4 participants