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

Enable me to create Viewer widgets #7030

Open
MarcSkovMadsen opened this issue Jul 28, 2024 · 4 comments
Open

Enable me to create Viewer widgets #7030

MarcSkovMadsen opened this issue Jul 28, 2024 · 4 comments
Labels
TRIAGE Default label for untriaged issues

Comments

@MarcSkovMadsen
Copy link
Collaborator

MarcSkovMadsen commented Jul 28, 2024

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()

image

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.

image

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.

@MarcSkovMadsen MarcSkovMadsen added the TRIAGE Default label for untriaged issues label Jul 28, 2024
@ahuang11
Copy link
Contributor

Just skimmed this issue; wondering whether you can inherit from Widget rather than Viewer, or what the benefit of using Viewer over Widget is?

@MarcSkovMadsen
Copy link
Collaborator Author

MarcSkovMadsen commented Jul 29, 2024

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?

@ahuang11
Copy link
Contributor

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()
image

@MarcSkovMadsen
Copy link
Collaborator Author

MarcSkovMadsen commented Jul 30, 2024

That is great.

This should be documented.

Why do we have both the Viewer and the CompositeWidget @philippjfr ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
TRIAGE Default label for untriaged issues
Projects
None yet
Development

No branches or pull requests

2 participants