Skip to content

Commit

Permalink
Build NodeTree on demand (#583)
Browse files Browse the repository at this point in the history
This PR introduces the idea that node tree should only be built on demand.
- do not build tree recursively, only show the top-level
- build the sub-tree when users select it, and expand it.
  • Loading branch information
superstar54 authored Apr 23, 2024
1 parent 390a0d3 commit 01be224
Showing 1 changed file with 34 additions and 8 deletions.
42 changes: 34 additions & 8 deletions aiidalab_widgets_base/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ def __init__(self, pk, name, **kwargs):
super().__init__(name=name, **kwargs)

@tl.default("opened")
def _default_openend(self):
return False
def _default_opened(self):
return True


class AiidaProcessNodeTreeNode(AiidaNodeTreeNode):
Expand All @@ -91,7 +91,7 @@ class CalcFunctionTreeNode(AiidaProcessNodeTreeNode):

class AiidaOutputsTreeNode(ipytree.Node):
icon = tl.Unicode("folder").tag(sync=True)
disabled = tl.Bool(True).tag(sync=True)
disabled = tl.Bool(False).tag(sync=True)

def __init__(self, name, parent_pk, namespaces: tuple[str, ...] = (), **kwargs):
self.parent_pk = parent_pk
Expand Down Expand Up @@ -141,6 +141,15 @@ def _refresh_output(self):
display(self._tree)

def _observe_tree_selected_nodes(self, change):
for node in change["new"]:
# find the selected node and build the tree from it, so that users can expand and explore the tree
node_pk = (
node.parent_pk
if isinstance(node, AiidaOutputsTreeNode)
else getattr(node, "pk", None)
)

self._build_tree(self.find_node(node_pk, getattr(node, "namespaces", None)))
return self.set_trait(
"selected_nodes",
tuple(
Expand Down Expand Up @@ -180,9 +189,19 @@ def _to_tree_node(cls, node, name=None, **kwargs):
name = calc_info(node)
else:
name = str(node)
return cls.NODE_TYPE.get(type(node), UnknownTypeTreeNode)(
tree_node = cls.NODE_TYPE.get(type(node), UnknownTypeTreeNode)(
pk=node.pk, name=name, **kwargs
)
# Set the style based on the process state of the node
if isinstance(node, orm.ProcessNode):
process_state = (
engine.ProcessState.EXCEPTED if node.is_failed else node.process_state
)
tree_node.icon_style = cls.PROCESS_STATE_STYLE.get(
process_state, cls.PROCESS_STATE_STYLE_DEFAULT
)

return tree_node

@classmethod
def _find_called(cls, root):
Expand Down Expand Up @@ -251,8 +270,8 @@ def _find_children(cls, root):

@classmethod
def _build_tree(cls, root):
"""Recursively build a tree nodes graph for a given tree node."""
root.nodes = [cls._build_tree(child) for child in cls._find_children(root)]
"""Build a tree nodes graph for a given tree node."""
root.nodes = list(cls._find_children(root))
return root

@classmethod
Expand Down Expand Up @@ -284,9 +303,16 @@ def update(self, _=None):
for tree_node in self._walk_tree(root_node):
self._update_tree_node(tree_node)

def find_node(self, pk):
def find_node(self, pk, namespaces=None):
"""Find a node by its pk and namespaces.
If node is an output node, it is identified by the parent pk and namespaces, otherwise by the pk."""
for node in self._walk_tree(self._tree):
if getattr(node, "pk", None) == pk:
node_pk = (
node.parent_pk
if isinstance(node, AiidaOutputsTreeNode)
else getattr(node, "pk", None)
)
if node_pk == pk and getattr(node, "namespaces", None) == namespaces:
return node
raise KeyError(pk)

Expand Down

0 comments on commit 01be224

Please sign in to comment.