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

Make it easy to use Traitlets including Ipywidgets/ AnyWidgets with Param #891

Open
MarcSkovMadsen opened this issue Nov 26, 2023 · 1 comment
Labels
type-feature Feature request

Comments

@MarcSkovMadsen
Copy link
Collaborator

Today you can panel.bind to the value trait of an ipywidgets. But ipywidgets sometimes don't have a value trait or sometimes it has lots of extra, interesting traits.

In https://discourse.holoviz.org/t/panel-works-with-anywidget/6466 I demonstrate how you can easily create an observer, A Parameterized object with parameters corresponding to the ipywidgets that observe for trait changes. Such an observer makes it easy to .watch, @depends, bind and more to the ipywidget.

Please consolidate this functionality and add it to Param.

It should probably be easy to only observe a specified selection of traits (white list) as well as a specified selection of traits not to watch (black list).

# pip install panel ipywidgets_bokeh anywidget
import anywidget
import traitlets

class CounterWidget(anywidget.AnyWidget):
    _esm = """
    export function render({ model, el }) {
      let getCount = () => model.get("count");
      let button = document.createElement("button");
      button.classList.add("counter-button");
      button.innerHTML = `Count is ${getCount()}`;
      button.addEventListener("click", () => {
        model.set("count", getCount() + 1);
        model.save_changes();
      });
      model.on("change:count", () => {
        button.innerHTML = `Count is ${getCount()}`;
      });
      el.appendChild(button);
    }
    """
    _css="""
    .counter-button { background-color: pink; font-size: 48px}
    .counter-button:hover { background-color: pink; }
    """
    count = traitlets.Int(0).tag(sync=True)

counter = CounterWidget()

# HELP FUNCTIONALITY to convert Traitlets Classes/ Events to Param Classes/ Events

import param

_ipywidget_classes = {}
_any_widget_traits = set(anywidget.AnyWidget().traits())

def create_observer(obj, traits=None)->param.Parameterized:
    """Returns a Parameterized class with parameters corresponding to the traits of the obj
    
    Args:
        traits: A list of traits to observe. If None all traits not on the base AnyWidget will be
        observed.
    """
    if not traits:
        traits = list(set(obj.traits())-_any_widget_traits)
    name = type(obj).__name__
    if name in _ipywidget_classes:
        observer_class = _ipywidget_classes[name]
    else:
        observer_class = param.parameterized_class(name, {trait: param.Parameter() for trait in traits})
        _ipywidget_classes[name] = observer_class
    
    values = {trait: getattr(obj, trait) for trait in traits}
    observer = observer_class(**values)
    obj.observe(lambda event: setattr(observer, event["name"], event["new"]), names=traits)
    return observer

# THE PANEL APP

import panel as pn
pn.extension("ipywidgets")

observer = create_observer(counter)

def some_output(count):
    return f"The count is {count}!"

component = pn.Column(counter, pn.bind(some_output, observer.param.count))

pn.template.FastListTemplate(
    site="Panel",
    title="Works with AnyWidget",
    main=[component],
).servable()
panel-works-with-anywidget.mp4
@MarcSkovMadsen MarcSkovMadsen added type-feature Feature request TRIAGE User-submitted issue that hasn't been triaged yet. labels Nov 26, 2023
@jbednar
Copy link
Member

jbednar commented Nov 27, 2023

That would all be great to have!

@maximlt maximlt removed the TRIAGE User-submitted issue that hasn't been triaged yet. label Nov 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type-feature Feature request
Projects
None yet
Development

No branches or pull requests

3 participants