How to have DynamicMap plots differ in size

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
print('Watermark of notebook execution:')
%watermark -iv

select_widget = pn.widgets.Checkbox(name='select')

def get_points(select):
    def column_width(plot, element):
        size = 200 if select else 400
        plot.handles['plot'].plot_width = size
    if select:
        return hv.Points(np.random.rand(10,10)).opts(title='A', hooks=[column_width], framewise=True)
        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)

The initial plot followed by the plot after selection, where the size of the plot have not changed!

This issue does relate a bit to # GridSpec sizing breaks when plot in DynamicMap updates

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


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 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())

@mmorys, Thanks a lot for your help. Your solution works. :+1: