Enable me to create Viewer widgets
I've many, many times been wanting to create custom widgets using the Viewer. But I've experienced just as many times its not possible.
For example now to help P720 in https://discourse.holoviz.org/t/panel-chat-interface/7505 I would like to create a custom widget and use that for the ChatInterface. But it cannot work as the Viewer cannot be made to work like a Widget.
Minimum, Reproducible Example
import panel as pn
import param
pn.extension()
class PreferenceInput(pn.viewable.Layoutable, pn.widgets.WidgetBase, pn.viewable.Viewer):
value = param.Parameter()
def __init__(self, **params):
super().__init__(**params)
self._preference_widget = pn.widgets.RadioBoxGroup(options=["blue", "red", "green"], value="blue", name="preference")
pn.bind(self._update_value, self._preference_widget)
self._layout = pn.Column(
"What is your preference?",
self._preference_widget,
)
def _update_value(self, preference):
self.value = "My preference is {preference}."
def __panel__(self):
return self._layout
preference_input = PreferenceInput()
def even_or_odd(contents, user, instance):
if len(contents) % 2 == 0:
return "Even number of characters."
return "Odd number of characters."
pn.chat.ChatInterface(callback=even_or_odd, widgets=[preference_input]).servable()
It works fine until I click send. Then I get
File "/home/jovyan/repos/private/panel/panel/chat/interface.py", line 405, in _click_send
value = active_widget.value
In this case the problem is that when a Viewer is put in a row its replaced with its __panel__(). Thus then the assumptions about how to retrieve the ChatInterface.active_widget property breaks down. In this case you could probable "fix" the active_widget implementation. But in my experience its a problem all over the place that its not possible to get a Viewer to behave like a widget.
As a bonus its also hard and not documented how to get a Viewer to be Layoutable. That is always another friction. The ChatInterface also assumes that about the input widgets. It assumes there is a sizing_mode on the widget.
Just skimmed this issue; wondering whether you can inherit from Widget rather than Viewer, or what the benefit of using Viewer over Widget is?
Its a good question. If that is possible it should be documented.
I don't believe its possible though. The Widget is for creating widgets from Bokeh JavaScript/ Typescript models?
Here's my take using CompositeWidget.
import panel as pn
import param
pn.extension()
class PreferenceInput(pn.widgets.CompositeWidget):
value = param.Parameter()
def __init__(self, **params):
super().__init__(**params)
self._preference_widget = pn.widgets.RadioBoxGroup(
options=["blue", "red", "green"], value="blue", name="preference"
)
pn.bind(self._update_value, self._preference_widget, watch=True)
self._composite[:] = [
"What is your preference?",
self._preference_widget,
]
def _update_value(self, preference):
self.value = f"My preference is {preference}."
preference_input = PreferenceInput()
def even_or_odd(contents, user, instance):
if len(contents) % 2 == 0:
return "Even number of characters."
return "Odd number of characters."
pn.chat.ChatInterface(callback=even_or_odd, widgets=[preference_input]).servable()
That is great.
This should be documented.
Why do we have both the Viewer and the CompositeWidget @philippjfr ?
We now have documents about using PyComponent + WidgetBase to create widgets.