Hi, I’m trying to make my plots in a DynamicMap differ I size, depending on which plot is selected. However, the straightforward way using different size values for width and height does not work, even if I specify framewise=True. I have also tried with a hook, to see if overriding the bokeh setting before plotting could change the size of the plot.
%load_ext watermark
import holoviews as hv
import numpy as np
import pandas as pd
import panel as pn
hv.extension('bokeh')
print('Watermark of notebook execution:')
%watermark -iv
select_widget = pn.widgets.Checkbox(name='select')
@pn.depends(select=select_widget.param.value)
def get_points(select):
def column_width(plot, element):
size = 200 if select else 400
print(size)
plot.handles['plot'].plot_width = size
if select:
return hv.Points(np.random.rand(10,10)).opts(title='A', hooks=[column_width], framewise=True)
else:
return hv.Points(np.random.rand(10,10)).opts(title='B', hooks=[column_width], framewise=True)
plot = hv.DynamicMap(get_points)
app = pn.Column(select_widget, plot)
app
Screenshots
The initial plot followed by the plot after selection, where the size of the plot have not changed!
One solution is to make the hv.Points and hv.DynamicMap responsive, meaning they fill the size of their container (Plotting with Bokeh), and put the DynamicMap inside a panel object, like a Column. Then when the button is checked, update the Column size and the contents will adapt.
This example code is organized using a parametrized class as in Param.
Example code
import holoviews as hv
import numpy as np
import panel as pn
import param as pm
hv.extension('bokeh')
class PointPlotter(pm.Parameterized):
select = pm.Boolean(False)
size = pm.Integer(400, bounds=(1, 1000), precedence=-1)
height = pm.Integer(400, precedence=-1, constant=True)
output_panel = pm.ClassSelector(class_ = pn.layout.Panel, precedence=-1)
@pm.depends('select', watch=True)
def update_size(self):
self.size = 200 if self.select else 400
self.output_panel.width = self.size
# @pm.depends('size') # Uncomment if you want the plot contents refreshed as well as the plot resized
def get_points(self):
return hv.Points(np.random.rand(10, 10)).opts(title='A', height=self.height, responsive=True)
def view(self):
return hv.DynamicMap(self.get_points).opts(responsive=True)
def panel(self):
self.output_panel = pn.Column(self.view(), width_policy='fixed', width=self.size)
return self.output_panel
p = PointPlotter()
app = pn.Column(p.param, p.panel())
app.servable()