How to implement a container widget? #293
-
I am trying to create a Box widget alternative to import anywidget
import traitlets
import ipywidgets as ipw
class Box(anywidget.AnyWidget):
_esm = """
export function render(view) {
// How to access and display children here?
}
"""
children = traitlets.List(trait=traitlets.Instance(ipw.DOMWidget)).tag(sync=True, **ipw.widget_serialization) Of course there should be nothing in the box as we did not handle it on JavaScript side. |
Beta Was this translation helpful? Give feedback.
Replies: 10 comments 1 reply
-
Thanks for trying out anywidget! I'd recommend having a look at the front end ipywidgets implementation for It seems the |
Beta Was this translation helpful? Give feedback.
-
Yes it was where I got stuck, because
I would share if I got successful in doing so, but will be waiting for a more straightforward solution by you and your team. You are doing a great job👍. |
Beta Was this translation helpful? Give feedback.
-
This really works provided that we use an HTML element which has import anywidget
import traitlets
import ipywidgets as ipw
class Javascript(anywidget.AnyWidget):
_esm = """
export function render(view) {
function accessParent(event) {
let box = event.target.parentNode.parentNode
box.classList.add("MyBox")
// Doing anything with box here
}
var iframe = document.createElement('iframe');
iframe.onload = accessParent
iframe.src = "https://www.tldraw.com"
view.el.appendChild(iframe);
}
"""
_css = """
.MyBox {
background: skyblue;
height: 400px;
}
.MyBox iframe {
border: 1px solid red;
background: whitesmoke;
height: 350px;
width: 100%;
}
""" Thanks to provide a direction for easily building custom widgets! |
Beta Was this translation helpful? Give feedback.
-
Well I dug into this a bit. Turns out it's not too hard to accomplish what you want currently with anywidget. Model serializers just allow you to hook into let children = deserialize_children(view.model.get("children")); Looking into import anywidget
import traitlets
import ipywidgets as ipw
class Box(anywidget.AnyWidget):
_esm = """
async function unpack_models(model_ids, manager) {
return Promise.all(
model_ids.map(id => manager.get_model(id.slice("IPY_MODEL_".length)))
);
}
export async function render(view) {
let model_ids = view.model.get("children"); // ["IPY_MODEL_<model_id>", ...]
let children_models = await unpack_models(model_ids, view.model.widget_manager);
for (let model of children_models) {
let child_view = await view.create_child_view(model);
view.el.appendChild(child_view.el);
}
}
"""
children = traitlets.List(trait=traitlets.Instance(ipw.DOMWidget)).tag(sync=True, **ipw.widget_serialization) box = Box(children=[
ipw.IntProgress(),
ipw.IntSlider(),
])
box |
Beta Was this translation helpful? Give feedback.
-
For clarification, |
Beta Was this translation helpful? Give feedback.
-
That's great. 👍 A little bit tricky as I would never have thought about |
Beta Was this translation helpful? Give feedback.
-
I had to do a bit of digging to find this function in the To be totally clear, you would need to equally dig for import { DOMWidgetModel, DOMWidgetView, unpack_models } from "@jupyter-widgets/base";
class MyBoxModel extends DOMWidgetModel {
/* ... */
static serializers = {
...DOMWidgetModel.serializers,
children: { deserialize: unpack_models },
}
}
class MyBoxView extends DOMWidgetView {
render() {
let models = this.model.get("children");
for (let model of models) {
let child_view = await this.create_child_view(model);
this.el.appendChild(child_view.el);
}
}
} |
Beta Was this translation helpful? Give feedback.
-
Yes you are right. Cookicutter way didn't let me do this for a whole year because it's a very steep learning curve plus usually there is no urge to learn JavaScript if you already know Python. |
Beta Was this translation helpful? Give feedback.
-
Love to hear it! Thanks again for trying it out and sharing this use case. I learned more about how ipywidgets work widgets under the hood :) |
Beta Was this translation helpful? Give feedback.
-
I've updated the sample to use import anywidget
import traitlets
import ipywidgets
class Box(anywidget.AnyWidget):
_esm = """
async function unpack_models(model_ids, manager) {
return Promise.all(
model_ids.map(id => manager.get_model(id.slice("IPY_MODEL_".length)))
);
}
export async function render({ model, el }) {
let model_ids = model.get("children"); // ["IPY_MODEL_<model_id>", ...]
let children_models = await unpack_models(model_ids, model.widget_manager);
for (let model of children_models) {
let child_view = await model.widget_manager.create_view(model);
el.appendChild(child_view.el);
}
}
"""
children = traitlets.List(traitlets.Instance(ipywidgets.DOMWidget)).tag(
sync=True, **ipywidgets.widget_serialization
) box = Box(
children=[
ipywidgets.IntProgress(),
ipywidgets.IntSlider(),
]
)
box |
Beta Was this translation helpful? Give feedback.
Well I dug into this a bit. Turns out it's not too hard to accomplish what you want currently with anywidget. Model serializers just allow you to hook into
view.model.get(<name>)
andview.model.set(<name>, <value>)
. As long as the Python serializer (ipy.widget_serialization
) targets JSON (and doesn't include binary data), you can accomplish this deserialization manually:Looking into
unpacked_models
, it's pretty straightforward to implement yourself forchildren
: