예제 #1
0
def search_api_tree(keywords, api_tree):
    """Search Earth Engine API and return functions containing the specified keywords

    Args:
        keywords (str): The keywords to search for.
        api_tree (dict): The dictionary containing the Earth Engine API tree.

    Returns:
        object: An ipytree object/widget.
    """
    import warnings

    warnings.filterwarnings("ignore")

    sub_tree = Tree()

    for key in api_tree.keys():
        if keywords.lower() in key.lower():
            sub_tree.add_node(api_tree[key])

    return sub_tree
예제 #2
0
def search_api_tree(keywords, api_tree, tools_dict):
    """Search the WhiteboxTools API and return functions containing the specified keywords

    Args:
        keywords (str): The keywords to search for.
        api_tree (dict): The dictionary containing the WhiteboxTools API tree.
        tools_dict (dict): The dictionary containing the dict of all tools.

    Returns:
        object: An ipytree object/widget.
    """
    import warnings

    warnings.filterwarnings("ignore")

    sub_tree = Tree()
    for key in api_tree.keys():
        if (keywords.lower()
                in key.lower()) or (keywords.lower()
                                    in tools_dict[key]["description"].lower()):
            sub_tree.add_node(api_tree[key])

    return sub_tree
예제 #3
0
def build_toolbox_tree(tools_dict, folder_icon="folder", tool_icon="wrench"):
    """Build the toolbox for WhiteboxTools.

    Args:
        tools_dict (dict): A dictionary containing information for all tools.
        folder_icon (str, optional): The font-awesome icon for tool categories. Defaults to "folder".
        tool_icon (str, optional): The font-awesome icon for tools. Defaults to "wrench".

    Returns:
        object: An ipywidget representing the toolbox.
    """
    left_widget = widgets.VBox()
    right_widget = widgets.VBox()
    full_widget = widgets.HBox([left_widget, right_widget])

    search_description = f"{len(tools_dict)} tools available. Search tools ..."
    search_box = widgets.Text(placeholder=search_description)
    search_box.layout.width = "270px"

    close_btn = widgets.Button(icon="close",
                               layout=widgets.Layout(width="32px"))

    def close_btn_clicked(b):
        full_widget.close()

    close_btn.on_click(close_btn_clicked)

    tree_widget = widgets.Output()
    tree_widget.layout.max_width = "310px"
    tree_widget.overflow = "auto"

    left_widget.children = [widgets.HBox([search_box, close_btn]), tree_widget]
    output = widgets.Output(layout=widgets.Layout(max_width="760px"))
    right_widget.children = [output]

    tree = Tree(multiple_selection=False)
    tree_dict = {}

    def search_box_callback(text):

        with tree_widget:
            if text.value == "":
                print("Loading...")
                tree_widget.clear_output(wait=True)
                display(tree)
            else:
                tree_widget.clear_output()
                print("Searching...")
                tree_widget.clear_output(wait=True)
                sub_tree = search_api_tree(text.value, tree_dict)
                display(sub_tree)

    search_box.on_submit(search_box_callback)

    root_name = "WhiteboxTools"
    root_node = Node(root_name)
    tree.add_node(root_node)

    categories = {}

    def handle_tool_clicked(event):
        if event["new"]:
            cur_node = event["owner"]
            tool_name = cur_node.name
            with output:
                output.clear_output()
                tool_ui = tool_gui(tools_dict[tool_name])
                display(tool_ui)

    for key in tools_dict.keys():
        category = tools_dict[key]["category"]
        if category not in categories.keys():
            category_node = Node(category, icon=folder_icon, opened=False)
            root_node.add_node(category_node)
            categories[category] = category_node
            tool_node = Node(key, icon=tool_icon)
            category_node.add_node(tool_node)
            tree_dict[key] = tool_node
            tool_node.observe(handle_tool_clicked, "selected")
        else:
            category_node = categories[category]
            tool_node = Node(key, icon=tool_icon)
            category_node.add_node(tool_node)
            tree_dict[key] = tool_node
            tool_node.observe(handle_tool_clicked, "selected")

    with tree_widget:
        tree_widget.clear_output()
        display(tree)

    return full_widget
예제 #4
0
    def __init__(self, **kwargs):
        self._tree = Tree()
        self._tree.observe(self._observe_tree_selected_nodes,
                           ["selected_nodes"])

        super().__init__(**kwargs)
