I am trying to select/deselect all children recursively when their parent gets selected/deselected.
I wrote a script for this in python, but because the node.selected property is sync, everytime I want to change it with
node.selected = selected
it syncs to the Javascript frontend and then directly syncs back to the python backend again.
So this: node.selected = selected
is extremly inefficient.
I would like to have this behaviour as an option for the ipytree or have the ability to change multiple selections of nodes and sync only once after.
If you want to listen to the change of selected_nodes, then listen to
jupyphant_entity.on_selected_neo_objects_changed with add_listener(self, fn)
"""
"""
Calling node.selected is extremly inefficient, because it makes a trip from Python -> Javascript -> Python
So there need to be as less calls as possible.
However by doing that, the ipytree does not store the correct selected nodes anymore, so the python now stores the truth
about which node is selected
"""
jupyphant_entity.ipytree_of_neo_objects.unobserve(on_selected_change_tree, names='selected_nodes')
old_selected_nodes = change['old']
new_selected_nodes = change['new']
# old_selected_nodes and new_selected_nodes are lists of Node objects
old_set = set(old_selected_nodes)
new_set = set(new_selected_nodes)
# Nodes that were newly selected
just_selected = new_set - old_set
just_deselected = old_set - new_set
def parent_selected(child):
for node in jupyphant_entity.selected_neo_objects:
if child in getattr(node, 'nodes', []):
return True
return False
# Function to propagate selection iteratively
def propagate(nodes, selected, all_selected_in_neo):
fire_event = False
stack = list(nodes)
while stack:
node = stack.pop()
if node in visited or (not selected and all_selected_in_neo and node in just_selected):
continue
visited.add(node)
children = getattr(node, 'nodes', [])
is_leaf = not children
# Only update node.selected if not a leaf
if not (is_leaf and do_not_select_leafs):
if node.selected != selected:
node.selected = selected
# Keep selected_neo_objects in sync
if selected:
jupyphant_entity.selected_neo_objects.add(node)
fire_event = True
else:
# Since leafs never get selected in the UI it is more intuative, that they are always selected, when parent is selected
if not is_leaf or node not in just_deselected or not parent_selected(node):
jupyphant_entity.selected_neo_objects.discard(node)
fire_event = True
stack.extend(children)
return fire_event
all_selected_in_neo = just_selected.issubset(jupyphant_entity.selected_neo_objects)
none_deselected_in_neo = just_deselected.isdisjoint(jupyphant_entity.selected_neo_objects)
visited = set() # Keep track of processed nodes
fire_event = False
if all_selected_in_neo:
if not none_deselected_in_neo:
fire_event = propagate(just_deselected, False, all_selected_in_neo) or fire_event
else:
fire_event = propagate(just_selected, True, all_selected_in_neo) or fire_event
if not none_deselected_in_neo:
fire_event = propagate(just_deselected, False, all_selected_in_neo) or fire_event
if fire_event:
jupyphant_entity.on_selected_neo_objects_changed.fire()
jupyphant_entity.ipytree_of_neo_objects.observe(on_selected_change_tree, names='selected_nodes')
I am trying to select/deselect all children recursively when their parent gets selected/deselected.
I wrote a script for this in python, but because the node.selected property is sync, everytime I want to change it with
node.selected = selected
it syncs to the Javascript frontend and then directly syncs back to the python backend again.
So this: node.selected = selected
is extremly inefficient.
I would like to have this behaviour as an option for the ipytree or have the ability to change multiple selections of nodes and sync only once after.
This is the solution that works for me for now:
`
def on_selected_change_tree(change, do_not_select_leafs=True):
"""
Selects/Deselects all Childs on Parent select/deselect
`