Fixes for multiple layers in lonboard + binder widget behaviour#4
Merged
Conversation
…played, improving DX, not requiring IPython.display stuff
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This was based on this plan that @wrynearson + Sonnet created for upstream fixes after running into issues when using
manywidgets:manywidgets upstream suggestions
Findings from using
FilterBinderwith a multi-layer lonboard map (onePolygonLayer+ oneScatterplotLayer, both driven by the sameRangeSlider). Three concrete issues with proposedfixes, ordered by impact.
1.
FilterBinderdoesn't accept a list of layersWhat happens
The
layertrait istraitlets.Instance(Widget), so passing a list raises immediately.Why it matters
Multi-layer maps are common — a typical pattern is a
PolygonLayerfor high-intensity events(ShakeMap footprints) and a
ScatterplotLayerfor everything else. The user naturally wants oneslider to filter both. Today they must create one
FilterBinderper layer and display every oneof them, or the bindings never activate (see issue 2).
Suggested fix
Accept either a single layer or a list in both Python and JS.
widget.py— replaceInstancewith aUnion:src/index.ts— iterate over whatever was passed:With this change the workaround (multiple
FilterBinders) becomes unnecessary.2.
FilterBindersilently does nothing unless it is rendered in the DOMWhat happens
No error. The map just never responds to the slider. Moving the slider has no effect.
Why it happens
All of
FilterBinder's logic lives in itsrender()JS function. In anywidget,render()onlyruns when the widget is mounted in the browser DOM. If you create a
FilterBinderbut neverdisplay()it (or place it in aColumn), the JS never starts, the event listeners are neverregistered, and the polling loop never runs.
This is a silent failure. The user gets no traceback, no warning — just a broken UI.
Suggested fixes
Option A — add a Python-side observer as a fallback for live kernel mode:
This makes
FilterBinderwork in live notebooks without ever callingdisplay(). The JSbinding still handles static export, so both paths are covered.
Option B — warn loudly if not displayed within a short timeout:
Not easily implementable in anywidget, but worth noting as a DX improvement.
Minimum viable fix — document it clearly. The current docs show a single example with a
Column(slider, binder, map)but don't explain whybinderhas to be in the layout.A note like this in the
FilterBinderdocs would prevent a lot of confusion:3.
Columndoesn't flatten list argumentsWhat happens
Column.__init__receives*childrenpositional args and wraps them inlist(children). Ifany arg is itself a list (e.g.
binders), that list ends up as a child element, and theList(Instance(Widget))trait rejects it.Suggested fix
Flatten in
__init__:Same fix applies to
RowandGrid, which have identical__init__signatures.With this,
Column(slider, binders, m)just works.Summary table
FilterBinder.layerrejects a listFilterBinderper layerFilterBindersilently inactive unless displayeddisplay(*binders)Columnrejects list argsColumn(slider, *binders, m)__init__