Customize widgets inside Param class

Hello,

I want to customize widgets of different Selectors that I create inside a Param inherited class.
The idea is to create a class for the selector and then create a selector object inside a dashboard class as in awesome-panel.

Here is the Selector class :

class TimePeriodSelector(param.Parameterized):
    settings_panel = param.Parameter(doc="A panel containing the settings of the LoadingStyler")
    countries = param.ObjectSelector(default="ALL", objects=["ALL"])
    time_period_type = param.ObjectSelector(default="Month", objects=["Week", "Month", 'Aggregates'])
    time_period = param.ObjectSelector(default="ALL", objects=["ALL"])
    suppliers = param.ObjectSelector(default="ALL", objects=["ALL"])
    channels = param.ObjectSelector(default="ALL", objects=["ALL"])
    sub_channels = param.ObjectSelector(default="ALL", objects=["ALL"])
    classes = param.ObjectSelector(default="ALL", objects=["ALL"])
    categories = param.ObjectSelector(default="ALL", objects=["ALL"])
    sub_categories = param.ObjectSelector(default="ALL", objects=["ALL"])
    brands = param.ObjectSelector(default="ALL", objects=["ALL"])
    cpg_table = param.String()
    cpg_country = param.String()

    panels = param.List()
    loading = param.Boolean(default=False, doc="""Whether or not to show the loading indicator""")

    view = param.Parameter()
    styler = param.ClassSelector(class_=LoadingStyler)

    def __init__(self, BQHandler: BQSalesEvolutionDup, **params):
        self.build_distinct_fields(self.distinct_fields)
        super().__init__(**params)
        self.settings_panel = pn.Param(
            self,
            parameters=[
                "countries",
                "time_period_type",
                "time_period",
                "suppliers",
                "channels",
                "sub_channels",
                "classes",
                "categories",
                "sub_categories",
                "brands"
            ],
            widgets={'countries': pn.widgets.MultiChoice,
                     'time_period_type': pn.widgets.RadioButtonGroup,
                     "time_period": pn.widgets.MultiSelect,
                     "suppliers": pn.widgets.MultiSelect,
                     "channels": pn.widgets.MultiSelect,
                     "sub_channels": pn.widgets.MultiSelect,
                     "classes": pn.widgets.MultiSelect,
                     "categories": pn.widgets.MultiSelect,
                     "sub_categories": pn.widgets.MultiSelect,
                     "brands": pn.widgets.MultiSelect}
        )
        self.style_panel = pn.pane.HTML(sizing_mode="fixed", width=0, height=0, margin=0)

def view():
    """Returns the app in a Template"""
    pn.config.sizing_mode = "stretch_width"
    template = pn.template.FastListTemplate(title="Loading Spinners")
    app = TimePeriodSelector(name="Filters")
    pn.Param(app.param, )
    template.sidebar[:] = [app.settings_panel]
    if not issubclass(template.theme, pn.template.base.DefaultTheme):
        app.styler.background_rgb = (0, 0, 0)
    return template


if __name__.startswith("bokeh"):
    view().servable()

And here is the dashboard class in a dashboard.py file:

class Dashboard(BaseDashboard):
    selector = param.ClassSelector(class_=TimePeriodSelector)
    barplot = param.ClassSelector(class_=OverlappingBarplot)
    panels = param.List()
    """An app which shows the sales evolution"""

    def __init__(self, BQHandler: BQSalesEvolutionDup, **params):
        super().__init__(**params)
        self.selector = TimePeriodSelector(self.BQHandler)
        self.x_axis_data, self.month_year = self.get_x_axis_data()

        # BarPlot
        self.barplot = OverlappingBarplot(x_axis_data=self.x_axis_data, title="Sales", tooltips="""
                     <b>Sales yag: </b> @yag{(0.0 a)}€ <br>
                     <b>Sales current year: </b> @current{(0.0 a)}€
                     """)

        self.plots = [self.barplot]
        self.panels = [
            self.barplot.figure
        ]

        self.settings_panel = pn.Column(
            pn.Param(self, parameters=["refresh_data"]),
            pn.pane.Markdown("## Settings"),
            self.selector.settings_panel,
            width=400,
            sizing_mode="fixed"
        )
        self.main = pn.Column(*self.panels, sizing_mode="stretch_both")
        self._refresh_data()
        self.view = pn.Row(self.main, self.settings_panel)

@site.add(APPLICATION)
def view():
    """Returns the app in a Template"""
    pn.config.sizing_mode = "stretch_width"
    template = pn.template.FastListTemplate(title="Loading Spinners")
    # tenant_id = pn.state.session_args["tenant_id"]
    tenant_id = "101013"
    BQHandler = BQSalesEvolutionDup(tenant_id)
    app = Dashboard(BQHandler, name="Sales Evolution Dashboard")
    template.main[:] = [app.view]
    if not issubclass(template.theme, pn.template.base.DefaultTheme):
        app.styler.background_rgb = (0, 0, 0)
    return app.view


if __name__.startswith("bokeh"):
    view().servable()

When I launch the following command panel serve dashboard.py, I get this error :

File "__init__.py", line 1423, in _validate_value:
raise ValueError("List parameter %r must be a list, not an object of type %s." Traceback (most recent call last):
  [...]
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/param/__init__.py", line 1397, in _validate
    self._validate_value(val, self.allow_None)
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/param/__init__.py", line 1423, in _validate_value
    raise ValueError("List parameter %r must be a list, not an object of type %s."
ValueError: List parameter 'value' must be a list, not an object of type <class 'str'>.

Things I have already tried :

  • Remove the widgets dict from the panel.settings variable and put it in pn.param() in the view function → I get no errors but the selectors are not customized.

Am I doing something wrong ?

It seems like ObjectSelector is deprecated and I suggest using either ListSelector or a simple Selector for your purpose.

1 Like