Files
WJLUOXIAO-XB_ToolBox/nodes_fast_toggle.py
T
2026-10-21 02:00:36 +08:00

158 lines
5.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
XB-BOX 快速开关(参考 rgthree-comfy 的 Fast Muter / Fast Groups Muter / Any Switch)
=================================================================================
三个节点:
① XB_DisableGroups —— 禁用分组
· **不接线**:在节点面板/右键菜单里勾选要控制的分组(可以随时增删);
· 点一行 = 把该分组里的所有节点一次性「静音 / 恢复」;
· 与 rgthree「禁用框」一致:状态直接读写画布上节点自己的 mode(随工作流保存)。
② XB_DisableNodes —— 禁用节点
· **不接线**:右击勾选要控制的节点(支持按分组整组加入);
· 点一行 = 把该节点「静音 / 绕过 / 恢复」;
· 与 rgthree「禁用节点」一致,但不占用输入端口、不需要拖线绑定。
③ XB_AnySwitch —— 切换任意
· 动态任意类型输入(默认 any_01 / any_02,连满自动追加);
· 与 rgthree「任意切换」完全一致:没有选择控件,永远输出第一个非空输入。
⚠️ 前两个节点**只在前端工作**(控制别的节点的 mode),Python 端不参与执行:
它们没有任何输入输出,ComfyUI 不会把它们放进执行队列。
"""
class AnyType(str):
"""一个永远“等于”任何类型的占位类型(credit: pythongosssss / rgthree)"""
def __ne__(self, other):
return False
def __eq__(self, other):
return True
def __hash__(self):
return hash("*")
class FlexibleOptionalInputType(dict):
"""让 optional 输入可以无限扩展(任何名字都视为存在,类型由构造时指定)。"""
def __init__(self, type_, data=None):
self.type_ = type_
self.data = data
if self.data is not None:
for k, v in self.data.items():
self[k] = v
def __getitem__(self, key):
if self.data is not None and key in self.data:
return self.data[key]
return (self.type_,)
def __contains__(self, key):
return True
any_type = AnyType("*")
class XB_DisableGroups:
"""禁用分组:不接线,勾选分组后一键静音 / 恢复整组节点。"""
DESCRIPTION = (
"禁用分组(参考 rgthree 禁用框):不接线,在面板或右键菜单里勾选要控制的分组;"
"点一行即可把该分组内所有节点静音 / 绕过 / 恢复。状态直接写在画布的节点上,随工作流保存。"
)
@classmethod
def INPUT_TYPES(cls):
return {"required": {}}
RETURN_TYPES = ()
FUNCTION = "noop"
CATEGORY = "XB_ToolBox/Utils"
OUTPUT_NODE = False
def noop(self):
"""纯前端控制节点,执行时什么都不做。"""
return {}
class XB_DisableNodes:
"""禁用节点:不接线,勾选节点(可按分组整组加入)后一键静音 / 绕过 / 恢复。"""
DESCRIPTION = (
"禁用节点(参考 rgthree 禁用节点):不接线,在面板或右键菜单里勾选要控制的节点(支持按分组整组加入);"
"点一行即可静音 / 绕过 / 恢复该节点。状态直接写在画布的节点上,随工作流保存。"
)
@classmethod
def INPUT_TYPES(cls):
return {"required": {}}
RETURN_TYPES = ()
FUNCTION = "noop"
CATEGORY = "XB_ToolBox/Utils"
OUTPUT_NODE = False
def noop(self):
return {}
class XB_AnySwitch:
"""切换任意:动态任意类型输入,直接输出第一个非空输入(无选择控件)。"""
DESCRIPTION = (
"切换任意(参考 rgthree 任意切换):把不同来源接到任意类型的输入端口,端口会随连线自动增加;"
"节点没有选择控件,永远输出从上往下第一个非空的输入。"
)
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": FlexibleOptionalInputType(any_type),
}
RETURN_TYPES = (any_type,)
RETURN_NAMES = ("*",)
FUNCTION = "switch"
CATEGORY = "XB_ToolBox/Utils"
OUTPUT_NODE = False
@staticmethod
def _items(kwargs):
items = [(k, v) for k, v in kwargs.items() if isinstance(k, str) and k.startswith("any_")]
items.sort(key=lambda kv: kv[0])
return items
@staticmethod
def _is_empty(value):
if value is None:
return True
# 上下文为空时的兜底(与 rgthree 一致)
if isinstance(value, dict) and "model" in value and "clip" in value:
return all(v is None for v in value.values())
return False
def switch(self, **kwargs):
"""取第一个非空输入(与 rgthree Any Switch 同行为)。"""
for _k, value in self._items(kwargs):
if not self._is_empty(value):
return (value,)
return (None,)
NODE_CLASS_MAPPINGS = {
"XB_DisableGroups": XB_DisableGroups,
"XB_DisableNodes": XB_DisableNodes,
"XB_AnySwitch": XB_AnySwitch,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"XB_DisableGroups": "XB-BOX - 🚫 禁用分组",
"XB_DisableNodes": "XB-BOX - 🚫 禁用节点",
"XB_AnySwitch": "XB-BOX - 🔀 切换任意",
}