Skip to content

Commit fec0fc0

Browse files
authored
Merge pull request #15 from StableLlama/more_nodes
More nodes
2 parents 1296aed + 0f54514 commit fec0fc0

File tree

9 files changed

+938
-33
lines changed

9 files changed

+938
-33
lines changed

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,46 @@ ComfyUI has different data types that serve different purposes:
274274
- Supports built-in ComfyUI iteration over each item
275275
- Best for:
276276
- Working directly with multiple items in parallel
277+
- Processing each item in a collection separately
278+
- When you need ComfyUI's automatic iteration functionality
279+
280+
### 2. LIST
281+
- A Python list passed as a single ComfyUI variable
282+
- Must be processed as a complete unit by compatible nodes
283+
- Operations apply to the entire LIST at once
284+
- Best for:
285+
- Storing and manipulating structured data as a single unit
286+
- When you need to preserve ordered collections
287+
- Passing complex data structures between nodes
288+
289+
### 3. SET
290+
- A Python set passed as a single ComfyUI variable
291+
- Unordered collection of unique items
292+
- Useful for membership testing, removing duplicates, and set operations
293+
- Best for:
294+
- When you need to ensure uniqueness of items
295+
- Performing mathematical set operations (union, intersection, difference)
296+
- Efficient membership testing (contains operation)
297+
- When item order doesn't matter
298+
299+
## Control Flow Nodes
300+
301+
Control flow nodes provide mechanisms to direct the flow of execution in your ComfyUI workflows, allowing for conditional processing and dynamic execution paths.
302+
303+
### Available Control Flow Nodes:
304+
305+
#### Conditional Processing
306+
- **if/else** - Routes execution based on a boolean condition
307+
- **if/elif/.../else** - Supports multiple conditional branches
308+
- **switch/case** - Selects from multiple options based on an index
309+
310+
#### Execution Management
311+
- **disable flow** - Conditionally enables or disables a flow
312+
- **flow select** - Directs output to either "true" or "false" path
313+
- **force calculation** - Prevents caching and forces recalculation
314+
- **force execution order** - Controls the sequence of node execution
315+
316+
These control flow nodes enable building more complex, dynamic workflows with decision-making capabilities based on runtime conditions.
277317
- Batch processing scenarios
278318
- When you need to apply the same operation to multiple inputs
279319
- When your operation needs to work with individual items separately

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "basic_data_handling"
7-
version = "0.4.0"
7+
version = "0.4.1"
88
description = """Basic Python functions for manipulating data that every programmer is used to.
99
Comprehensive node collection for data manipulation in ComfyUI workflows.
1010

src/basic_data_handling/control_flow_nodes.py

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def check_lazy_status(self, **kwargs) -> list[str]:
9090
# Check if condition
9191
if kwargs.get("if", False) and kwargs.get("then") is None:
9292
needed.append("then")
93-
return needed # If main condition is true, we only need "then"
93+
return needed # If the main condition is true, we only need "then"
9494

9595
# Check each elif condition
9696
elif_index = 0
@@ -153,7 +153,7 @@ class SwitchCase(ComfyNodeABC):
153153
def INPUT_TYPES(cls):
154154
return {
155155
"required": ContainsDynamicDict({
156-
"selector": (IO.INT, {"default": 0, "min": 0}),
156+
"select": (IO.INT, {"default": 0, "min": 0}),
157157
"case_0": (IO.ANY, {"lazy": True, "_dynamic": "number"}),
158158
}),
159159
"optional": {
@@ -167,29 +167,29 @@ def INPUT_TYPES(cls):
167167
DESCRIPTION = cleandoc(__doc__ or "")
168168
FUNCTION = "execute"
169169

170-
def check_lazy_status(self, selector: int, **kwargs) -> list[str]:
170+
def check_lazy_status(self, select: int, **kwargs) -> list[str]:
171171
needed = []
172172

173-
# Check for needed case inputs based on selector
173+
# Check for necessary case inputs based on select
174174
case_count = 0
175175
for key, value in kwargs.items():
176176
if key.startswith("case_"):
177177
try:
178178
case_index = int(key.split("_")[1])
179179
case_count = max(case_count, case_index + 1)
180-
if value is None and selector == case_index:
180+
if value is None and select == case_index:
181181
needed.append(key)
182182
except ValueError:
183183
pass # Not a numeric case key
184184

185-
# Check if default is needed when selector is out of range
186-
if "default" in kwargs and kwargs["default"] is None and not 0 <= selector < case_count:
185+
# Check if default is needed when select is out of range
186+
if "default" in kwargs and kwargs["default"] is None and not 0 <= select < case_count:
187187
needed.append("default")
188188

189189
return needed
190190

191191
def execute(self, selector: int, **kwargs) -> tuple[Any]:
192-
# Build cases array from all case_X inputs
192+
# Build a case array from all case_X inputs
193193
cases = []
194194
for i in range(len(kwargs)):
195195
case_key = f"case_{i}"
@@ -202,10 +202,44 @@ def execute(self, selector: int, **kwargs) -> tuple[Any]:
202202
if 0 <= selector < len(cases) and cases[selector] is not None:
203203
return (cases[selector],)
204204

205-
# If selector is out of range or the selected case is None, return default
205+
# If select is out of range or the selected case is None, return default
206206
return (kwargs.get("default"),)
207207

208208

209+
class DisableFlow(ComfyNodeABC):
210+
"""
211+
Conditionally enable or disable a flow.
212+
213+
This node takes a value and either passes it through or blocks execution
214+
based on the 'select' parameter. When 'select' is True, the value passes through;
215+
when False, execution is blocked.
216+
"""
217+
@classmethod
218+
def INPUT_TYPES(cls):
219+
return {
220+
"required": {
221+
"value": (IO.ANY, {}),
222+
"select": (IO.BOOLEAN, {"default": True}),
223+
}
224+
}
225+
226+
RETURN_TYPES = (IO.ANY,)
227+
RETURN_NAMES = ("value",)
228+
CATEGORY = "Basic/flow control"
229+
DESCRIPTION = cleandoc(__doc__ or "")
230+
FUNCTION = "execute"
231+
232+
@classmethod
233+
def IS_CHANGED(s, value: Any):
234+
return float("NaN") # not equal to anything -> trigger recalculation
235+
236+
def execute(self, value: Any, select: bool = True) -> tuple[Any]:
237+
if select:
238+
return (value,)
239+
else:
240+
return (ExecutionBlocker(None),)
241+
242+
209243
class FlowSelect(ComfyNodeABC):
210244
"""
211245
Select the direction of the flow.
@@ -237,6 +271,35 @@ def select(self, value, select = True) -> tuple[Any, Any]:
237271
return ExecutionBlocker(None), value
238272

