Lock plot aspect ratio for resizing browser window

Hello,

I’m working on a project using Bokeh 3.2.1 where I have a layout with multiple plots and widgets organized in multiple columns and rows. I would like all the plots to use as much space as they can while being scaled to maintain their aspect ratio whenever I resize the browser window, but haven’t managed to do so.

Based on the documentation, I thought sizing_mode = scale_both and/or aspect_ratio = 1.0 would do the trick, but apparently not.

I tried to create a minimal example below; please note that I use pyscript, so the code below can be saved as html which can be opened in a browser to test.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-3.2.1.js"></script>
    <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.2.1.min.js"></script>
    <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.2.1.min.js"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@holoviz/[email protected]/dist/panel.min.js"></script>

    <link rel="stylesheet" href="https://pyscript.net/releases/2022.12.1/pyscript.css" />
    <script defer src="https://pyscript.net/releases/2022.12.1/pyscript.js"></script>
  </head>
  <style>
    html,body{
        height: 100%;
        min-height: 100%;
        padding: 0;
        margin: 0;
    }
    .maindiv {
        height: 100%;
        min-height: 100%;
        padding: 0;
        margin: 0;
    }
</style>
  <body>
    <py-config>
       packages = [
          "https://cdn.holoviz.org/panel/1.2.1/dist/wheels/bokeh-3.2.1-py3-none-any.whl",
          "https://cdn.holoviz.org/panel/1.2.1/dist/wheels/panel-1.2.1-py3-none-any.whl",
       ]
    </py-config>
    <div id="simple_app" class="maindiv"></div>
    <py-script>
      import panel as pn

      pn.extension()

      from bokeh.plotting import figure
      from bokeh.layouts import column, row
      from bokeh.models import Div
      
      p1 = figure(toolbar_location=None, aspect_ratio=1.0, sizing_mode="scale_both")
      p1.line([0, 1], [0, 1])
      
      div1 = Div(text='', width=500)

      bokeh_app = pn.pane.Bokeh(row(column(p1, sizing_mode="scale_both"), column(div1, background='red'), sizing_mode="stretch_both"))
      
      pn.Row(bokeh_app).servable(target='simple_app')
      
    </py-script>
  </body>
</html>

Basically, with the above, the aspect ratio of the plot is not maintained, the plot just stretches both horizontally and vertically.

When I set sizing_mode=“scale_width” for the plot instead, the aspect ratio is maintained if the parent column is higher than it is wide; however, when I resize the browser window such that the parent column is wider than it is high, then the aspect ratio is again not maintained and the plot uses as much width as possible whereas the height is being reduced.

The behavior I would expect is that, if the parent column is wider than it is high, the plot would be scaled such that the maximum height is being used and the width scaled such that the aspect ratio is maintained (i.e., the width will be scaled to match the height to maintain an aspect ratio of 1.0).

I hope I’m just missing something trivial!

These options adjust the range start/end values automatically to keep the “data” aspect ratio in sync witht the “physical pixel” aspect ratio. They cause a reaction to update the axes extents, based on how the actual canvas size might change. I think you are expecting the converse, i.e. something that will control the physical pixel dimensions of the canvas? They don’t do that, at all.

When I set sizing_mode=“scale_width” for the plot instead, the aspect ratio is maintained if the parent column is higher than it is wide; however, when I resize the browser window such that the parent column is wider than it is high, then the aspect ratio is again not maintained and the plot uses as much width as possible whereas the height is being reduced.

Seems like a possible bug, I don’ have any suggestions other than to open a GitHub Issue with full details.

All that said, I think scale_width may only control the pixel aspect ratio of the entire plot canvas, but presumably you are actually concerned with the pixel aspect ratio of the internal “frame” between the axes. There are presently not really any mechanisms to control that that I am aware of. You’d still want to set match_aspect which would keep the data/pixel ratios in sync, by updating the data range extents as the frame pixel dimensions may change slightly depending on axes label sizes, or other small things. If you truly need super fine control over the internal frame pixel aspect ratio then Bokeh is probably not suitable to your case.

Ah sorry I wasn’t clear enough: I am indeed only concerned about the aspect ratio of the entire plot canvas. As far as I have understood the documentation, the sizing_mode keyword argument for figure should control the component’s width and height (not the axes ranges or similar), and that’s exactly what I want. It just doesn’t appear to behave as described. Whenever using a sizing_mode of scale_width, scale_height, or scale_both, the component’s aspect ratio should never be changed.

As I said, it sounds like a bug.

BTW looking at your actual code you do not appear to have set match_aspect=True. The aspect_ratio value only has an effect when aspect-matching is enabled (but again, these settings only affect the axes start/end values, and do not affect any pixel dimensions).

Thank you, I also tried both with and without match_aspect = True already.

However, I still don’t understand why these settings are supposed to affect the axes start/end values. The documentation for sizing_mode for figure says:

This is a high-level setting for maintaining width and height of the component.

For the aspect_ratio keyword argument it says:

Describes the proportional relationship between component’s width and height.

And for the width and height keyword arguments, it says:

The width of the component (in pixels).

The height of the component (in pixels).

To me it couldn’t be clearer that these settings affect the pixel dimensions. If that’s indeed not the case, then the documentation seems very misleading.

My apologies, it’s aspect_scale that is specific to plots, and works together with match_aspect. I guess aspect_ratio is a generic property for all layoutable objects that was added recently (I have no personal experience with it).

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.