How do you set the height of a plot when using hvplot, panel.ReactiveExpr and pn.panel? I’ve tried to insert height argument to multiple places but they do not seem to have any effect.
- The panel.ReactiveExpr mention
widget_layout but I’m not sure if you can pass height there somehow
- The
.opts(height=800) I took from Holoviews Customizing Plots. I’m not sure if these are holoviews objects but it has .opts function which accepts args. That does not seem to have any effect, though.
- I also tried giving args to
pn.panel just because it is the last step which creates the graph. It also takes sizing_mode="strecth_width", but not height does not have any effect. This by the way accepts height arguments which works if the input is not ReactiveExpr but simply combined_scatter = data1.hvplot.scatter() * data2.hvplot.scatter() (where data1 and data2 are pandas Series). Could not find the docs for this one.
Code for MWE
import hvplot.pandas
import pandas as pd
import numpy as np
import panel as pn
pn.extension(sizing_mode="stretch_width", template="fast")
data1 = pn.rx(pd.DataFrame(dict(a=np.random.randn(100), b=np.random.randn(100))))
data2 = pn.rx(pd.DataFrame(dict(a=np.random.randn(100), b=np.random.randn(100))))
slider = pn.widgets.IntRangeSlider(
name="rande for b",
start=0,
end=100,
value=(0, 100),
step=10,
).servable(area="sidebar")
data1 = data1[data1["b"].between(slider.value[0], slider.value[1])]["a"]
data2 = data2[data2["b"].between(slider.value[0], slider.value[1])]["a"]
combined_scatter = data1.hvplot.scatter() * data2.hvplot.scatter()
combined_scatter.opts(height=800) # has no effect
combined_plot = pn.ReactiveExpr(
combined_scatter,
height=800, # has no effect
)
graph_panel = pn.panel(
combined_plot,
sizing_mode="stretch_width",
height=800, # has no effect
)
graph_panel.servable(
title="My Title",
# does not take height argument
)
This produces
Question(s)
- How to set the height of the plot?
- why
pn.panel does not accept, or ignores, height with interactive graphs? (but works well if the graph is not tied to any widget)
Okay, some progress. Seems that the .opts has no effect as the combined_scatter is of type param.reactive.rx (created with panel.rx). But there is a private attribute called ._current which stores the Overlay object, which has the opts() method which can be called and works.
>>> combined_scatter
:Overlay
.Scatter.A.I :Scatter [index] (a)
.Scatter.A.II :Scatter [index] (a)
>>> type(combined_scatter)
param.reactive.rx
>>> combined_scatter._current
:Overlay
.Scatter.A.I :Scatter [index] (a)
.Scatter.A.II :Scatter [index] (a)
>>> type(combined_scatter._current)
holoviews.core.overlay.Overlay
Therefore, using
combined_scatter._current.opts(height=800)
instead of
combined_scatter.opts(height=800)
works. Now I get a graph of height 800:
But I feel a bit dirty using a private attribute. What would be the correct way to accomplish this?
Adding some debugging details
Edit: I can also see that the ReactiveExpr object has a layout. It even has a height of 810 but for some reason that height is not used after passing the object to pn.panel.
>>> type(combined_plot.layout)
panel.layout.base.Row
>>> combined_plot.layout
Row(height=810, sizing_mode='stretch_width')
[0] Row(sizing_mode='stretch_width')
[0] ParamFunction(function, _pane=HoloViews, defer_load=False, sizing_mode='stretch_width')
>>> combined_plot.layout.height
810
What does pn.panel do?
- It should return a displayable Panel object.
- pn.panel does nothing to
panel.param.ReactiveExpr objects, as can seen in panel/pane/base.py which starts with:
if isinstance(obj, (Viewable, ServableMixin)):
return obj
The ReactiveExpr is subclass of both Viewable and ServableMixin. Therefore:
>>> combined_plot is pn.panel(
combined_plot,
sizing_mode="stretch_width",
)
True
What does the height in pn.ReactiveExpr do?
- This has some effect. It changes the outer
div.bk-Row height in the html (which will be height+10). So, setting height=455 gives an outer div with height of 465 (always height+10):
- Then there is another
div.bk-Row which has always height of 310:
- This in turn has
bk-panel-models-layout-Column inside it with height of 310, and this has div.bk-Figure inside of it with always height of 300 and margin of 5.
The combined_scatter , which is of type param.reactive.rx, created in
combined_scatter = data1.hvplot.scatter() * data2.hvplot.scatter()
has also a public method called register_display_handler. I’m not sure if that’s the supposed to be used, but this seems to also do the trick:
def display_handler(overlay, **_):
overlay.opts(height=800)
return overlay
combined_scatter.register_display_handler(
lambda x: isinstance(x, holoviews.core.overlay.Overlay),
display_handler,
)
And I’m not sure if the _display_handlers are meant to be copied over to new rx instances (for example when it’s cloned). If not, the above could be also:
def display_handler(overlay, **_):
overlay.opts(height=800)
return overlay
combined_scatter.register_display_handler(
lambda x: True,
display_handler,
)
What I do not understand is that why the
combined_scatter.opts(height=800)
call does not work directly. Could someone confirm that (1) should the combined_scatter.opts(height=800) call work? (2) What is the API that is expected to be used for setting height for an interactive graph?