Increase the resolution/scale with the default "Save" button on the toolbar

I’d like to automatically increase the scale_factor (well I’m after an increased resolution or DPI in general) of my plots when clicking the “Save” button via the Bokeh toolbar on the plot.

Clicking the “Save” button as of present saves image of exactly how it appears on screen. However, when saving a plot, I want this to be a near-publication quality export.

I’m aware of export_png and its scale_factor argument, but this doesn’t feel right to me as I need to install a headless browser to manually execute this (firefox and geckodriver). It feels weird to me to implement a hook that will then re-plot in a headless browser, server side when this could be done directly in the client browser… but maybe that’s the way it needs to be done?

I’m wondering if there is a way to tell bokeh to increase the scale_factor to some default value so that I can get improved resolutions of my saved plots when clicking the default “Save” button.

Increase the width/height of the plot in my UI isn’t really a solution either as I really want to upscale it or increase the DPI while maintaining relative font size to the plot itself.

import numpy as np

from bokeh.plotting import figure, show

rng = np.random.default_rng()
x = rng.normal(loc=0, scale=1, size=1000)

p = figure(width=670, height=400,
           title="Normal (Gaussian) Distribution")

# Histogram
bins = np.linspace(-3, 3, 40)
hist, edges = np.histogram(x, density=True, bins=bins)
p.quad(top=hist, bottom=0, left=edges[:-1], right=edges[1:],
         fill_color="skyblue", line_color="white",
         legend_label="1000 random samples")

# Probability density function
x = np.linspace(-3.0, 3.0, 100)
pdf = np.exp(-0.5*x**2) / np.sqrt(2.0*np.pi)
p.line(x, pdf, line_width=2, line_color="navy",
       legend_label="Probability Density Function")

p.y_range.start = 0
p.xaxis.axis_label = "x"
p.yaxis.axis_label = "PDF(x)"


Export I get from the Save button:

bokeh_plot (1)

If I try to put that plot in a presentation or document, its quite low resolution

The following gives me the desired effect, but I really want this to be handled by the “Save” button… and I just noticed this includes the toolbar which I don’t want in the screenshot

from import export_png
export_png(p, filename='foo.png', scale_factor=3)
1 Like

All of the work of rendering anything at all is only actually done by JavaScript code, so yes, running in some kind of browser really is the only option. [1]

The SaveTool and export_png work in very different ways. The export_png function drives a browser using Selenium then basically takes a screenshot programmatically. As part of that, it has the opportunity to do things like e.g. set the scale factor, because Selenium provides APIs that can control the browser at that level.

By contrast, the (much, much older and simpler) SaveTool just calls a basic JavaScript function that dumps the current contents of an HTML canvas as a binary blob. There is no JavaScript API to change the scale factor that I am aware of. Overall scale factor is a property of the running browser outside the JavaScript engine (which is why Selenium can control it). But what this means is that AFAIK there is simply no way to accomplish what you are asking for.

IMO the solution is to use export_png. Generating images programmatically from Python is exactly what it was developed for (at great effort after many years of requests, mostly from users wanting exports for publication).

  1. Well, in principle someone could re-implement all of BokehJS rendering in something else like Cairo or Agg (or whatever). But that would almost certainly take a few person-years worth of effort so unless you know someone with very deep pockets willing to fund such work directly, there’s really no chance that would ever get undertaken. ↩︎

1 Like

Thanks @Bryan! This is incredibly insightful!

Aha, I think this is the critical context I was missing. So in order to adjust the scale factor at all, we basically need to spin up a new browser

I’ll stick with this and perhaps add my own custom button to do this.

I suspect I’ll need to do a few things for a custom “Save” button that increases the scale factor:

  1. Add a new button to the toolbar that triggers a Python callback (I’ve seen examples of this done before)
  2. Install the browser dependencies server-side and execute export_png with the desired scale_factor
  3. Serialize the saved PNG and send it back to the client
  4. Download the received PNG

Steps 3 and 4 are the daunting part for me being new to Bokeh and Panel (where I’m using Bokeh) so if anyone feels like chiming in with a snippet on how to tackle those parts, it would be greatly appreciated!

1 Like

The Panel folks might be interested in streamlining something like that from their higher level vantage point (or for that matter maybe they even already have something I don’t know about). FWIW they do have their own Discourse instance so it might also be useful to ask about the simplest workflow for this using Panel there:

1 Like

I was originally going to raise the issue there but as I was digging, I thought this might be possible in Bokeh directly. With the context you’ve given me, I’ll open a topic on the Panel discourse about adding a custom save button there to do just this.

Will cross-link when I’ve posted

1 Like

@banesullivan-kb, I am grateful that you opened this topic because I have exactly the same concern and was postponing the work on my to-do-list.
Thanks also to @Bryan for the explanation.

For 2. and 3. I guess that get_screenshot_as_png() could do the job, although I didn’t try it myself yet.

For the download part 4., have a look at that topic in which a html file created with file_html() is downloaded:

1 Like