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

display shows nothing while directly outputing the widget is OK #762

Open
fzyzcjy opened this issue Dec 12, 2024 · 9 comments
Open

display shows nothing while directly outputing the widget is OK #762

fzyzcjy opened this issue Dec 12, 2024 · 9 comments
Labels
bug Something isn't working experimental

Comments

@fzyzcjy
Copy link

fzyzcjy commented Dec 12, 2024

Describe the bug

Hi thanks for the lib! However, it seems display shows nothing.

This is OK:

image

But this shows nothing:

image

With logs being normal (no errors)

image

Reproduction

from pydantic import BaseModel
import anywidget.experimental
import psygnal

_counter_esm = """
function render({ model, el }) {
  let button = document.createElement("button");
  button.innerHTML = `count is ${model.get("value")}`;
  button.addEventListener("click", () => {
    model.set("value", model.get("value") + 1);
    model.save_changes();
  });
  model.on("change:value", () => {
    button.innerHTML = `count is ${model.get("value")}`;
  });
  el.classList.add("counter-widget");
  el.appendChild(button);
}
export default { render };
"""

@anywidget.experimental.widget(esm=_counter_esm)
@psygnal.evented
class HelloWidget(BaseModel):
  value: int = 0

# HelloWidget(value=42)
display(HelloWidget(value=42))

Logs

No response

System Info

jupyter lab

Severity

annoyance

@fzyzcjy fzyzcjy added the bug Something isn't working label Dec 12, 2024
@manzt
Copy link
Owner

manzt commented Dec 12, 2024

Hi there, I am able to reproduce. I'm not sure this is something we can resolve in the experimental API.

I'm not sure why, but it seems like the global display might special case ipywidgets and the experimental API in anywidget is without ipywidgets.

I was able to get this to work by importing from IPython.display explicitly:

++ from IPython.display import display

from pydantic import BaseModel
import anywidget.experimental
import psygnal

@anywidget.experimental.widget(esm="""
function render({ model, el }) {
  let button = document.createElement("button");
  button.innerHTML = `count is ${model.get("value")}`;
  button.addEventListener("click", () => {
    model.set("value", model.get("value") + 1);
    model.save_changes();
  });
  model.on("change:value", () => {
    button.innerHTML = `count is ${model.get("value")}`;
  });
  el.classList.add("counter-widget");
  el.appendChild(button);
}
export default { render };
""")
@psygnal.evented
class HelloWidget(BaseModel):
  value: int = 0

display(HelloWidget(value=42))

For context, the display method calls HelloWidget()._repr_mimebundle_() internally, and the returned value is identical between the experimental and ipywidgets-based APIs. So I'm not entirely sure how the special casing is happening, or if there is a way we could emulate it as well for the class without traitlets/ipywidgets.

@manzt
Copy link
Owner

manzt commented Dec 12, 2024

For anyone else reading this, display works for the core ipywidgets-based API:

import anywidget
import traitlets

class CounterWidget(anywidget.AnyWidget):
    _esm = """
    function render({ model, el }) {
      let count = () => model.get("value");
      let btn = document.createElement("button");
      btn.innerHTML = `count is ${count()}`;
      btn.addEventListener("click", () => {
        model.set("value", count() + 1);
        model.save_changes();
      });
      model.on("change:value", () => {
        btn.innerHTML = `count is ${count()}`;
      });
      el.appendChild(btn);
    }
    export default { render };
    """
    value = traitlets.Int(0).tag(sync=True)

display(CounterWidget())

@fzyzcjy
Copy link
Author

fzyzcjy commented Dec 12, 2024

Hi thanks you for the quick reply! But importing the display function does not work for me:

image

@manzt
Copy link
Owner

manzt commented Dec 12, 2024

Ah you know what, I think I figured something out. I don't think it is related to the display import, my mistake.

display(HelloWidget(value=42))

Does not show anything for me. But assigning the widget to variable first:

w = HelloWidget(value=42)
display(w)

does work for me. Would you be able to try both and confirm? I can have a look to understand the underlying behavior later if this is the root of the issue.

@fzyzcjy
Copy link
Author

fzyzcjy commented Dec 12, 2024

Yes it's working!

image

@fzyzcjy
Copy link
Author

fzyzcjy commented Dec 12, 2024

More observations:

  1. Variable in function also not work

image

  1. Variable referenced by global variable does work

image

  1. Variable in function, but referenced by global var, does work

image

@fzyzcjy
Copy link
Author

fzyzcjy commented Dec 12, 2024

I think I find something: The object is weirdly deleted (if we do not reference it in a global variable)! Surely, if it is deleted, then everything will go wrong.

image
image

@manzt
Copy link
Owner

manzt commented Dec 12, 2024

Thanks for the exploration! ok, now i’m realizing that this is due to fact we make widgets with our Descriptor weakrefable. The comms are getting cleaned up.

cc: @tlambert03

@fzyzcjy
Copy link
Author

fzyzcjy commented Dec 12, 2024

Thanks, then look like this can be fixed and looking forward to the future release!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working experimental
Projects
None yet
Development

No branches or pull requests

2 participants