From 32b4a329dc004f3107a011868b1885f3a16043e1 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Wed, 30 Mar 2022 17:02:50 +0200 Subject: [PATCH 1/5] Add throttled options to Widget Variable and Filter --- lumen/filters/base.py | 10 +++++++++- lumen/variables/base.py | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lumen/filters/base.py b/lumen/filters/base.py index c6c51f675..7c0f982dd 100644 --- a/lumen/filters/base.py +++ b/lumen/filters/base.py @@ -213,6 +213,11 @@ class WidgetFilter(BaseWidgetFilter): e.g. for a numeric value this could be a regular slider or a range slider.""") + throttled = param.Boolean(default=True, doc=""" + If the widget has a value_throttled parameter use that instead, + ensuring that no intermediate events are generated, e.g. when + dragging a slider.""") + widget = param.ClassSelector(class_=pn.widgets.Widget) filter_type = 'widget' @@ -228,7 +233,10 @@ def __init__(self, **params): self.widget.name = self.label self.widget.visible = self.visible self.widget.disabled = self.disabled - self.widget.link(self, value='value', visible='visible', disabled='disabled', bidirectional=True) + links = dict(value='value', visible='visible', disabled='disabled') + if self.throttled and 'value_throttled' in self.widget.param: + links['value_throttled'] = links.pop('value') + self.widget.link(self, bidirectional=True, **links) if self.default is not None: self.widget.value = self.default diff --git a/lumen/variables/base.py b/lumen/variables/base.py index 91ddb7518..d3bdce0ed 100644 --- a/lumen/variables/base.py +++ b/lumen/variables/base.py @@ -178,6 +178,11 @@ class Widget(Variable): A Widget variable that updates when the widget value changes. """ + throttled = param.Boolean(default=True, doc=""" + If the widget has a value_throttled parameter use that instead, + ensuring that no intermediate events are generated, e.g. when + dragging a slider.""") + variable_type = 'widget' def __init__(self, **params): @@ -202,7 +207,10 @@ def __init__(self, **params): pass deserialized[k] = v self._widget = widget_type(**deserialized) - self._widget.link(self, value='value', bidirectional=True) + if self.throttled and 'value_throttled' in self._widget.param: + self._widget.link(self, value='value_throttled', bidirectional=True) + else: + self._widget.link(self, value='value', bidirectional=True) @property def panel(self): From 7324484d0a8e1ba10c6d09c2a5526723633cbc71 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Wed, 30 Mar 2022 17:30:45 +0200 Subject: [PATCH 2/5] Small fix --- lumen/variables/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lumen/variables/base.py b/lumen/variables/base.py index d3bdce0ed..065ce6b22 100644 --- a/lumen/variables/base.py +++ b/lumen/variables/base.py @@ -208,7 +208,7 @@ def __init__(self, **params): deserialized[k] = v self._widget = widget_type(**deserialized) if self.throttled and 'value_throttled' in self._widget.param: - self._widget.link(self, value='value_throttled', bidirectional=True) + self._widget.link(self, value_throttled='value', bidirectional=True) else: self._widget.link(self, value='value', bidirectional=True) From 554a6bc7cd4d720b0ff8e5531ca08aa226821f5b Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Wed, 30 Mar 2022 18:49:15 +0200 Subject: [PATCH 3/5] Add fixes and tests --- lumen/filters/base.py | 22 +++++++++++---------- lumen/tests/filters/test_base.py | 33 +++++++++++++++++++++++++++++--- lumen/tests/test_variables.py | 17 ++++++++++++++-- lumen/variables/base.py | 3 ++- 4 files changed, 59 insertions(+), 16 deletions(-) diff --git a/lumen/filters/base.py b/lumen/filters/base.py index 7c0f982dd..1fed6f846 100644 --- a/lumen/filters/base.py +++ b/lumen/filters/base.py @@ -176,6 +176,11 @@ class BaseWidgetFilter(Filter): disabled = param.Boolean(default=False, doc=""" Whether the filter should be disabled.""") + throttled = param.Boolean(default=True, doc=""" + If the widget has a value_throttled parameter use that instead, + ensuring that no intermediate events are generated, e.g. when + dragging a slider.""") + visible = param.Boolean(default=True, doc=""" Whether the filter should be visible.""") @@ -193,7 +198,12 @@ def _url_sync_error(self, values): @property def panel(self): widget = self.widget.clone() - self.widget.link(widget, value='value', visible='visible', disabled='disabled', bidirectional=True) + if self.throttled and 'value_throttled' in self.widget.param: + widget.link(self.widget, value_throttled='value') + self.widget.link(widget, value='value') + self.widget.link(widget, visible='visible', disabled='disabled', bidirectional=True) + else: + self.widget.link(widget, value='value', visible='visible', disabled='disabled', bidirectional=True) return widget @@ -213,11 +223,6 @@ class WidgetFilter(BaseWidgetFilter): e.g. for a numeric value this could be a regular slider or a range slider.""") - throttled = param.Boolean(default=True, doc=""" - If the widget has a value_throttled parameter use that instead, - ensuring that no intermediate events are generated, e.g. when - dragging a slider.""") - widget = param.ClassSelector(class_=pn.widgets.Widget) filter_type = 'widget' @@ -233,10 +238,7 @@ def __init__(self, **params): self.widget.name = self.label self.widget.visible = self.visible self.widget.disabled = self.disabled - links = dict(value='value', visible='visible', disabled='disabled') - if self.throttled and 'value_throttled' in self.widget.param: - links['value_throttled'] = links.pop('value') - self.widget.link(self, bidirectional=True, **links) + self.widget.link(self, bidirectional=True, value='value', visible='visible', disabled='disabled') if self.default is not None: self.widget.value = self.default diff --git a/lumen/tests/filters/test_base.py b/lumen/tests/filters/test_base.py index 9fc07dbd9..c5ac7dddb 100644 --- a/lumen/tests/filters/test_base.py +++ b/lumen/tests/filters/test_base.py @@ -1,3 +1,5 @@ +import param + from lumen.filters import Filter @@ -5,9 +7,9 @@ def test_resolve_module_type(): assert Filter._get_type('lumen.filters.base.Filter') is Filter -def test_widget_filter_link(): +def test_widget_filter_link_unthrottled(): wfilter = Filter.from_spec( - {'type': 'widget', 'field': 'test'}, + {'type': 'widget', 'field': 'test', 'throttled': False}, {'example': { 'test': { 'type': 'integer', @@ -19,7 +21,7 @@ def test_widget_filter_link(): widget = wfilter.panel assert widget.value == (0, 2) - + widget.value = (1, 2) assert wfilter.value == (1, 2) @@ -35,3 +37,28 @@ def test_widget_filter_link(): widget.disabled = False assert widget.disabled == False + + +def test_widget_filter_link_throttled(): + wfilter = Filter.from_spec( + {'type': 'widget', 'field': 'test'}, + {'example': { + 'test': { + 'type': 'integer', + 'inclusiveMinimum': 0, + 'inclusiveMaximum': 2 + } + }} + ) + widget = wfilter.panel + + assert widget.value == (0, 2) + + with param.edit_constant(widget): + widget.value_throttled = (1, 2) + + assert wfilter.value == (1, 2) + + wfilter.value = (2, 2) + + assert widget.value == (2, 2) diff --git a/lumen/tests/test_variables.py b/lumen/tests/test_variables.py index 562e872ba..f2db33d85 100644 --- a/lumen/tests/test_variables.py +++ b/lumen/tests/test_variables.py @@ -1,5 +1,7 @@ import os +import param + from panel.widgets import IntSlider from lumen.variables import Variables, Variable @@ -47,11 +49,22 @@ def test_resolve_widget_variable_by_module_ref(): assert isinstance(var._widget, IntSlider) -def test_widget_variable_linking(): - var = Variable.from_spec({'type': 'widget', 'kind': 'IntSlider'}) +def test_widget_variable_linking_unthrottled(): + var = Variable.from_spec({'type': 'widget', 'kind': 'IntSlider', 'throttled': False}) var._widget.value = 3 assert var.value == 3 var.value = 4 assert var._widget.value == 4 + + +def test_widget_variable_linking_unthrottled(): + var = Variable.from_spec({'type': 'widget', 'kind': 'IntSlider'}) + + with param.edit_constant(var._widget): + var._widget.value_throttled = 3 + assert var.value == 3 + + var.value = 4 + assert var._widget.value == 4 diff --git a/lumen/variables/base.py b/lumen/variables/base.py index 065ce6b22..5526be9b9 100644 --- a/lumen/variables/base.py +++ b/lumen/variables/base.py @@ -208,7 +208,8 @@ def __init__(self, **params): deserialized[k] = v self._widget = widget_type(**deserialized) if self.throttled and 'value_throttled' in self._widget.param: - self._widget.link(self, value_throttled='value', bidirectional=True) + self._widget.link(self, value_throttled='value') + self.param.watch(lambda e: self._widget.param.update({'value': e.new}), 'value') else: self._widget.link(self, value='value', bidirectional=True) From 0391abc802479dba7d468e5495f2abaaf8848c93 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Wed, 30 Mar 2022 19:00:48 +0200 Subject: [PATCH 4/5] Fix test --- lumen/tests/test_variables.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lumen/tests/test_variables.py b/lumen/tests/test_variables.py index f2db33d85..695346579 100644 --- a/lumen/tests/test_variables.py +++ b/lumen/tests/test_variables.py @@ -59,11 +59,12 @@ def test_widget_variable_linking_unthrottled(): assert var._widget.value == 4 -def test_widget_variable_linking_unthrottled(): +def test_widget_variable_linking_throttled(): var = Variable.from_spec({'type': 'widget', 'kind': 'IntSlider'}) with param.edit_constant(var._widget): var._widget.value_throttled = 3 + assert var.value == 3 var.value = 4 From 056d98135d3bf23d5c0f918133890f5157146e00 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Wed, 30 Mar 2022 19:16:01 +0200 Subject: [PATCH 5/5] Fix for Widget variable --- lumen/variables/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lumen/variables/base.py b/lumen/variables/base.py index 5526be9b9..93ad34f8a 100644 --- a/lumen/variables/base.py +++ b/lumen/variables/base.py @@ -188,7 +188,8 @@ class Widget(Variable): def __init__(self, **params): default = params.pop('default', None) refs = params.pop('refs', {}) - super().__init__(default=default, refs=refs, name=params.get('name')) + throttled = params.pop('throttled', True) + super().__init__(default=default, refs=refs, name=params.get('name'), throttled=throttled) kind = params.pop('kind', None) if kind is None: raise ValueError("A Widget Variable type must declare the kind of widget.")