Skip to content

Commit

Permalink
Merge pull request #14 from ssciwr/observer-fixes
Browse files Browse the repository at this point in the history
Observer fixes
  • Loading branch information
dokempf authored Feb 9, 2022
2 parents 4c58777 + d3719c0 commit 5ff571a
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 3 deletions.
2 changes: 1 addition & 1 deletion ipywidgets_jsonschema/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from ipywidgets_jsonschema.form import Form

__version__ = "0.3.0"
__version__ = "0.3.1"
44 changes: 42 additions & 2 deletions ipywidgets_jsonschema/form.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ class FormError(Exception):
)


def as_tuple(obj):
if isinstance(obj, collections.abc.Iterable) and not isinstance(obj, str):
return obj
else:
return (obj,)


class Form:
def __init__(self, schema, vertically_place_labels=False, use_sliders=False):
"""Create a form with Jupyter widgets from a JSON schema
Expand Down Expand Up @@ -51,6 +58,9 @@ def __init__(self, schema, vertically_place_labels=False, use_sliders=False):
self.vertically_place_labels = vertically_place_labels
self.use_sliders = use_sliders

# Store a list of registered observers to add them to runtime-generated widgets
self._observers = []

# Construct the widgets
self._form_element = self._construct(schema, root=True, label=None)

Expand All @@ -72,6 +82,7 @@ def construct_element(

def observe(self, handler, names=traitlets.All, type="change"):
"""Register a change handler with all the widgets that support it"""
self._observers.append((handler, names, type))
self._form_element.register_observer(handler, names, type)

@property
Expand Down Expand Up @@ -331,8 +342,15 @@ def add_entry(_):
if len(vbox.children) == schema["maxItems"] + 1:
return

elements.insert(0, self._construct(schema["items"], label=None))
newelem = self._construct(schema["items"], label=None)
elements.insert(0, newelem)
item = elements[0].widgets[0]

# Register existing observers
for h, n, t in self._observers:
newelem.register_observer(h, n, t)

# Add array controls to our new element
trash = ipywidgets.Button(
icon="trash", layout=ipywidgets.Layout(width="33%")
)
Expand All @@ -343,6 +361,25 @@ def add_entry(_):
icon="arrow-down", layout=ipywidgets.Layout(width="33%")
)

def trigger_observers():
# Adding or removing an entry to this widget should trigger all value change handlers.
# As we do not have a proper widget to register the handler, we trigger it
# ourselves. This should make proper use of traitlets.
for h, n, t in self._observers:
if t == "change" and (n is traitlets.All or "value" in as_tuple(n)):
h(
{
"name": "value",
"old": {},
"new": {},
"owner": None,
"type": "change",
}
)

# We trigger observers upon adding
trigger_observers()

def remove_entry(b):
# If we are at the specified minimum, remove should be ignored
if "minItems" in schema:
Expand All @@ -358,6 +395,9 @@ def remove_entry(b):
vbox.children = vbox.children[:index] + vbox.children[index + 1 :]
elements.pop(index)

# We trigger observers upon removing
trigger_observers()

trash.on_click(remove_entry)

def move(dir):
Expand Down Expand Up @@ -455,7 +495,7 @@ def _construct_anyof(self, schema, label=None, key="anyOf"):
widget = ipywidgets.VBox([selector] + elements[0].widgets)

# Whenever there is a change, we switch the subschema widget
def _select(change):
def _select(_):
widget.children = [selector] + elements[names.index(selector.value)].widgets

selector.observe(_select)
Expand Down

0 comments on commit 5ff571a

Please sign in to comment.