Is is possible to link an plot to a widget, or to set an 'on_selection_event' on plot?

Hello everyone,

I wanted to ask if it was possible to fill a param.multiselect widget by using the selection on a plot ?
Is there a existing mechanism like an “on_selection(func)” event or something.
It will be great if I could link my plot to my widget, but just having the value and an event mechanism will be enough to do what I want.

Thanks in advance

What do you want to select on your plot? A point/line/box…?

A box, by using the tool box_select !

This little app allows you to select points on a plot with a box_select and it reacts to this new selection by selecting the widget values (I’ve displayed their index).

import numpy as np
import panel as pn
import param
import holoviews as hv

hv.extension('bokeh')

class App(pn.viewable.Viewer):
    
    points_selection = param.ListSelector(objects=list(range(10)))
      
    def __init__(self, **params):
        super().__init__(**params)
        self.points = hv.Points(np.random.multivariate_normal((0, 0), [[1, 0.1], [0.1, 1]], (10,)))
        sel = hv.streams.Selection1D(source=self.points)
        sel.add_subscriber(self.update_points_selection)
    
    def update_points_selection(self, index):
        self.points_selection = index
    
    def __panel__(self):
        return pn.Row(
            pn.widgets.MultiSelect.from_param(self.param.points_selection, sizing_mode='stretch_height'),
            self.points.opts(tools=['box_select'], active_tools=['box_select'])
    )

App()

image

It’s using a few things:

  • One of the linked streams offered by holoviews, here a Selection1D that reacts on its source plot. See the user guide on custom interactivity with bokeh plots. Streams have a add_subscriber method that allows you that add any callback that you want. I’ve used that to update the list of selected points.
  • The app is a subclass pn.viewable.Viewer and implements def __panel__(self). This is a nice way to create your own panel components. Here you’ll notice that the app itself is rendered as Panel dashboard in the notebook :slight_smile:
  • pn.widgets.MultiSelect.from_param that will create a MultiSelect widget linked to the Parameter points_selection.
2 Likes

awesome @maximlt :slight_smile:

An app version could look something like this

import numpy as np
import panel as pn
import param
import holoviews as hv

pn.extension(sizing_mode="stretch_width")
hv.extension('bokeh')

ACCENT_COLOR = "#0072B5"

class App(pn.viewable.Viewer):

    points_selection = param.ListSelector(objects=list(range(10)))

    def __init__(self, **params):
        super().__init__(**params)
        self.points = hv.Points(np.random.multivariate_normal((0, 0), [[1, 0.1], [0.1, 1]], (10,))).opts(color=ACCENT_COLOR, size=8, responsive=True)
        sel = hv.streams.Selection1D(source=self.points)
        sel.add_subscriber(self.update_points_selection)

    def update_points_selection(self, index):
        self.points_selection = index

    def __panel__(self):
        return pn.Row(
            pn.widgets.MultiSelect.from_param(self.param.points_selection, sizing_mode='stretch_height'),
            self.points.opts(tools=['box_select'], active_tools=['box_select']), sizing_mode="stretch_both"
    )

template = pn.template.FastGridTemplate(
    site="Panel", title="Linked Plots and Widgets", main=[App()], accent_base_color=ACCENT_COLOR, header_background=ACCENT_COLOR, row_height=300
).servable()
3 Likes