diff --git a/README.md b/README.md index 23f6c70..37af5ab 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,6 @@ These nodes allow setting up interactive workflows. Several "Interactive Selector" nodes may be plugged to an "Interactive Switch". Selector nodes contain a button that the user can press to select that path of execution and continue execution. -The "Interactive Trigger" only allows execution to progress when a button is pressed. - The simple versions of the nodes show and pass an image. The "with parameters" versions of the nodes show and pass the image (optional), but also passes a latent, mask, string, int, and float. The "Interactive Save" node previews images and contains a button that triggers saving the image. @@ -46,14 +44,6 @@ Download the example workflow from [here](interactive_example_workflow_with_para ![Workflow](interactive_example_workflow_with_parameters.png) -## Example (Trigger) - -This example uses an "Interactive Trigger" node on a low-step preview to gate moving on with more sampling steps, and an "Interactive Save" node to gate saving an image on a button click. - -Download the example workflow from [here](interactive_example_workflow_trigger.json) or drag and drop the screenshot into ComfyUI. - -![Workflow](interactive_example_workflow_trigger.png) - ## Example (Full workflow with several options per step) This example uses SDXL Turbo to quickly sample images, and uses several rounds of "Interactive Selector (with parameters)" and "Interactive Switch (with parameters)" nodes to allow the user to pick between three options, three times in a row. diff --git a/__init__.py b/__init__.py index 2cbfa96..de48713 100755 --- a/__init__.py +++ b/__init__.py @@ -1,5 +1,3 @@ -from .comfyui_interactive import InteractiveTrigger -from .comfyui_interactive import InteractiveTriggerWithParameters from .comfyui_interactive import InteractiveSelector from .comfyui_interactive import InteractiveSelectorWithParameters from .comfyui_interactive import InteractiveSwitch @@ -16,8 +14,6 @@ from .comfyui_interactive import InteractiveFloat WEB_DIRECTORY = "js" NODE_CLASS_MAPPINGS = { - "InteractiveTrigger": InteractiveTrigger, - "InteractiveTriggerWithParameters": InteractiveTriggerWithParameters, "InteractiveSelector": InteractiveSelector, "InteractiveSelectorWithParameters": InteractiveSelectorWithParameters, "InteractiveSwitch": InteractiveSwitch, @@ -33,8 +29,6 @@ NODE_CLASS_MAPPINGS = { } NODE_DISPLAY_NAME_MAPPINGS = { - "InteractiveTrigger": "Interactive Trigger", - "InteractiveTriggerWithParameters": "Interactive Trigger (with parameters)", "InteractiveSelector": "Interactive Selector", "InteractiveSelectorWithParameters": "Interactive Selector (with parameters)", "InteractiveSwitch": "Interactive Switch", diff --git a/comfyui_interactive.py b/comfyui_interactive.py index 7bc3b2f..1dcae93 100755 --- a/comfyui_interactive.py +++ b/comfyui_interactive.py @@ -96,85 +96,6 @@ class InteractiveSeed: return (seed_value,) -class InteractiveTriggerWithParameters: - def __init__(self): - self.output_dir = folder_paths.get_temp_directory() - self.last_trigger_action = 0 - self.compress_level = 4 - self.prefix_append = "_trigger_" + ''.join(random.choice("abcdefghijklmnopqrstupvxyz") for _ in range(5)) - - @classmethod - def INPUT_TYPES(s): - return { - "required": { - "trigger_action": ("INT", {"default": 0}), - }, - "optional": { - "images": ("IMAGE", ), - "parameter_latent": ("LATENT", ), - "parameter_mask": ("MASK", ), - "parameter_string": ("STRING", {"forceInput": True}), - "parameter_int": ("INT", {"forceInput": True}), - "parameter_float": ("FLOAT", {"forceInput": True}), - }, - "hidden": { - "prompt": "PROMPT", - "extra_pnginfo": "EXTRA_PNGINFO" - }, - } - - CATEGORY = "interactive" - RETURN_TYPES = ("IMAGE", "LATENT", "MASK", "STRING", "INT", "FLOAT") - RETURN_NAMES = ("images", "parameter_latent", "parameter_mask", "parameter_string", "parameter_int", "parameter_float") - FUNCTION = "run" - - def run(self, trigger_action, images=None, parameter_latent=None, parameter_mask=None, parameter_string=None, parameter_int=None, parameter_float=None, prompt=None, extra_pnginfo=None): - out = {} - - results = save_images_with_metadata(images=images, output_dir=self.output_dir, save_type="temp", prompt=prompt, extra_pnginfo=extra_pnginfo, prefix=self.prefix_append, compress_level=self.compress_level) - out["ui"] = {"images": results} - - if trigger_action != self.last_trigger_action: - self.last_trigger_action = trigger_action - else: - nodes.interrupt_processing() - return (images, parameter_latent, parameter_mask, parameter_string, parameter_int, parameter_float, out) - - -class InteractiveTrigger(InteractiveTriggerWithParameters): - @classmethod - def INPUT_TYPES(s): - return { - "required": { - "trigger_action": ("BOOLEAN", {"default": False}), - }, - "optional": { - "images": ("IMAGE", ), - }, - "hidden": { - "prompt": "PROMPT", - "extra_pnginfo": "EXTRA_PNGINFO" - }, - } - - RETURN_TYPES = ("IMAGE",) - RETURN_NAMES = ("images",) - - def run(self, trigger_action, images=None, parameter_latent=None, parameter_mask=None, parameter_string=None, parameter_int=None, parameter_float=None, prompt=None, extra_pnginfo=None): - out = {} - - output_dir = folder_paths.get_output_directory() - results = save_images_with_metadata(images=images, output_dir=output_dir, save_type="temp", prompt=prompt, extra_pnginfo=extra_pnginfo, prefix=self.prefix_append, compress_level=self.compress_level) - out["ui"] = {"images": results} - - if trigger_action != self.last_trigger_action: - self.last_trigger_action = trigger_action - else: - nodes.interrupt_processing() - return (images, out) - - - class InteractiveSelectorWithParameters: def __init__(self): self.output_dir = folder_paths.get_temp_directory() diff --git a/interactive_example_workflow_trigger.json b/interactive_example_workflow_trigger.json deleted file mode 100644 index bcaa67a..0000000 --- a/interactive_example_workflow_trigger.json +++ /dev/null @@ -1,866 +0,0 @@ -{ - "last_node_id": 19, - "last_link_id": 35, - "nodes": [ - { - "id": 7, - "type": "InteractiveInteger", - "pos": { - "0": 160, - "1": 1030 - }, - "size": { - "0": 315, - "1": 58 - }, - "flags": {}, - "order": 0, - "mode": 0, - "inputs": [], - "outputs": [ - { - "name": "integer", - "type": "INT", - "links": [ - 3, - 4 - ], - "slot_index": 0 - } - ], - "title": "Integer (for Interactive) - max_steps", - "properties": { - "Node name for S&R": "InteractiveInteger" - }, - "widgets_values": [ - 20 - ] - }, - { - "id": 4, - "type": "CheckpointLoaderSimple", - "pos": { - "0": 40, - "1": 100 - }, - "size": { - "0": 315, - "1": 98 - }, - "flags": {}, - "order": 1, - "mode": 0, - "inputs": [], - "outputs": [ - { - "name": "MODEL", - "type": "MODEL", - "links": [ - 1, - 20 - ], - "slot_index": 0 - }, - { - "name": "CLIP", - "type": "CLIP", - "links": [ - 2, - 9 - ], - "slot_index": 1 - }, - { - "name": "VAE", - "type": "VAE", - "links": [ - 12, - 24 - ], - "slot_index": 2 - } - ], - "properties": { - "Node name for S&R": "CheckpointLoaderSimple" - }, - "widgets_values": [ - "sd1.5/dreamshaper_8.safetensors" - ] - }, - { - "id": 8, - "type": "InteractiveInteger", - "pos": { - "0": 160, - "1": 1150 - }, - "size": { - "0": 344.3999938964844, - "1": 58 - }, - "flags": {}, - "order": 2, - "mode": 0, - "inputs": [], - "outputs": [ - { - "name": "integer", - "type": "INT", - "links": [ - 6, - 29 - ], - "slot_index": 0 - } - ], - "title": "Integer (for Interactive) - preview_steps", - "properties": { - "Node name for S&R": "InteractiveInteger" - }, - "widgets_values": [ - 10 - ] - }, - { - "id": 9, - "type": "CLIPTextEncode", - "pos": { - "0": 150, - "1": 680 - }, - "size": { - "0": 400, - "1": 200 - }, - "flags": {}, - "order": 6, - "mode": 0, - "inputs": [ - { - "name": "clip", - "type": "CLIP", - "link": 9 - } - ], - "outputs": [ - { - "name": "CONDITIONING", - "type": "CONDITIONING", - "links": [ - 10, - 22 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "CLIPTextEncode" - }, - "widgets_values": [ - "ugly" - ] - }, - { - "id": 17, - "type": "EmptyLatentImage", - "pos": { - "0": 160, - "1": 1310 - }, - "size": { - "0": 315, - "1": 106 - }, - "flags": {}, - "order": 3, - "mode": 0, - "inputs": [], - "outputs": [ - { - "name": "LATENT", - "type": "LATENT", - "links": [ - 30 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "EmptyLatentImage" - }, - "widgets_values": [ - 512, - 512, - 1 - ] - }, - { - "id": 18, - "type": "InteractiveSeed", - "pos": { - "0": 1070, - "1": 430 - }, - "size": { - "0": 210, - "1": 82 - }, - "flags": {}, - "order": 4, - "mode": 0, - "inputs": [], - "outputs": [ - { - "name": "seed_value", - "type": "INT", - "links": [ - 31, - 32 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "InteractiveSeed" - }, - "widgets_values": [ - 203209234455398, - "" - ], - "color": "#444444", - "bgcolor": "#444444" - }, - { - "id": 5, - "type": "CLIPTextEncode", - "pos": { - "0": 160, - "1": 400 - }, - "size": { - "0": 400, - "1": 200 - }, - "flags": {}, - "order": 5, - "mode": 0, - "inputs": [ - { - "name": "clip", - "type": "CLIP", - "link": 2 - } - ], - "outputs": [ - { - "name": "CONDITIONING", - "type": "CONDITIONING", - "links": [ - 11, - 21 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "CLIPTextEncode" - }, - "widgets_values": [ - "dog eating chocolate ice cream" - ] - }, - { - "id": 1, - "type": "KSamplerAdvanced", - "pos": { - "0": 1430, - "1": 580 - }, - "size": { - "0": 320, - "1": 510 - }, - "flags": {}, - "order": 10, - "mode": 0, - "inputs": [ - { - "name": "model", - "type": "MODEL", - "link": 20 - }, - { - "name": "positive", - "type": "CONDITIONING", - "link": 21 - }, - { - "name": "negative", - "type": "CONDITIONING", - "link": 22 - }, - { - "name": "latent_image", - "type": "LATENT", - "link": 35 - }, - { - "name": "start_at_step", - "type": "INT", - "link": 29, - "widget": { - "name": "start_at_step" - } - }, - { - "name": "steps", - "type": "INT", - "link": 4, - "widget": { - "name": "steps" - } - }, - { - "name": "noise_seed", - "type": "INT", - "link": 32, - "widget": { - "name": "noise_seed" - } - } - ], - "outputs": [ - { - "name": "LATENT", - "type": "LATENT", - "links": [ - 23 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "KSamplerAdvanced" - }, - "widgets_values": [ - "enable", - 685150185064353, - "randomize", - 20, - 8, - "euler", - "normal", - 0, - 10000, - "disable" - ] - }, - { - "id": 13, - "type": "VAEDecode", - "pos": { - "0": 1480, - "1": 1140 - }, - "size": { - "0": 210, - "1": 46 - }, - "flags": {}, - "order": 11, - "mode": 0, - "inputs": [ - { - "name": "samples", - "type": "LATENT", - "link": 23 - }, - { - "name": "vae", - "type": "VAE", - "link": 24 - } - ], - "outputs": [ - { - "name": "IMAGE", - "type": "IMAGE", - "links": [ - 25 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "VAEDecode" - }, - "widgets_values": [] - }, - { - "id": 14, - "type": "InteractiveSave", - "pos": { - "0": 1840, - "1": 590 - }, - "size": { - "0": 210, - "1": 290 - }, - "flags": {}, - "order": 12, - "mode": 0, - "inputs": [ - { - "name": "images", - "type": "IMAGE", - "link": 25 - } - ], - "outputs": [], - "properties": { - "Node name for S&R": "InteractiveSave" - }, - "widgets_values": [ - "ComfyUI", - 0, - "" - ], - "color": "#444444", - "bgcolor": "#444444" - }, - { - "id": 3, - "type": "KSamplerAdvanced", - "pos": { - "0": 640, - "1": 570 - }, - "size": { - "0": 320, - "1": 540 - }, - "flags": {}, - "order": 7, - "mode": 0, - "inputs": [ - { - "name": "model", - "type": "MODEL", - "link": 1 - }, - { - "name": "positive", - "type": "CONDITIONING", - "link": 11 - }, - { - "name": "negative", - "type": "CONDITIONING", - "link": 10 - }, - { - "name": "latent_image", - "type": "LATENT", - "link": 30 - }, - { - "name": "end_at_step", - "type": "INT", - "link": 6, - "widget": { - "name": "end_at_step" - } - }, - { - "name": "steps", - "type": "INT", - "link": 3, - "widget": { - "name": "steps" - } - }, - { - "name": "noise_seed", - "type": "INT", - "link": 31, - "widget": { - "name": "noise_seed" - } - } - ], - "outputs": [ - { - "name": "LATENT", - "type": "LATENT", - "links": [ - 16, - 33 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "KSamplerAdvanced" - }, - "widgets_values": [ - "enable", - 490309960760018, - "randomize", - 20, - 8, - "euler", - "normal", - 0, - 10000, - "disable" - ] - }, - { - "id": 10, - "type": "VAEDecode", - "pos": { - "0": 680, - "1": 1200 - }, - "size": { - "0": 210, - "1": 46 - }, - "flags": {}, - "order": 8, - "mode": 0, - "inputs": [ - { - "name": "samples", - "type": "LATENT", - "link": 16 - }, - { - "name": "vae", - "type": "VAE", - "link": 12 - } - ], - "outputs": [ - { - "name": "IMAGE", - "type": "IMAGE", - "links": [ - 34 - ], - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "VAEDecode" - }, - "widgets_values": [] - }, - { - "id": 19, - "type": "InteractiveTriggerWithParameters", - "pos": { - "0": 1040, - "1": 630 - }, - "size": { - "0": 310.79998779296875, - "1": 230 - }, - "flags": {}, - "order": 9, - "mode": 0, - "inputs": [ - { - "name": "images", - "type": "IMAGE", - "link": 34, - "shape": 7 - }, - { - "name": "parameter_latent", - "type": "LATENT", - "link": 33, - "shape": 7 - }, - { - "name": "parameter_mask", - "type": "MASK", - "link": null, - "shape": 7 - }, - { - "name": "parameter_string", - "type": "STRING", - "link": null, - "widget": { - "name": "parameter_string" - }, - "shape": 7 - }, - { - "name": "parameter_int", - "type": "INT", - "link": null, - "widget": { - "name": "parameter_int" - }, - "shape": 7 - }, - { - "name": "parameter_float", - "type": "FLOAT", - "link": null, - "widget": { - "name": "parameter_float" - }, - "shape": 7 - } - ], - "outputs": [ - { - "name": "images", - "type": "IMAGE", - "links": null - }, - { - "name": "parameter_latent", - "type": "LATENT", - "links": [ - 35 - ], - "slot_index": 1 - }, - { - "name": "parameter_mask", - "type": "MASK", - "links": null - }, - { - "name": "parameter_string", - "type": "STRING", - "links": null - }, - { - "name": "parameter_int", - "type": "INT", - "links": null - }, - { - "name": "parameter_float", - "type": "FLOAT", - "links": null - } - ], - "properties": { - "Node name for S&R": "InteractiveTriggerWithParameters" - }, - "widgets_values": [ - 0, - "", - 0, - 0, - "" - ] - } - ], - "links": [ - [ - 1, - 4, - 0, - 3, - 0, - "MODEL" - ], - [ - 2, - 4, - 1, - 5, - 0, - "CLIP" - ], - [ - 3, - 7, - 0, - 3, - 5, - "INT" - ], - [ - 4, - 7, - 0, - 1, - 5, - "INT" - ], - [ - 6, - 8, - 0, - 3, - 4, - "INT" - ], - [ - 9, - 4, - 1, - 9, - 0, - "CLIP" - ], - [ - 10, - 9, - 0, - 3, - 2, - "CONDITIONING" - ], - [ - 11, - 5, - 0, - 3, - 1, - "CONDITIONING" - ], - [ - 12, - 4, - 2, - 10, - 1, - "VAE" - ], - [ - 16, - 3, - 0, - 10, - 0, - "LATENT" - ], - [ - 20, - 4, - 0, - 1, - 0, - "MODEL" - ], - [ - 21, - 5, - 0, - 1, - 1, - "CONDITIONING" - ], - [ - 22, - 9, - 0, - 1, - 2, - "CONDITIONING" - ], - [ - 23, - 1, - 0, - 13, - 0, - "LATENT" - ], - [ - 24, - 4, - 2, - 13, - 1, - "VAE" - ], - [ - 25, - 13, - 0, - 14, - 0, - "IMAGE" - ], - [ - 29, - 8, - 0, - 1, - 4, - "INT" - ], - [ - 30, - 17, - 0, - 3, - 3, - "LATENT" - ], - [ - 31, - 18, - 0, - 3, - 6, - "INT" - ], - [ - 32, - 18, - 0, - 1, - 6, - "INT" - ], - [ - 33, - 3, - 0, - 19, - 1, - "LATENT" - ], - [ - 34, - 10, - 0, - 19, - 0, - "IMAGE" - ], - [ - 35, - 19, - 1, - 1, - 3, - "LATENT" - ] - ], - "groups": [], - "config": {}, - "extra": { - "ds": { - "scale": 0.7513148009015786, - "offset": [ - 183.62061931511303, - 344.94653369555596 - ] - } - }, - "version": 0.4 -} \ No newline at end of file diff --git a/interactive_example_workflow_trigger.png b/interactive_example_workflow_trigger.png deleted file mode 100644 index fc3f08e..0000000 Binary files a/interactive_example_workflow_trigger.png and /dev/null differ diff --git a/js/buttons.js b/js/buttons.js index 755f2dc..bbf31d9 100644 --- a/js/buttons.js +++ b/js/buttons.js @@ -236,9 +236,6 @@ function resetInteractiveForward(node) { findWidgetByName(currentNode, "selected").value = false; currentNode.imgs = []; } - if (currentNode.type === "InteractiveSave" || currentNode.type == "InteractiveTrigger" || currentNode.type == "InteractiveTriggerWithParameters") { - currentNode.imgs = []; - } const connections = getNodeConnections(currentNode); connections.outputs.forEach(outputNode => { @@ -252,14 +249,6 @@ function resetInteractiveForward(node) { app.registerExtension({ name: "interactive.showbutton", nodeCreated(node) { - if (node.comfyClass === "InteractiveTrigger" || node.comfyClass === "InteractiveTriggerWithParameters") { - toggleWidget(node, findWidgetByName(node, "trigger_action"), false); - node.addWidget("button", "👤 Trigger Action", "", () => { - const triggerWidget = findWidgetByName(node, "trigger_action"); - triggerWidget.value = (triggerWidget.value + 1) % 1000; - app.queuePrompt(0, 1); - }); - } if (node.comfyClass == "InteractiveSelector" || node.comfyClass == "InteractiveSelectorWithParameters") { toggleWidget(node, findWidgetByName(node, "selected"), false); node.addWidget("button", textSelect, "", () => { diff --git a/pyproject.toml b/pyproject.toml index 9bb396d..7c27409 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "comfyui-interactive" -description = "Nodes that allow making the UI interactive, with selectors, switches, triggers, etc.. Enables previewing generations at low sampling steps, then unlocking full sampling with the click of a button. Enables selecting across multiple options with the click of a button to move a workflow forward." +description = "Nodes that allow making the UI interactive, with selectors and switches, etc.. Enables previewing generations at low sampling steps, then unlocking full sampling with the click of a button. Enables selecting across multiple options with the click of a button to move a workflow forward." version = "1.0.0" license = { file = "LICENSE" }