diff --git a/src/basic_data_handling/control_flow_nodes.py b/src/basic_data_handling/control_flow_nodes.py index dc1f2bb..02bf445 100644 --- a/src/basic_data_handling/control_flow_nodes.py +++ b/src/basic_data_handling/control_flow_nodes.py @@ -1,6 +1,8 @@ from typing import Any from inspect import cleandoc from comfy.comfy_types.node_typing import IO, ComfyNodeABC +from comfy_execution.graph import ExecutionBlocker + class IfElse(ComfyNodeABC): """ @@ -218,10 +220,42 @@ class ExecutionOrder(ComfyNodeABC): return (None,) +class FlowSelect(ComfyNodeABC): + """ + Select the direction of the flow. + + This node takes a value and directs it to either the "true" or "false" output. + + Note: for dynamic switching in a Data Flow you might want to use + "filter select" instead. + """ + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "value": (IO.ANY, {}), + "select": (IO.BOOLEAN, {}), + } + } + + RETURN_TYPES = (IO.ANY, IO.ANY) + RETURN_NAMES = ("true", "false") + CATEGORY = "Basic/flow control" + DESCRIPTION = cleandoc(__doc__ or "") + FUNCTION = "select" + + def select(self, value, select = True) -> tuple[Any, Any]: + if select: + return value, ExecutionBlocker(None) + else: + return ExecutionBlocker(None), value + + NODE_CLASS_MAPPINGS = { "Basic data handling: IfElse": IfElse, "Basic data handling: IfElifElse": IfElifElse, "Basic data handling: SwitchCase": SwitchCase, + "Basic data handling: FlowSelect": FlowSelect, "Basic data handling: ExecutionOrder": ExecutionOrder, } @@ -229,5 +263,6 @@ NODE_DISPLAY_NAME_MAPPINGS = { "Basic data handling: IfElse": "if/else", "Basic data handling: IfElifElse": "if/elif/.../else", "Basic data handling: SwitchCase": "switch/case", + "Basic data handling: FlowSelect": "flow select", "Basic data handling: ExecutionOrder": "force execution order", } diff --git a/src/basic_data_handling/data_list_nodes.py b/src/basic_data_handling/data_list_nodes.py index 346ba61..565602d 100644 --- a/src/basic_data_handling/data_list_nodes.py +++ b/src/basic_data_handling/data_list_nodes.py @@ -295,6 +295,46 @@ class DataListFilter(ComfyNodeABC): return (result,) +class DataListFilterSelect(ComfyNodeABC): + """ + Filters a Data List using boolean values. + + This node takes a value Data List and a filter Data List (containing only boolean values). + It returns two new Data Lists containing only the elements from the value list where the + corresponding element in the filter list is true or false. + + If the lists have different lengths, the last element of the shorter list is repeated + till the lengths are matching. + """ + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "value": (IO.ANY, {}), + "select": (IO.BOOLEAN, {}), + } + } + + RETURN_TYPES = (IO.ANY, IO.ANY) + RETURN_NAMES = ("true", "false") + CATEGORY = "Basic/Data List" + DESCRIPTION = cleandoc(__doc__ or "") + FUNCTION = "select" + INPUT_IS_LIST = True + OUTPUT_IS_LIST = (True, True,) + + def select(self, **kwargs: list[Any]) -> tuple[list[Any]]: + values = kwargs.get('value', []) + selects = kwargs.get('select', []) + + # Create a new list with only items where the filter is False + result_true, result_false = [], [] + for value, select in zip(values, selects): + (result_true if select else result_false).append(value) + + return result_true, result_false + + class DataListGetItem(ComfyNodeABC): """ Retrieves an item at a specified position in a list. @@ -771,7 +811,8 @@ class DataListToList(ComfyNodeABC): INPUT_IS_LIST = True def convert(self, **kwargs: list[Any]) -> tuple[list[Any]]: - return (kwargs.get('list', []).copy(),) + print(f"input list: '{kwargs.get('list', [])}'") + return (list(kwargs.get('list', [])).copy(),) class DataListToSet(ComfyNodeABC): @@ -810,6 +851,7 @@ NODE_CLASS_MAPPINGS = { "Basic data handling: DataListCount": DataListCount, "Basic data handling: DataListExtend": DataListExtend, "Basic data handling: DataListFilter": DataListFilter, + "Basic data handling: DataListFilterSelect": DataListFilterSelect, "Basic data handling: DataListGetItem": DataListGetItem, "Basic data handling: DataListIndex": DataListIndex, "Basic data handling: DataListInsert": DataListInsert, @@ -838,6 +880,7 @@ NODE_DISPLAY_NAME_MAPPINGS = { "Basic data handling: DataListCount": "count", "Basic data handling: DataListExtend": "extend", "Basic data handling: DataListFilter": "filter", + "Basic data handling: DataListFilterSelect": "filter select", "Basic data handling: DataListGetItem": "get item", "Basic data handling: DataListIndex": "index", "Basic data handling: DataListInsert": "insert",