holoviz / holoviews

With Holoviews, your data visualizes itself.
https://holoviews.org
BSD 3-Clause "New" or "Revised" License
2.69k stars 402 forks source link

Cannot link tap to float slider #6384

Open ahuang11 opened 4 days ago

ahuang11 commented 4 days ago

For widgets, there’s pn.Param and from_param.

What’s the equivalent for HoloViews streams?

https://discourse.holoviz.org/t/whats-the-best-practice-for-linking-up-holoviews-streams-with-parameterized/8215/3

I thought the following would work, but I get TypeError: Constant parameter 'x' cannot be modified

Can we remove the constant=True, or create a dedicated method like from_param or from_params?

image
import param
import holoviews as hv
import panel as pn
pn.extension()

class TapTest(pn.custom.PyComponent):
    x = param.Number(default=0)
    y = param.Number(default=0)

    def __init__(self, **params):
        super().__init__(**params)

        self._tap = hv.streams.Tap()

        hv_pane = pn.pane.HoloViews()
        hv_pane.object = hv.DynamicMap(self.plot, streams=[self._tap])

        x_spinner = pn.widgets.FloatSlider.from_param(self.param.x)
        y_spinner = pn.widgets.FloatSlider.from_param(self.param.y)
        x_spinner.link(self._tap, value='x', bidirectional=True)
        y_spinner.link(self._tap, value='y', bidirectional=True)
        self._layout = pn.Column(hv_pane, x_spinner, y_spinner)

    def plot(self, x, y):
        return hv.Points([(x, y)]).opts(tools=['tap'])

    def __panel__(self):
        return self._layout

pn.serve(TapTest())

The workaround is a bit more code

import param
import holoviews as hv
import panel as pn
pn.extension()

class TapTest(pn.custom.PyComponent):
    x = param.Number(default=0)
    y = param.Number(default=0)

    def __init__(self, **params):
        super().__init__(**params)

        self._tap = hv.streams.Tap()
        hv_pane = pn.pane.HoloViews()
        hv_pane.object = hv.DynamicMap(self.plot, streams=[self._tap])

        pn.bind(self.update_tap, self.param.x, self.param.y, watch=True)

        self._layout = pn.Column(hv_pane, self.param.x, self.param.y)

    def update_tap(self, x, y):
        self._tap.event(x=x, y=y)

    def plot(self, x, y):
        if x and y:
            self.param.update(x=x, y=y)
        return hv.Points([(self.x, self.y)]).opts(tools=['tap'])

    def __panel__(self):
        return self._layout

pn.serve(TapTest())
philippjfr commented 4 days ago

This issue has been mentioned on HoloViz Discourse. There might be relevant details there:

https://discourse.holoviz.org/t/whats-the-best-practice-for-linking-up-holoviews-streams-with-parameterized/8215/4

philippjfr commented 3 days ago

Fully in favor.