239273

274+
class ForceCalculation(ComfyNodeABC):
275+
"""
276+
Forces recalculation of the connected nodes.
277+
278+
This node passes the input directly to the output but prevents caching
279+
by marking itself as an output node and also indicates the out has changed.
280+
Use this when you need to ensure nodes are always recalculated.
281+
"""
282+
283+
OUTPUT_NODE = True # Marks as an output node to force calculation
284+
285+
@classmethod
286+
def INPUT_TYPES(cls):
287+
return {
288+
"required": {
289+
"value": (IO.ANY, {}),
290+
}
291+
}
292+
293+
RETURN_TYPES = (IO.ANY,)
294+
RETURN_NAMES = ("value",)
295+
CATEGORY = "Basic/flow control"
296+
DESCRIPTION = cleandoc(__doc__ or "")
297+
FUNCTION = "execute"
298+
299+
def execute(self, value: Any) -> tuple[Any, int]:
300+
return (value,)
301+
302+
240303
class ExecutionOrder(ComfyNodeABC):
241304
"""
242305
Force execution order in the workflow.
@@ -268,14 +331,18 @@ def execute(self, **kwargs) -> tuple[Any]:
268331
"Basic data handling: IfElse": IfElse,
269332
"Basic data handling: IfElifElse": IfElifElse,
270333
"Basic data handling: SwitchCase": SwitchCase,
334+
"Basic data handling: DisableFlow": DisableFlow,
271335
"Basic data handling: FlowSelect": FlowSelect,
336+
"Basic data handling: ForceCalculation": ForceCalculation,
272337
"Basic data handling: ExecutionOrder": ExecutionOrder,
273338
}
274339

275340
NODE_DISPLAY_NAME_MAPPINGS = {
276341
"Basic data handling: IfElse": "if/else",
277342
"Basic data handling: IfElifElse": "if/elif/.../else",
278343
"Basic data handling: SwitchCase": "switch/case",
344+
"Basic data handling: DisableFlow": "disable flow",
279345
"Basic data handling: FlowSelect": "flow select",
346+
"Basic data handling: ForceCalculation": "force calculation",
280347
"Basic data handling: ExecutionOrder": "force execution order",
281348
}

0 commit comments

Comments
 (0)