Skip to content

Commit

Permalink
Allow Editable slider to be embedded (#6391)
Browse files Browse the repository at this point in the history
* Allow Editable slider to be embedded

* Update bokeh pin
  • Loading branch information
philippjfr authored Feb 26, 2024
1 parent d6c6814 commit 6db6d81
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 5 deletions.
2 changes: 2 additions & 0 deletions panel/io/embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ def is_embeddable(object):

widget_data, merged, ignore = [], {}, []
for widget in widgets:
if 'composite' in widget.tags:
continue
if widget._param_pane is not None:
# Replace parameter links with JS links
link = param_to_jslink(model, widget)
Expand Down
55 changes: 53 additions & 2 deletions panel/tests/io/test_embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from panel.pane import Str
from panel.param import Param
from panel.widgets import (
Checkbox, FloatSlider, IntSlider, Select, StaticText,
Checkbox, EditableFloatSlider, FloatSlider, IntSlider, Select, StaticText,
)


Expand Down Expand Up @@ -92,7 +92,6 @@ def link(target, event):
assert event['model'] == model.children[1].ref
assert event['new'] == '<pre>%s</pre>' % k


def test_embed_float_slider_explicit_values(document, comm):
select = FloatSlider()
string = Str()
Expand Down Expand Up @@ -122,6 +121,44 @@ def link(target, event):
assert event2['model'] == model.children[1].ref
assert event2['new'] == '<pre>%s</pre>' % states[k]

def test_embed_editable_float_slider_explicit_values(document, comm):
select = EditableFloatSlider()
string = Str()
def link(target, event):
target.object = event.new
select.link(string, callbacks={'value': link})
panel = Row(select, string)
with config.set(embed=True):
model = panel.get_root(document, comm)
embed_state(panel, model, document, states={select: [0.1, 0.7, 1]})
_, state = document.roots
assert set(state.state) == {0, 1, 2}
states = {0: 0.1, 1: 0.7, 2: 1}
for (k, v) in state.state.items():
content = json.loads(v['content'])
assert 'events' in content
events = content['events']
assert len(events) == 4
event1, event2, event3, event4 = events
assert event1['kind'] == 'ModelChanged'
assert event1['attr'] == 'text'
assert event1['model'] == model.children[0].children[1].children[0].ref
assert event1['new'] == '<b>%s</b>' % states[k]

assert event2['kind'] == 'ModelChanged'
assert event2['attr'] == 'value'
assert event2['new'] == states[k]

assert event3['kind'] == 'ModelChanged'
assert event3['attr'] == 'value'
assert event3['model'] == model.children[0].children[0].children[1].ref
assert event3['new'] == states[k]

assert event4['kind'] == 'ModelChanged'
assert event4['attr'] == 'text'
assert event4['model'] == model.children[1].ref
assert event4['new'] == f'&lt;pre&gt;{states[k]}&lt;/pre&gt;'

def test_embed_float_slider_default_value(document, comm):
slider = FloatSlider(start=0, end=7.2, value=3.6)
string = Str()
Expand All @@ -136,6 +173,20 @@ def link(target, event):
assert set(state.state) == {0, 1, 2}
assert layout.children[0].children[1].value == 1

def test_embed_editable_float_slider_default_value(document, comm):
slider = EditableFloatSlider(start=0, end=7.2, value=3.6)
string = Str()
def link(target, event):
target.object = event.new
slider.link(string, callbacks={'value': link})
panel = Row(slider, string)
with config.set(embed=True):
model = panel.get_root(document, comm)
embed_state(panel, model, document)
layout, state = document.roots
assert set(state.state) == {0, 1, 2}
assert layout.children[0].children[1].children[1].value == 1

def test_embed_select_explicit_values(document, comm):
select = Select(options=['A', 'B', 'C'])
string = Str()
Expand Down
20 changes: 18 additions & 2 deletions panel/widgets/slider.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
DateRangeSlider as _BkDateRangeSlider, DateSlider as _BkDateSlider,
RangeSlider as _BkRangeSlider, Slider as _BkSlider,
)
from bokeh.models.widgets.sliders import NumericalSlider as _BkNumericalSlider
from param.parameterized import resolve_value

from ..config import config
Expand Down Expand Up @@ -120,6 +121,12 @@ def __init__(self, **params):
def _get_embed_state(self, root, values=None, max_opts=3):
ref = root.ref['id']
w_model, parent = self._models[ref]
if not isinstance(w_model, _BkNumericalSlider):
is_composite = True
parent = w_model
w_model = w_model.select_one({'type': _BkNumericalSlider})
else:
is_composite = False
_, _, doc, comm = state._views[ref]

# Compute sampling
Expand All @@ -138,8 +145,13 @@ def _get_embed_state(self, root, values=None, max_opts=3):
layout_opts = {k: v for k, v in self.param.values().items()
if k in Layoutable.param and k != 'name'}

if is_composite:
layout_opts['show_value'] = False
else:
layout_opts['name'] = self.name

value = values[np.argmin(np.abs(np.array(values)-self.value))]
dw = DiscreteSlider(options=values, value=value, name=self.name, **layout_opts)
dw = DiscreteSlider(options=values, value=value, **layout_opts)
dw.link(self, value='value')
self._models.pop(ref)
index = parent.children.index(w_model)
Expand Down Expand Up @@ -367,7 +379,9 @@ def __init__(self, **params):
'is one of the declared options.'
% self.value)

self._text = StaticText(margin=(5, 0, 0, 5), styles={'white-space': 'nowrap'})
self._text = StaticText(
margin=(5, 0, 0, 5), styles={'white-space': 'nowrap'}
)
self._slider = None
self._composite = Column(self._text, self._slider)
self._update_options()
Expand Down Expand Up @@ -450,6 +464,7 @@ def _update_style(self, *events):
slider_margin = (0, r, b, l)
text_style = {k: v for k, v in style.items()
if k not in ('style', 'orientation')}
text_style['visible'] = self.show_value and text_style['visible']
self._text.param.update(margin=text_margin, **text_style)
self._slider.param.update(margin=slider_margin, **style)
if self.width:
Expand Down Expand Up @@ -814,6 +829,7 @@ def __init__(self, **params):
self._label = StaticText(margin=0, align='end')
self._slider = self._slider_widget(
value=self.value, margin=(0, 0, 5, 0), sizing_mode='stretch_width',
tags=['composite']
)
self._slider.param.watch(self._sync_value, 'value')
self._slider.param.watch(self._sync_value, 'value_throttled')
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def run(self):
########## dependencies ##########

install_requires = [
'bokeh >=3.2.0,<3.5.0',
'bokeh >=3.4.0.dev8,<3.5.0',
'param >=2.0.0,<3.0',
'pyviz_comms >=2.0.0',
'xyzservices >=2021.09.1', # Bokeh dependency, but pyodide 23.0.0 does not always pick it up
Expand Down

0 comments on commit 6db6d81

Please sign in to comment.