bokeh icon indicating copy to clipboard operation
bokeh copied to clipboard

Document width_policy, heigh_policy

Open bryevdv opened this issue 5 years ago • 3 comments

#4407 was closed in the large layout PR work, but some of the features such as width_policy and height_policy never got documented fully, or examples added.

bryevdv avatar Aug 16 '20 02:08 bryevdv

As requested, here is a complete example for width_policy and heigth_policy in action. Happy to help!

"""Example for Bokeh width_policy and heigth_policy.

We want to create a 2x2 gridplot that stretches with the browser window.
Each figure has a RangeTool below that should stay at a fixed height.
"""
import numpy as np
from bokeh.models import ColumnDataSource, RangeTool
from bokeh.plotting import figure, output_file, show
from bokeh.layouts import column, gridplot
from bokeh.models.ranges import Range1d


def get_select_RangeTool(p, x, y, source):
    """Return a figure that uses the range_tool to control the figure p."""
    select = figure(plot_height=45, y_range=p.y_range, y_axis_type=None,
                    tools="", toolbar_location=None,
                    background_fill_color="#efefef",
                    # This is where the magic happens:
                    height_policy="fixed", width_policy="fit",
                    )
    select.line(x, y, source=source)

    # Create a RangeTool that will be added to the "select" figure
    range_tool = RangeTool(x_range=p.x_range)  # Link figure and RangeTool
    range_tool.overlay.fill_color = "navy"
    range_tool.overlay.fill_alpha = 0.25

    select.ygrid.grid_line_color = None
    select.add_tools(range_tool)
    select.toolbar.active_multi = range_tool

    return select


def get_figure_with_select():
    """Return a column of a figure with its select RangeTool."""
    # Create data
    x = np.linspace(0, 4*np.pi)
    y = np.sin(x)
    source = ColumnDataSource(data=dict(x=x, y=y))

    # Set up plot
    p = figure()
    p.x_range = Range1d(min(x), max(x))  # Bug? This should not be necessary!
    p.line('x', 'y', source=source)
    select = get_select_RangeTool(p, 'x', 'y', source)
    return column(p, select, sizing_mode='stretch_both')


# Create four figures to add to a 2x2 gridplot
fig_list = [get_figure_with_select(), get_figure_with_select(),
            get_figure_with_select(), get_figure_with_select()]
grid = gridplot(fig_list, ncols=2, sizing_mode='stretch_both')
# Create output
output_file('Example_height_policy.html', title='Example height_policy')
show(grid)

As a bonus, there is a bug report hidden in there. Without the line

p.x_range = Range1d(min(x), max(x))  # Bug? This should not be necessary!

...this error comes up:

range_tool = RangeTool(x_range=p.x_range)  # Link figure and RangeTool
ValueError: expected an instance of type Range1d, got DataRange1d(id='8713', ...) of type DataRange1d

At least this seems like a bug to me. Should RangeTool not be able to accept DataRange1d? Hacking the Range of the original figure to Range1d is a really weird workaround.

Should I open a new issue about this? Regards!

jnettels avatar Aug 16 '20 08:08 jnettels

At least this seems like a bug to me. Should RangeTool not be able to accept DataRange1d?

They are a bit at odds. A DataRange1d is for automatically setting the range extent based on the available data, and a range tool is about explicitly controlling the range extent, completely ignoring the data.

bryevdv avatar Aug 17 '20 03:08 bryevdv

given this is layout-related and layout is being revamped for 3.x, I am going to kick this down the road cc @tcmetzger @mattpap

bryevdv avatar Aug 26 '21 20:08 bryevdv