예제 #5
0
class NodesTreeWidget(ipw.Output):
    """A tree widget for the structured representation of a nodes graph."""

    nodes = traitlets.Tuple().tag(trait=traitlets.Instance(Node))
    selected_nodes = traitlets.Tuple(read_only=True).tag(
        trait=traitlets.Instance(Node))

    PROCESS_STATE_STYLE = {
        ProcessState.EXCEPTED: "danger",
        ProcessState.FINISHED: "success",
        ProcessState.KILLED: "warning",
        ProcessState.RUNNING: "info",
        ProcessState.WAITING: "info",
    }

    PROCESS_STATE_STYLE_DEFAULT = "default"

    NODE_TYPE = {
        WorkChainNode: WorkChainProcessTreeNode,
        CalcFunctionNode: CalcFunctionTreeNode,
        CalcJobNode: CalcJobTreeNode,
    }

    def __init__(self, **kwargs):
        self._tree = Tree()
        self._tree.observe(self._observe_tree_selected_nodes,
                           ["selected_nodes"])

        super().__init__(**kwargs)

    def _refresh_output(self):
        # There appears to be a bug in the ipytree implementation that sometimes
        # causes the output to not be properly cleared. We therefore refresh the
        # displayed tree upon change of the process trait.
        with self:
            clear_output()
            display(self._tree)

    def _observe_tree_selected_nodes(self, change):
        return self.set_trait(
            "selected_nodes",
            tuple(
                load_node(pk=node.pk) for node in change["new"]
                if hasattr(node, "pk")),
        )

    def _convert_to_tree_nodes(self, old_nodes, new_nodes):
        "Convert nodes into tree nodes while re-using already converted nodes."
        old_nodes_ = {node.pk: node for node in old_nodes}
        assert len(old_nodes_) == len(old_nodes)  # no duplicated nodes

        for node in new_nodes:
            if node.pk in old_nodes_:
                yield old_nodes_[node.pk]
            else:
                yield self._to_tree_node(node, opened=True)

    @traitlets.observe("nodes")
    def _observe_nodes(self, change):
        self._tree.nodes = list(
            sorted(
                self._convert_to_tree_nodes(old_nodes=self._tree.nodes,
                                            new_nodes=change["new"]),
                key=lambda node: node.pk,
            ))
        self.update()
        self._refresh_output()

    @classmethod
    def _to_tree_node(cls, node, name=None, **kwargs):
        """Convert an AiiDA node to a tree node."""
        if name is None:
            if isinstance(node, ProcessNode):
                name = calc_info(node)
            else:
                name = str(node)
        return cls.NODE_TYPE.get(type(node), UnknownTypeTreeNode)(pk=node.pk,
                                                                  name=name,
                                                                  **kwargs)

    @classmethod
    def _find_called(cls, root):
        assert isinstance(root, AiidaProcessNodeTreeNode)
        process_node = load_node(root.pk)
        called = process_node.called
        called.sort(key=lambda p: p.ctime)
        for node in called:
            if node.pk not in root.nodes_registry:
                try:
                    name = calc_info(node)
                except AttributeError:
                    name = str(node)

                root.nodes_registry[node.pk] = cls._to_tree_node(node,
                                                                 name=name)
            yield root.nodes_registry[node.pk]

    @classmethod
    def _find_outputs(cls, root):
        process_node = load_node(root.parent_pk)
        if root.namespace:
            outputs = process_node.outputs[root.namespace]
        else:
            outputs = process_node.outputs

        output_nodes = {key: outputs[key] for key in outputs}

        for key in sorted(output_nodes.keys(),
                          key=lambda k: getattr(outputs[k], "pk", -1)):
            node = output_nodes[key]

            if isinstance(node, AttributeDict):
                yield AiidaOutputsTreeNode(name=key,
                                           parent_pk=root.parent_pk,
                                           namespace=key)
            else:
                if node.pk not in root.nodes_registry:
                    root.nodes_registry[node.pk] = cls._to_tree_node(
                        node, name=f"{key}<{node.pk}>")
                yield root.nodes_registry[node.pk]

    @classmethod
    def _find_children(cls, root):
        """Find all children of the provided AiiDA node."""
        if isinstance(root, AiidaProcessNodeTreeNode):
            yield root.outputs_node
            yield from cls._find_called(root)
        elif isinstance(root, AiidaOutputsTreeNode):
            yield from cls._find_outputs(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)
        ]
        return root

    @classmethod
    def _walk_tree(cls, root):
        """Breadth-first search of the node tree."""
        yield root
        for node in root.nodes:
            yield from cls._walk_tree(node)

    def _update_tree_node(self, tree_node):
        if isinstance(tree_node, AiidaProcessNodeTreeNode):
            process_node = load_node(tree_node.pk)
            tree_node.name = calc_info(process_node)
            tree_node.icon_style = self.PROCESS_STATE_STYLE.get(
                process_node.process_state, self.PROCESS_STATE_STYLE_DEFAULT)

    def update(self, _=None):
        """Refresh nodes based on the latest state of the root process and its children."""
        for root_node in self._tree.nodes:
            self._build_tree(root_node)
            for tree_node in self._walk_tree(root_node):
                self._update_tree_node(tree_node)

    def find_node(self, pk):
        for node in self._walk_tree(self._tree):
            if getattr(node, "pk", None) == pk:
                return node
        raise KeyError(pk)
예제 #6
0
def test_tree():
    Tree()