Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add throttled options to Widget Variable and Filter #264

Merged
merged 5 commits into from
Mar 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions lumen/filters/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.""")

Expand All @@ -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


Expand Down Expand Up @@ -228,7 +238,7 @@ 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)
self.widget.link(self, bidirectional=True, value='value', visible='visible', disabled='disabled')
if self.default is not None:
self.widget.value = self.default

Expand Down
33 changes: 30 additions & 3 deletions lumen/tests/filters/test_base.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import param

from lumen.filters import Filter


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',
Expand All @@ -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)
Expand All @@ -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)
18 changes: 16 additions & 2 deletions lumen/tests/test_variables.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import os

import param

from panel.widgets import IntSlider

from lumen.variables import Variables, Variable
Expand Down Expand Up @@ -47,11 +49,23 @@ 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_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
assert var._widget.value == 4
14 changes: 12 additions & 2 deletions lumen/variables/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,12 +178,18 @@ 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):
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.")
Expand All @@ -202,7 +208,11 @@ 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_throttled='value')
self.param.watch(lambda e: self._widget.param.update({'value': e.new}), 'value')
else:
self._widget.link(self, value='value', bidirectional=True)

@property
def panel(self):
Expand Down