Add Multi Bool Panel custom node

This commit is contained in:
solidlime
2026-08-02 12:09:18 +09:00
commit 58d6e1e34f
6 changed files with 180 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
__pycache__/
*.pyc
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 solidlime
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+48
View File
@@ -0,0 +1,48 @@
# Multi Bool Panel
## 概要
1ノードで複数のbooleanトグルを管理できるComfyUIカスタムノードです。
最大20個の名前付きトグルを持ち、各トグルは独立したBOOLEAN出力ソケットを持ちます。
`slot_count` でトグル数を1〜20の範囲で調整でき、`all_switch` で全トグルをONにできます。
## インストール
**ComfyUI Manager の場合**: カスタムノードマネージャーで「Multi Bool Panel」を検索してインストール(ComfyUI-Managerレジストリ登録後)。
**git clone の場合**:
1. `ComfyUI/custom_nodes/` で `git clone https://github.com/solidlime/ComfyUI-MultiBoolPanel` を実行
2. ComfyUIを再起動します
3. ノード追加メニューで「Multi Bool Panel」を検索して追加します(カテゴリ: `Logic`)
**手動コピーの場合**: このフォルダ(`MultiBoolPanel`)を `ComfyUI/custom_nodes/` にコピー → ComfyUI再起動 →「Multi Bool Panel」を検索して追加
## 使い方(基本)
- トグルスイッチのON/OFFがそのまま `Switch 1`〜`Switch 20` のBOOLEAN出力に出ます
- `slot_count` を超える番号の出力は常に `False` です
- `Labels` 出力には、ONかつラベル名が設定されているトグル名がカンマ区切りで出力されます
- トグル名は `label_N` で変更できます
## 使い方(一括操作)
`name_filter` にノード名(タイトルまたはクラス名)の部分文字列を指定します。
`broadcast_mode` で適用方法を選択します:
- `widgets`: 対象ノードのbooleanウィジェット(チェックボックス)全てをトグル値に書き換えます
- `bypass`: 対象ノードのbypass状態を切り替えます
- `mute`: 対象ノードのmute状態を切り替えます
- `off`: 一括操作しません(通常のトグルパネルとして使用)
トグル番号Nは「フィルタに一致するN番目のノード(タイトル順ソート)」に対応します。
`all_switch` がONの場合は全対象ノードがONになります。
適用タイミング:
- ノード上の「Apply to matching nodes」ボタン(手動)
- `auto_apply` がONの場合は、キュー実行時に自動適用されます
## 制限・注意
- トグルは最大20個固定です
- `widgets` モードは対象ノードのBOOLEAN型ウィジェットのみ書き換えます(入力ソケット経由の接続には影響しません)
- 名前フィルタは部分一致で、タイトルとクラス名の両方を確認します
+5
View File
@@ -0,0 +1,5 @@
from .multi_bool_panel import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
WEB_DIRECTORY = "./web"
__version__ = "1.0.0"
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]
+49
View File
@@ -0,0 +1,49 @@
"""
Multi Bool Panel — 1ノードに複数の名前付きbooleanトグルを持つComfyUIカスタムノード。
各トグルは独立したBOOLEAN出力ソケットを持つ。slot_countでトグル数を調整できる。
名前フィルタによる一括操作(他ノードのbooleanウィジェット / bypass / mute)は
フロントエンド拡張 (web/js/multi_bool_panel.js) が担当する。
"""
MAX_SLOTS = 20
class SolidlimeMultiBoolPanel:
@classmethod
def INPUT_TYPES(cls):
required = {
"slot_count": ("INT", {"default": 4, "min": 1, "max": MAX_SLOTS, "step": 1}),
"name_filter": ("STRING", {"default": "", "multiline": False}),
"broadcast_mode": (["off", "widgets", "bypass", "mute"], {"default": "off"}),
"auto_apply": ("BOOLEAN", {"default": False}),
"all_switch": ("BOOLEAN", {"default": False}),
}
for i in range(1, MAX_SLOTS + 1):
required[f"label_{i}"] = ("STRING", {"default": "", "multiline": False})
required[f"switch_{i}"] = ("BOOLEAN", {"default": False})
return {"required": required}
RETURN_TYPES = ("STRING",) + ("BOOLEAN",) * MAX_SLOTS
RETURN_NAMES = ("Labels",) + tuple(f"Switch {i}" for i in range(1, MAX_SLOTS + 1))
FUNCTION = "execute"
CATEGORY = "Logic"
def execute(self, **kwargs):
slot_count = max(1, min(MAX_SLOTS, int(kwargs.get("slot_count", 4))))
all_switch = bool(kwargs.get("all_switch", False))
labels_out = []
out = []
for i in range(1, MAX_SLOTS + 1):
if i > slot_count:
out.append(False)
continue
label = str(kwargs.get(f"label_{i}", "") or "")
val = bool(kwargs.get(f"switch_{i}", False)) or all_switch
out.append(val)
if val and label:
labels_out.append(label)
return (", ".join(labels_out),) + tuple(out)
NODE_CLASS_MAPPINGS = {"SolidlimeMultiBoolPanel": SolidlimeMultiBoolPanel}
NODE_DISPLAY_NAME_MAPPINGS = {"SolidlimeMultiBoolPanel": "Multi Bool Panel"}
+55
View File
@@ -0,0 +1,55 @@
import { app } from "../../scripts/app.js";
const PANEL_TYPE = "SolidlimeMultiBoolPanel";
function widget(node, name) {
return (node.widgets || []).find((w) => w.name === name);
}
function applyBroadcast(node) {
const filter = (widget(node, "name_filter")?.value || "").trim();
const mode = widget(node, "broadcast_mode")?.value;
if (!filter || !mode || mode === "off") return;
const slotCount = Math.max(1, parseInt(widget(node, "slot_count")?.value ?? "4", 10) || 1);
const targets = (app.graph._nodes || [])
.filter((n) => n.id !== node.id && (
(n.title && n.title.includes(filter)) ||
(n.type && n.type.includes(filter))
))
.sort((a, b) => (a.title || a.type).localeCompare(b.title || b.type, "ja"));
targets.forEach((t, idx) => {
const on = idx < slotCount ? !!widget(node, `switch_${idx + 1}`)?.value : false;
if (mode === "bypass") {
t.bypass = on;
} else if (mode === "mute") {
t.mute = on;
} else if (mode === "widgets") {
(t.widgets || []).forEach((wid) => {
if (wid.type === "boolean" || (wid.options && wid.options.type === "boolean")) {
wid.value = on;
}
});
}
});
app.graph.setDirtyCanvas(true, true);
}
app.registerExtension({
name: "MultiBoolPanel",
async setup() {
const origQueue = app.queuePrompt.bind(app);
app.queuePrompt = function (number, detail) {
(app.graph._nodes || []).forEach((n) => {
if (n.type === PANEL_TYPE && widget(n, "auto_apply")?.value) applyBroadcast(n);
});
return origQueue(number, detail);
};
},
async nodeCreated(node) {
if (node.type === PANEL_TYPE || node.constructor.type === PANEL_TYPE) {
node.addWidget("button", "Apply to matching nodes", null, () => applyBroadcast(node));
}
},
});