From 213e999ea85f96cc44def4208a5eb98a542d8187 Mon Sep 17 00:00:00 2001 From: Simon Hoxbro Date: Sat, 21 Nov 2020 21:12:52 +0100 Subject: [PATCH 1/6] Adding throttled to param --- panel/param.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/panel/param.py b/panel/param.py index 6f922fbdd3..e1fb3d2c75 100644 --- a/panel/param.py +++ b/panel/param.py @@ -114,6 +114,10 @@ class Param(PaneBase): Dictionary of widget overrides, mapping from parameter name to widget class.""") + throttled = param.Boolean(default=False, doc=""" + If a widget has value_throttled as a param it will only report + on mouse up.""") + priority = 0.1 _unpack = True @@ -416,6 +420,8 @@ def event(change): def action(change): value(self.object) watcher = widget.param.watch(action, 'clicks') + elif self.throttled and hasattr(widget, 'value_throttled'): + watcher = widget.param.watch(link_widget, 'value_throttled') else: watcher = widget.param.watch(link_widget, 'value') watchers.append(watcher) From 8f9e3a6e895f337346e0a241ed89d32706a569df Mon Sep 17 00:00:00 2001 From: Simon Hoxbro Date: Sat, 21 Nov 2020 22:07:17 +0100 Subject: [PATCH 2/6] Moved throttled to widgets argument --- panel/param.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/panel/param.py b/panel/param.py index e1fb3d2c75..ae7c12041b 100644 --- a/panel/param.py +++ b/panel/param.py @@ -114,10 +114,6 @@ class Param(PaneBase): Dictionary of widget overrides, mapping from parameter name to widget class.""") - throttled = param.Boolean(default=False, doc=""" - If a widget has value_throttled as a param it will only report - on mouse up.""") - priority = 0.1 _unpack = True @@ -420,7 +416,7 @@ def event(change): def action(change): value(self.object) watcher = widget.param.watch(action, 'clicks') - elif self.throttled and hasattr(widget, 'value_throttled'): + elif kw_widget.get('throttled', False) and hasattr(widget, 'value_throttled'): watcher = widget.param.watch(link_widget, 'value_throttled') else: watcher = widget.param.watch(link_widget, 'value') From 91bd2e33544ef908bbc338a531fa2f29e042ce90 Mon Sep 17 00:00:00 2001 From: Simon Hoxbro Date: Sun, 22 Nov 2020 17:20:02 +0100 Subject: [PATCH 3/6] Added value_throttled to updates --- panel/param.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/panel/param.py b/panel/param.py index ae7c12041b..0a4e2a9142 100644 --- a/panel/param.py +++ b/panel/param.py @@ -466,6 +466,8 @@ def action(event): return else: updates['value'] = change.new + if hasattr(widget, 'value_throttled'): + updates['value_throttled'] = change.new try: self._updating.append(p_name) @@ -474,7 +476,8 @@ def action(event): widget.param.set_param(**updates) widget.param.trigger(*updates) else: - widget.param.set_param(**updates) + with param.edit_constant(widget): + widget.param.set_param(**updates) finally: self._updating.remove(p_name) From 2a918a495f77ae641323ff5e523cfb9daa9182e8 Mon Sep 17 00:00:00 2001 From: Simon Hoxbro Date: Sun, 22 Nov 2020 17:20:32 +0100 Subject: [PATCH 4/6] Added unittest --- panel/tests/test_param.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/panel/tests/test_param.py b/panel/tests/test_param.py index 1a6104cdbd..ad0942ae5e 100644 --- a/panel/tests/test_param.py +++ b/panel/tests/test_param.py @@ -626,6 +626,44 @@ class Test(param.Parameterized): assert number.height == 100 assert isinstance(text, TextInput) + +def test_set_widgets_throttled(document, comm): + class Test(param.Parameterized): + a = param.Number(default=0, bounds=(0, 10), precedence=1) + + test = Test() + pane = Param(test) + + model = pane.get_root(document, comm=comm) + + pane.widgets = {"a": {"throttled": False}} + assert len(model.children) == 2 + _, number = model.children + + number.value = 1 + assert number.value == 1 + # assert number.value_throttled == 1 # Should this work? + assert test.a == 1 + + test.a = 2 + assert number.value == 2 + assert number.value_throttled == 2 + assert test.a == 2 + + pane.widgets = {"a": {"throttled": True}} + _, number = model.children + + number.value_throttled = 3 + # assert number.value == 3 # Should this work? + assert number.value_throttled == 3 + assert test.a == 3 + + test.a = 4 + assert number.value == 4 + assert number.value_throttled == 4 + assert test.a == 4 + + def test_set_show_name(document, comm): class Test(param.Parameterized): a = param.Number(bounds=(0, 10)) From 5792920b32cdf951033be5215fa4c5cf5fe892b2 Mon Sep 17 00:00:00 2001 From: Simon Hoxbro Date: Tue, 24 Nov 2020 21:46:33 +0100 Subject: [PATCH 5/6] More clear seperation if widget is throttled or not --- panel/param.py | 7 +++---- panel/tests/test_param.py | 13 ++++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/panel/param.py b/panel/param.py index 0a4e2a9142..5dc1e8e15e 100644 --- a/panel/param.py +++ b/panel/param.py @@ -464,10 +464,10 @@ def action(event): idx = self._callbacks.index(prev_watcher) self._callbacks[idx] = watchers[0] return + elif kw_widget.get('throttled', False) and hasattr(widget, 'value_throttled'): + updates['value_throttled'] = change.new else: updates['value'] = change.new - if hasattr(widget, 'value_throttled'): - updates['value_throttled'] = change.new try: self._updating.append(p_name) @@ -476,8 +476,7 @@ def action(event): widget.param.set_param(**updates) widget.param.trigger(*updates) else: - with param.edit_constant(widget): - widget.param.set_param(**updates) + widget.param.set_param(**updates) finally: self._updating.remove(p_name) diff --git a/panel/tests/test_param.py b/panel/tests/test_param.py index ad0942ae5e..02fa2202f7 100644 --- a/panel/tests/test_param.py +++ b/panel/tests/test_param.py @@ -633,7 +633,6 @@ class Test(param.Parameterized): test = Test() pane = Param(test) - model = pane.get_root(document, comm=comm) pane.widgets = {"a": {"throttled": False}} @@ -642,24 +641,28 @@ class Test(param.Parameterized): number.value = 1 assert number.value == 1 - # assert number.value_throttled == 1 # Should this work? + assert number.value_throttled != 1 assert test.a == 1 test.a = 2 assert number.value == 2 - assert number.value_throttled == 2 + assert number.value_throttled != 2 assert test.a == 2 + # By setting throttled to true, + # `test.a` is linked to `number.value_throttled` + # instead of `number.value`. pane.widgets = {"a": {"throttled": True}} + assert len(model.children) == 2 _, number = model.children number.value_throttled = 3 - # assert number.value == 3 # Should this work? + assert number.value != 3 assert number.value_throttled == 3 assert test.a == 3 test.a = 4 - assert number.value == 4 + assert number.value != 4 assert number.value_throttled == 4 assert test.a == 4 From 43d4cf23ca6e09cb0e718c8c4e45a430ba588b82 Mon Sep 17 00:00:00 2001 From: Simon Hoxbro Date: Sun, 6 Dec 2020 19:04:24 +0100 Subject: [PATCH 6/6] Added documentation --- examples/reference/panes/Param.ipynb | 45 +++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/examples/reference/panes/Param.ipynb b/examples/reference/panes/Param.ipynb index dd2a542b45..3d694c77fd 100644 --- a/examples/reference/panes/Param.ipynb +++ b/examples/reference/panes/Param.ipynb @@ -47,6 +47,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "## Model building\n", "Let's build a model of a cycling Athlete and her PowerCurve. \n", "\n", "The PowerCurve is a recording of her maximum power output in Watt per kg for fixed durations of time." @@ -325,12 +326,54 @@ ")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Disabling continuous updates for slider widgets\n", + "When a function takes a long time to run and depends on a parameter, realtime feedback can be a burden instead of being helpful.\n", + "\n", + "Therefore if a slider widget is used for a parameter and you have a function which takes long time to calculate, you can set the `throttled` keyword in the `panel.param.widgets` to `True` for the given parameter. This will then first run the function after the release of the mouse button of the slider.\n", + "\n", + "An example can be seen below where two parameters is connected to two sliders, one with and one without `throttled` enabled." + ] + }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "class A(param.Parameterized):\n", + " without_throttled_enabled = param.Range(\n", + " default=(100, 250),\n", + " bounds=(0, 250),\n", + " )\n", + "\n", + " with_throttled_enabled = param.Range(\n", + " default=(100, 250),\n", + " bounds=(0, 250),\n", + " )\n", + "\n", + " def __init__(self, **params):\n", + " super().__init__(**params)\n", + " widgets = {\n", + " \"without_throttled_enabled\": pn.widgets.IntRangeSlider,\n", + " \"with_throttled_enabled\": {\n", + " \"type\": pn.widgets.IntRangeSlider,\n", + " \"throttled\": True,\n", + " },\n", + " }\n", + " self.controls = pn.Param(self, widgets=widgets)\n", + "\n", + " @param.depends(\"controls\")\n", + " def calculation(self):\n", + " return self.without_throttled_enabled, self.with_throttled_enabled\n", + "\n", + "\n", + "a = A()\n", + "pn.Column(a.controls, a.calculation)" + ] } ], "metadata": {