holoviz / panel

Panel: The powerful data exploration & web app framework for Python
https://panel.holoviz.org
BSD 3-Clause "New" or "Revised" License
4.71k stars 510 forks source link

Make it easy for me to provide examples that my users can run a model on #6205

Open MarcSkovMadsen opened 9 months ago

MarcSkovMadsen commented 9 months ago

Use Case

Sometimes you want to make it easy for your users to input a value. For example if you have a model and you want your user to try it out.

For example Gradio supports this and uses it extensible for demos of model. See Examples. Please note that their Examples component support more than text buttons. It can be buttons representing any kind of media including audio and video.

Panel has different Button groups. But they don't serve my purpose because they are associated with one or more options being checked. Also visually.

Hack Code

A hack is to develop something like the below. I added an ExampleSelect too because that is also used a lot in the Hugging Face/ Gradio universe.

import panel as pn
import param

pn.extension(sizing_mode="stretch_width", design="material")

class ExamplesButtons(pn.viewable.Viewer):
    value = param.String()

    def __init__(self, names, button_type="default", default_layout=pn.Column):
        super().__init__()
        group = default_layout()
        for name in names:
            button = pn.widgets.Button(name=name, button_type=button_type, sizing_mode="fixed", button_style="outline")
            button.param.watch(self._update_value, "clicks")
            group.append(button)

        self._group = group

    def __panel__(self):
        return self._group

    def _update_value(self, event):
        self.value = event.obj.name

class ExamplesSelect(pn.viewable.Viewer):
    value = param.String()

    def __init__(self, names,):
        super().__init__()

        self._names=names

        widget = pn.widgets.Select(options=["Examples", *names])
        widget.param.watch(self._update_value_first_time, "value")
        self._group = pn.Column(widget)

    def __panel__(self):
        return self._group

    def _update_value_first_time(self, event):
        self.value = event.new

        widget = pn.widgets.Select(value=self.value, options=self._names)
        widget.param.watch(self._update_value, "value", queued=True)
        self._group[:] = [widget]

    def _update_value(self, event):
        self.value = event.new

examples=[f"Example {i}" for i in range(10)]

button_group = ExamplesButtons(examples, default_layout=pn.layout.FlexBox)
button_layout = pn.Column(
    "## Examples", button_group, "## Input", button_group.param.value, pn.widgets.Button(name="Submit", button_type="primary")
)

select_group = ExamplesSelect(examples)
select_layout = pn.Column(
    "## Examples", select_group, "## Input", select_group.param.value, pn.widgets.Button(name="Submit", button_type="primary")
)

pn.Row(button_layout, select_layout).servable()

https://github.com/holoviz/panel/assets/42288570/20f4bd82-817d-44fa-8268-b14d799bbfd8

Additional Context

I was starting to write a small article about using Panel with Transformers library. But having to show and explain the above to users just makes everything so much more complicated.

Gradio users would assume something like the above is very easy to do.

Note

The options of the Select widget in the example above are hard to update dynamically. I could not figure out how to do it.

Question

Are we interested in adding missing Gradio functionality like this to Panel?

ahuang11 commented 9 months ago

I don't fully understand this issue. Can you clarify what Panel is missing here?

Panel has different Button groups. But they don't serve my purpose because they are associated with one or more options being checked. Also visually.

Isn't your ExampleButtons similar to https://panel.holoviz.org/reference/widgets/RadioButtonGroup.html?

MarcSkovMadsen commented 9 months ago

RadioButtons have 1) initial state and 2) visual state. My ExampleButtons don't.