How to create a self-contained, custom "Panel"?

Solution - Inheritance - Works in Panel 0.9.7

This works and can be instantiated and used as simple as MyCustomWidget(df). :+1:

custom-component

import matplotlib.pyplot as plt
import pandas as pd
import panel as pn
import param
import seaborn as sns


class MyCustomWidget(pn.Column):
    column = param.Selector()

    def __init__(self, data, **params):
        self._rename["column"] = None
        # Note:
        # I need to add _rename of `column` in order to not get the exception
        # AttributeError: unexpected attribute 'column' to Column, possible attributes are align, aspect_ratio, background, children, css_classes, disabled, height, height_policy, js_event_callbacks, js_property_callbacks, margin, max_height, max_width, min_height, min_width, name, rows, sizing_mode, spacing, subscribed_events, tags, visible, width or width_policy
        # The reason is that the code of Column tries to map every parameter on the class to a property on the underlying bokeh model.
        # and `column` is not a property on the underlying bokeh model.

        super().__init__(**params)

        self._plot_pane = pn.pane.Matplotlib(background="blue", width=300, height=300)
        self[:] = [self.param.column, self._plot_pane]

        self.param.watch(self._update_plot_pane, "column")
        # Note: Setting @param.depends("column", watch=True) on _update_plot_pane does not work.

        columns = data.columns.values
        self.param.column.objects = columns
        self.column = columns[0]
        # Note: I need to set self.column to show a plot initially

    def _update_plot_pane(self, _):
        plt.close()
        # Note:
        # - I get exception if plt.close is below ax line. See https://github.com/holoviz/panel/issues/1482
        # - The plot does not change if I remove plot.close() fully.

        ax = sns.distplot(df[self.column])
        self._plot_pane.object = ax.figure

df = pd.DataFrame(data={"x": [1, 2, 3, 4, 5, 6, 7], "y": [1, 2, 2, 4, 5, 9, 7]})
MyCustomWidget(df).servable()

Pros

  • Simple, natural way of constructing and using an instance of the component

Cons

  • The implementation is not a simple and flexible as it should be.
    • I need to _rename my custom parameters.
    • @param.depends("column", watch=True) is not working.
      • I need to use self.param.watch instead.
    • I cannot use objects like strings or plots in self[:].
      • I need to use layouts, panes or widgets in self[:]
    • The order of watch, plot_pane, self.column= matters.
      • (self.column= should be below the two others).
      • Could lead to confusion and questions from new Panel developers.
  • A user could in fact change the content of the column after construction.

For me this is the way to do it. And the cons above should be removed or minimized.

WHAT DO YOU THINK?

1 Like