S
Solara5mo ago
J-M

Reusing ipywidget composite UI in solara

I am trying to reuse some ipywidget UI components with minimal rewrite in a new solara dashboard. I noticed that using a display function, and I am not sure where it is from since there is no explicit import. A prior question uses this in https://discord.com/channels/1106593685241614489/1232310547794628649. I used it to reuse displaying an ipyleaflet Map, with success, working by inference from another example https://huggingface.co/spaces/FranciscoGS/solara-geospatialDashboard/blob/main/pages/Servicios_apoyo.py#L106 . I am now trying to apply the same recipe to display a list of ipywidget Datetime pickers and FloatSliders:
def build_param_ui(station_id):
import ipywidgets as ipw
# some code
ui = InteractiveSimulationUi(sim)
_simui = ui
ctrls = ipw.VBox(ui.all_ctrls)
display(ctrls)
def build_param_ui(station_id):
import ipywidgets as ipw
# some code
ui = InteractiveSimulationUi(sim)
_simui = ui
ctrls = ipw.VBox(ui.all_ctrls)
display(ctrls)
and the function is wrapped in a solara component
@solara.component
def ParamSelector():
station_id = selected_station_id.value
build_param_ui(station_id)
@solara.component
def ParamSelector():
station_id = selected_station_id.value
build_param_ui(station_id)
However this fails with the following stacktrace
5 Replies
J-M
J-M5mo ago
I am doing something wrong using display on a ipw VBox, perhaps. Any insight would be welcome.
Traceback (most recent call last):
"/v/lib/python3.9/site-packages/reacton/core.py", line 1707, in _render
root_element = el.component.f(*el.args, **el.kwargs)
"//app/pages/__init__.py", line 208, in ParamSelector
build_param_ui(station_id)
# snipping lines
fs=ipw.FloatSlider(
"/ipywidgets/widgets/widget_float.py", line 26, in __init__
super().__init__(**kwargs)
"/ipywidgets/widgets/widget_description.py", line 35, in __init__
super().__init__(*args, **kwargs)
"/ipywidgets/widgets/widget.py", line 506, in __init__
self.open()
"/ipywidgets/widgets/widget.py", line 525, in open
state, buffer_paths, buffers = _remove_buffers(self.get_state())
"/ipywidgets/widgets/widget.py", line 615, in get_state
value = to_json(getattr(self, k), self)
"/ipywidgets/widgets/widget.py", line 54, in _widget_to_json
return "IPY_MODEL_" + x.model_id
"/solara/server/patch.py", line 527, in model_id_debug
raise RuntimeError(f"Widget {type(self)} has been closed, the stacktrace when the widget was closed is:\n{closed_stack[id(self)]}")
RuntimeError: Widget <class 'ipywidgets.widgets.widget_layout.Layout'> has been closed, the stacktrace when the widget was closed is:
"/v/bin/solara", line 8, in <module>
sys.exit(main())
Traceback (most recent call last):
"/v/lib/python3.9/site-packages/reacton/core.py", line 1707, in _render
root_element = el.component.f(*el.args, **el.kwargs)
"//app/pages/__init__.py", line 208, in ParamSelector
build_param_ui(station_id)
# snipping lines
fs=ipw.FloatSlider(
"/ipywidgets/widgets/widget_float.py", line 26, in __init__
super().__init__(**kwargs)
"/ipywidgets/widgets/widget_description.py", line 35, in __init__
super().__init__(*args, **kwargs)
"/ipywidgets/widgets/widget.py", line 506, in __init__
self.open()
"/ipywidgets/widgets/widget.py", line 525, in open
state, buffer_paths, buffers = _remove_buffers(self.get_state())
"/ipywidgets/widgets/widget.py", line 615, in get_state
value = to_json(getattr(self, k), self)
"/ipywidgets/widgets/widget.py", line 54, in _widget_to_json
return "IPY_MODEL_" + x.model_id
"/solara/server/patch.py", line 527, in model_id_debug
raise RuntimeError(f"Widget {type(self)} has been closed, the stacktrace when the widget was closed is:\n{closed_stack[id(self)]}")
RuntimeError: Widget <class 'ipywidgets.widgets.widget_layout.Layout'> has been closed, the stacktrace when the widget was closed is:
"/v/bin/solara", line 8, in <module>
sys.exit(main())
iisakkirotko
iisakkirotko5mo ago
Hey @J-M! The display function can be imported from solara, but we just hijack the IPython one, so that one should work equally well. Like the stacktrace says, reacton is trying to render a widget into an element, but that widget has been closed since the render started. Is there any more context to the stacktrace when the widget was closed, or is it only this sys.exit(main()) call? For the reference, often if you want to avoid having to use display, you can also append .element to your widget calls, so
ipywidgets.FloatSlider(args)
ipywidgets.FloatSlider(args)
would become
ipywidgets.FloatSlider.element(args)
ipywidgets.FloatSlider.element(args)
J-M
J-M4mo ago
Thank you @Iisakki Rotko . I think I can get by trying to use every ipywidgets one by one, using .element. I do not understand the widget lifecycle (python and JS sides) enough to understand why it is reported as closed. I thought the use of a global variable _simui = ui to hold an indirect reference to all ipywidget instances via ui would prevent garbage collection (if this is a synonym of "closed") and keep widgets "alive".
MaartenBreddels
MaartenBreddels4mo ago
@J-M could you be using a global widget? The idea is that because multiple users share the same server, we need to isolate widgets from each user. A global widget can be a closed one, and might result in an error like this
J-M
J-M3mo ago
THanks @MaartenBreddels and apologies for the belated reply. if by global widget you mean a static variable (class variable) I don't think so. I worked around this, perhaps for the better anyway. I'll see if I can make the time this month to write one or more blog posts on solara and give links.
Want results from more Discord servers?
Add your server