I feel as though this is a bug, but wanted to first post here to discuss since it is possible I am not using the new layout engine properly. I am struggling to get a responsive DynamicMap
to maintain an equal aspect ratio through instantiation and updates. I would like the visualization to fill the available space, but maintain an equal/square aspect ratio (and given we are working on portrait oriented monitors, this really just means fill/scale width).
I have a feeling I might be encountering multiple related issues, but it is not clear to me what I should try to be doing to get the desired effect. Responsive behavior was as expected with panel v0.14.4
.
- panel==1.0.2
- bokeh==3.1.1
- holoviews==1.16.0
- param==1.13.0
- python==3.10.10
In the following example I place a simple DynamicMap
within a template both as-is and contained by different layouts. This is not exhaustive, but captures what I am experiencing at the moment. (I apologize for some of the extra clutter here).
The following is shown (pseudo-code):
-
pn.pane.HoloViews(hv.DynamicMap, sizing_mode='scale_width'
) -
pn.pane.HoloViews(hv.DynamicMap
) -
pn.Row(pn.pane.HoloViews(hv.DynamicMap, sizing_mode='scale_width'
)) -
pn.Column(pn.pane.HoloViews(hv.DynamicMap, sizing_mode='scale_width'
)) -
pn.Card(pn.pane.HoloViews(hv.DynamicMap, sizing_mode='scale_width'
)) -
pn.WidgetBox(pn.pane.HoloViews(hv.DynamicMap, sizing_mode='scale_width'
))
Example:
# test.py
import holoviews as hv
import numpy as np
import pandas as pd
import panel as pn
hv.extension('bokeh')
pn.extension()
# set up callback to generate 1000 points and stream to the dynamic map
limits = (0, 100)
btn = pn.widgets.Button(name='Update')
def update(event):
data = pd.DataFrame({
'x': np.random.randint(*limits, size=1000),
'y': np.random.randint(*limits, size=1000)
})
data_stream.send(data)
btn.on_click(update)
# plot data frame
def plot(data):
return hv.Points(data, kdims=['x', 'y'])
# set up stream and dynamic map
data_stream = hv.streams.Pipe(data=pd.DataFrame(columns=['x', 'y']))
points = hv.DynamicMap(plot, streams=[data_stream]).opts(
default_tools=[], # not needed, but makes scrolling easier when viewing this example
xlim=limits, # needed to give initial shape to the canvas
ylim=limits, # needed to give initial shape to the canvas
responsive=True,
data_aspect=1,
aspect='square' # needed, possibly related to https://github.com/holoviz/holoviews/issues/5425
)
hv_pane = pn.pane.HoloViews(
points,
sizing_mode='scale_width'
)
hv_pane_no_sizing_mode = pn.pane.HoloViews(
points
)
pn.template.MaterialTemplate(
main=[
btn,
# ----------
pn.panel('# Not contained'),
hv_pane, # works!
# ----------
pn.panel('# Not contained, no sizing mode'),
hv_pane_no_sizing_mode, # responsive + update works, but aspect is not respected =(
# ----------
pn.panel('# Row Container'),
pn.Row(
hv_pane # works!
),
# ----------
pn.panel('# Column Container'),
pn.Column(
hv_pane # =(
),
# ----------
pn.panel('# Card Container'),
pn.Card(
hv_pane # =(
),
# ----------
pn.panel('# WidgetBox Container'),
pn.WidgetBox(
hv_pane # =(
)
],
sidebar=[
pn.panel('# Filler!'), # used to enable the sidebar so we can dynamically resize by collapsing it
]
).servable()
panel serve test.py